PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 5.8.4
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v5.8.4
7.2.2 7.2.1 7.2 7.1.2 7.1.1 7.1 7.0.4 7.0.6 7.0.7 6.3.8 6.3.7 6.3.6 6.3.5 6.3.4 6.3.3 6.3.1 trunk 5.7.3 5.7.5 5.8.1 5.8.2 5.8.3 5.8.4 5.8.6 6.0.4 All 37 releases
mlsimport / vendor / phpcsstandards / phpcsutils / PHPCSUtils / Utils / ControlStructures.php

ControlStructures.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 5.8.4, at vendor/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ControlStructures.php

279 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
4 *
5 * @package PHPCSUtils
6 * @copyright 2019-2020 PHPCSUtils Contributors
7 * @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8 * @link https://github.com/PHPCSStandards/PHPCSUtils
9 */
10
11 namespace PHPCSUtils\Utils;
12
13 use PHP_CodeSniffer\Exceptions\RuntimeException;
14 use PHP_CodeSniffer\Files\File;
15 use PHP_CodeSniffer\Util\Tokens;
16 use PHPCSUtils\Tokens\Collections;
17
18 /**
19 * Utility functions for use when examining control structures.
20 *
21 * @since 1.0.0
22 */
23 final class ControlStructures
24 {
25
26 /**
27 * Check whether a control structure has a body.
28 *
29 * Some control structures - `while`, `for` and `declare` - can be declared without a body, like:
30 * ```php
31 * while (++$i < 10);
32 * ```
33 *
34 * All other control structures will always have a body, though the body may be empty, where "empty" means:
35 * no _code_ is found in the body. If a control structure body only contains a comment, it will be
36 * regarded as empty.
37 *
38 * @since 1.0.0
39 *
40 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
41 * @param int $stackPtr The position of the token we are checking.
42 * @param bool $allowEmpty Whether a control structure with an empty body should
43 * still be considered as having a body.
44 * Defaults to `true`.
45 *
46 * @return bool `TRUE` when the control structure has a body, or in case `$allowEmpty` is set to `FALSE`:
47 * when it has a non-empty body.
48 * `FALSE` in all other cases, including when a non-control structure token has been passed.
49 */
50 public static function hasBody(File $phpcsFile, $stackPtr, $allowEmpty = true)
51 {
52 $tokens = $phpcsFile->getTokens();
53
54 // Check for the existence of the token.
55 if (isset($tokens[$stackPtr]) === false
56 || isset(Collections::controlStructureTokens()[$tokens[$stackPtr]['code']]) === false
57 ) {
58 return false;
59 }
60
61 // Handle `else if`.
62 if ($tokens[$stackPtr]['code'] === \T_ELSE && isset($tokens[$stackPtr]['scope_opener']) === false) {
63 $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
64 if ($next !== false && $tokens[$next]['code'] === \T_IF) {
65 $stackPtr = $next;
66 }
67 }
68
69 /*
70 * The scope markers are set. This is the simplest situation.
71 */
72 if (isset($tokens[$stackPtr]['scope_opener']) === true) {
73 if ($allowEmpty === true) {
74 return true;
75 }
76
77 // Check whether the body is empty.
78 $start = ($tokens[$stackPtr]['scope_opener'] + 1);
79 $end = ($phpcsFile->numTokens + 1);
80 if (isset($tokens[$stackPtr]['scope_closer']) === true) {
81 $end = $tokens[$stackPtr]['scope_closer'];
82 }
83
84 $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $start, $end, true);
85 if ($nextNonEmpty !== false) {
86 return true;
87 }
88
89 return false;
90 }
91
92 /*
93 * Control structure without scope markers.
94 * Either single line statement or inline control structure.
95 *
96 * - Single line statement doesn't have a body and is therefore always empty.
97 * - Inline control structure has to have a body and can never be empty.
98 *
99 * This code also needs to take live coding into account where a scope opener is found, but
100 * no scope closer.
101 */
102 $searchStart = ($stackPtr + 1);
103 if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
104 $searchStart = ($tokens[$stackPtr]['parenthesis_closer'] + 1);
105 }
106
107 $nextNonEmpty = $phpcsFile->findNext(
108 Tokens::$emptyTokens,
109 $searchStart,
110 null,
111 true
112 );
113 if ($nextNonEmpty === false
114 || $tokens[$nextNonEmpty]['code'] === \T_SEMICOLON
115 || $tokens[$nextNonEmpty]['code'] === \T_CLOSE_TAG
116 ) {
117 // Parse error or single line statement.
118 return false;
119 }
120
121 if ($tokens[$nextNonEmpty]['code'] === \T_OPEN_CURLY_BRACKET) {
122 if ($allowEmpty === true) {
123 return true;
124 }
125
126 // Unrecognized scope opener due to parse error.
127 $nextNext = $phpcsFile->findNext(
128 Tokens::$emptyTokens,
129 ($nextNonEmpty + 1),
130 null,
131 true
132 );
133
134 if ($nextNext === false) {
135 return false;
136 }
137
138 return true;
139 }
140
141 return true;
142 }
143
144 /**
145 * Check whether an IF or ELSE token is part of an "else if".
146 *
147 * @since 1.0.0
148 *
149 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
150 * @param int $stackPtr The position of the token we are checking.
151 *
152 * @return bool
153 */
154 public static function isElseIf(File $phpcsFile, $stackPtr)
155 {
156 $tokens = $phpcsFile->getTokens();
157
158 // Check for the existence of the token.
159 if (isset($tokens[$stackPtr]) === false) {
160 return false;
161 }
162
163 if ($tokens[$stackPtr]['code'] === \T_ELSEIF) {
164 return true;
165 }
166
167 if ($tokens[$stackPtr]['code'] !== \T_ELSE && $tokens[$stackPtr]['code'] !== \T_IF) {
168 return false;
169 }
170
171 if ($tokens[$stackPtr]['code'] === \T_ELSE && isset($tokens[$stackPtr]['scope_opener']) === true) {
172 return false;
173 }
174
175 switch ($tokens[$stackPtr]['code']) {
176 case \T_ELSE:
177 $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
178 if ($next !== false && $tokens[$next]['code'] === \T_IF) {
179 return true;
180 }
181 break;
182
183 case \T_IF:
184 $previous = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
185 if ($previous !== false && $tokens[$previous]['code'] === \T_ELSE) {
186 return true;
187 }
188 break;
189 }
190
191 return false;
192 }
193
194 /**
195 * Retrieve the exception(s) being caught in a CATCH condition.
196 *
197 * @since 1.0.0
198 *
199 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
200 * @param int $stackPtr The position of the token we are checking.
201 *
202 * @return array<int, array<string, string|int>>
203 * Array with information about the caught Exception(s).
204 * The returned array will contain the following information for
205 * each caught exception:
206 * ```php
207 * 0 => array(
208 * 'type' => string, // The type declaration for the exception being caught.
209 * 'type_token' => integer, // The stack pointer to the start of the type declaration.
210 * 'type_end_token' => integer, // The stack pointer to the end of the type declaration.
211 * )
212 * ```
213 * In case of an invalid catch structure, the array may be empty.
214 *
215 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified `$stackPtr` is not of
216 * type `T_CATCH` or doesn't exist.
217 * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If no parenthesis opener or closer can be
218 * determined (parse error).
219 */
220 public static function getCaughtExceptions(File $phpcsFile, $stackPtr)
221 {
222 $tokens = $phpcsFile->getTokens();
223
224 if (isset($tokens[$stackPtr]) === false
225 || $tokens[$stackPtr]['code'] !== \T_CATCH
226 ) {
227 throw new RuntimeException('$stackPtr must be of type T_CATCH');
228 }
229
230 if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
231 throw new RuntimeException('Parentheses opener/closer of the T_CATCH could not be determined');
232 }
233
234 $opener = $tokens[$stackPtr]['parenthesis_opener'];
235 $closer = $tokens[$stackPtr]['parenthesis_closer'];
236 $exceptions = [];
237
238 $foundName = '';
239 $firstToken = null;
240 $lastToken = null;
241
242 for ($i = ($opener + 1); $i <= $closer; $i++) {
243 if (isset(Tokens::$emptyTokens[$tokens[$i]['code']])) {
244 continue;
245 }
246
247 if (isset(Collections::namespacedNameTokens()[$tokens[$i]['code']]) === false) {
248 // Add the current exception to the result array if one was found.
249 if ($foundName !== '') {
250 $exceptions[] = [
251 'type' => $foundName,
252 'type_token' => $firstToken,
253 'type_end_token' => $lastToken,
254 ];
255 }
256
257 if ($tokens[$i]['code'] === \T_BITWISE_OR) {
258 // Multi-catch. Reset and continue.
259 $foundName = '';
260 $firstToken = null;
261 $lastToken = null;
262 continue;
263 }
264
265 break;
266 }
267
268 if (isset($firstToken) === false) {
269 $firstToken = $i;
270 }
271
272 $foundName .= $tokens[$i]['content'];
273 $lastToken = $i;
274 }
275
276 return $exceptions;
277 }
278 }
279