Total Complexity | 111 |
Total Lines | 728 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like TokensAnalyzer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use TokensAnalyzer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
31 | final class TokensAnalyzer |
||
32 | { |
||
33 | /** |
||
34 | * Tokens collection instance. |
||
35 | * |
||
36 | * @var Tokens |
||
37 | */ |
||
38 | private $tokens; |
||
39 | |||
40 | /** |
||
41 | * @var ?GotoLabelAnalyzer |
||
42 | */ |
||
43 | private $gotoLabelAnalyzer; |
||
44 | |||
45 | public function __construct(Tokens $tokens) |
||
46 | { |
||
47 | $this->tokens = $tokens; |
||
48 | } |
||
49 | |||
50 | /** |
||
51 | * Get indexes of methods and properties in classy code (classes, interfaces and traits). |
||
52 | * |
||
53 | * @return array[] |
||
54 | */ |
||
55 | public function getClassyElements(): array |
||
56 | { |
||
57 | $elements = []; |
||
58 | |||
59 | for ($index = 1, $count = \count($this->tokens) - 2; $index < $count; ++$index) { |
||
60 | if ($this->tokens[$index]->isClassy()) { |
||
61 | [$index, $newElements] = $this->findClassyElements($index, $index); |
||
62 | $elements += $newElements; |
||
63 | } |
||
64 | } |
||
65 | |||
66 | ksort($elements); |
||
67 | |||
68 | return $elements; |
||
69 | } |
||
70 | |||
71 | /** |
||
72 | * Get indexes of namespace uses. |
||
73 | * |
||
74 | * @param bool $perNamespace Return namespace uses per namespace |
||
75 | * |
||
76 | * @return int[]|int[][] |
||
77 | */ |
||
78 | public function getImportUseIndexes(bool $perNamespace = false): array |
||
79 | { |
||
80 | $tokens = $this->tokens; |
||
81 | |||
82 | $uses = []; |
||
83 | $namespaceIndex = 0; |
||
84 | |||
85 | for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) { |
||
86 | $token = $tokens[$index]; |
||
87 | |||
88 | if ($token->isGivenKind(T_NAMESPACE)) { |
||
89 | $nextTokenIndex = $tokens->getNextTokenOfKind($index, [';', '{']); |
||
90 | $nextToken = $tokens[$nextTokenIndex]; |
||
91 | |||
92 | if ($nextToken->equals('{')) { |
||
93 | $index = $nextTokenIndex; |
||
94 | } |
||
95 | |||
96 | if ($perNamespace) { |
||
97 | ++$namespaceIndex; |
||
98 | } |
||
99 | |||
100 | continue; |
||
101 | } |
||
102 | |||
103 | if ($token->isGivenKind(T_USE)) { |
||
104 | $uses[$namespaceIndex][] = $index; |
||
105 | } |
||
106 | } |
||
107 | |||
108 | if (!$perNamespace && isset($uses[$namespaceIndex])) { |
||
109 | return $uses[$namespaceIndex]; |
||
110 | } |
||
111 | |||
112 | return $uses; |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * Check if there is an array at given index. |
||
117 | */ |
||
118 | public function isArray(int $index): bool |
||
119 | { |
||
120 | return $this->tokens[$index]->isGivenKind([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN]); |
||
121 | } |
||
122 | |||
123 | /** |
||
124 | * Check if the array at index is multiline. |
||
125 | * |
||
126 | * This only checks the root-level of the array. |
||
127 | */ |
||
128 | public function isArrayMultiLine(int $index): bool |
||
129 | { |
||
130 | if (!$this->isArray($index)) { |
||
131 | throw new \InvalidArgumentException(sprintf('Not an array at given index %d.', $index)); |
||
132 | } |
||
133 | |||
134 | $tokens = $this->tokens; |
||
135 | |||
136 | // Skip only when its an array, for short arrays we need the brace for correct |
||
137 | // level counting |
||
138 | if ($tokens[$index]->isGivenKind(T_ARRAY)) { |
||
139 | $index = $tokens->getNextMeaningfulToken($index); |
||
140 | } |
||
141 | |||
142 | return $this->isBlockMultiline($tokens, $index); |
||
|
|||
143 | } |
||
144 | |||
145 | public function isBlockMultiline(Tokens $tokens, int $index): bool |
||
146 | { |
||
147 | $blockType = Tokens::detectBlockType($tokens[$index]); |
||
148 | |||
149 | if (null === $blockType || !$blockType['isStart']) { |
||
150 | throw new \InvalidArgumentException(sprintf('Not an block start at given index %d.', $index)); |
||
151 | } |
||
152 | |||
153 | $endIndex = $tokens->findBlockEnd($blockType['type'], $index); |
||
154 | |||
155 | for (++$index; $index < $endIndex; ++$index) { |
||
156 | $token = $tokens[$index]; |
||
157 | $blockType = Tokens::detectBlockType($token); |
||
158 | |||
159 | if ($blockType && $blockType['isStart']) { |
||
160 | $index = $tokens->findBlockEnd($blockType['type'], $index); |
||
161 | |||
162 | continue; |
||
163 | } |
||
164 | |||
165 | if ( |
||
166 | $token->isWhitespace() |
||
167 | && !$tokens[$index - 1]->isGivenKind(T_END_HEREDOC) |
||
168 | && false !== strpos($token->getContent(), "\n") |
||
169 | ) { |
||
170 | return true; |
||
171 | } |
||
172 | } |
||
173 | |||
174 | return false; |
||
175 | } |
||
176 | |||
177 | /** |
||
178 | * Returns the attributes of the method under the given index. |
||
179 | * |
||
180 | * The array has the following items: |
||
181 | * 'visibility' int|null T_PRIVATE, T_PROTECTED or T_PUBLIC |
||
182 | * 'static' bool |
||
183 | * 'abstract' bool |
||
184 | * 'final' bool |
||
185 | * |
||
186 | * @param int $index Token index of the method (T_FUNCTION) |
||
187 | */ |
||
188 | public function getMethodAttributes(int $index): array |
||
189 | { |
||
190 | $tokens = $this->tokens; |
||
191 | $token = $tokens[$index]; |
||
192 | |||
193 | if (!$token->isGivenKind(T_FUNCTION)) { |
||
194 | throw new \LogicException(sprintf('No T_FUNCTION at given index %d, got "%s".', $index, $token->getName())); |
||
195 | } |
||
196 | |||
197 | $attributes = [ |
||
198 | 'visibility' => null, |
||
199 | 'static' => false, |
||
200 | 'abstract' => false, |
||
201 | 'final' => false, |
||
202 | ]; |
||
203 | |||
204 | for ($i = $index; $i >= 0; --$i) { |
||
205 | $tokenIndex = $tokens->getPrevMeaningfulToken($i); |
||
206 | |||
207 | $i = $tokenIndex; |
||
208 | $token = $tokens[$tokenIndex]; |
||
209 | |||
210 | if ($token->isGivenKind(T_STATIC)) { |
||
211 | $attributes['static'] = true; |
||
212 | |||
213 | continue; |
||
214 | } |
||
215 | |||
216 | if ($token->isGivenKind(T_FINAL)) { |
||
217 | $attributes['final'] = true; |
||
218 | |||
219 | continue; |
||
220 | } |
||
221 | |||
222 | if ($token->isGivenKind(T_ABSTRACT)) { |
||
223 | $attributes['abstract'] = true; |
||
224 | |||
225 | continue; |
||
226 | } |
||
227 | |||
228 | // visibility |
||
229 | |||
230 | if ($token->isGivenKind(T_PRIVATE)) { |
||
231 | $attributes['visibility'] = T_PRIVATE; |
||
232 | |||
233 | continue; |
||
234 | } |
||
235 | |||
236 | if ($token->isGivenKind(T_PROTECTED)) { |
||
237 | $attributes['visibility'] = T_PROTECTED; |
||
238 | |||
239 | continue; |
||
240 | } |
||
241 | |||
242 | if ($token->isGivenKind(T_PUBLIC)) { |
||
243 | $attributes['visibility'] = T_PUBLIC; |
||
244 | |||
245 | continue; |
||
246 | } |
||
247 | |||
248 | // found a meaningful token that is not part of |
||
249 | // the function signature; stop looking |
||
250 | break; |
||
251 | } |
||
252 | |||
253 | return $attributes; |
||
254 | } |
||
255 | |||
256 | /** |
||
257 | * Check if there is an anonymous class under given index. |
||
258 | */ |
||
259 | public function isAnonymousClass(int $index): bool |
||
260 | { |
||
261 | if (!$this->tokens[$index]->isClassy()) { |
||
262 | throw new \LogicException(sprintf('No classy token at given index %d.', $index)); |
||
263 | } |
||
264 | |||
265 | if (!$this->tokens[$index]->isGivenKind(T_CLASS)) { |
||
266 | return false; |
||
267 | } |
||
268 | |||
269 | return $this->tokens[$this->tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_NEW); |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * Check if the function under given index is a lambda. |
||
274 | */ |
||
275 | public function isLambda(int $index): bool |
||
276 | { |
||
277 | if ( |
||
278 | !$this->tokens[$index]->isGivenKind(T_FUNCTION) |
||
279 | && (\PHP_VERSION_ID < 70400 || !$this->tokens[$index]->isGivenKind(T_FN)) |
||
280 | ) { |
||
281 | throw new \LogicException(sprintf('No T_FUNCTION or T_FN at given index %d, got "%s".', $index, $this->tokens[$index]->getName())); |
||
282 | } |
||
283 | |||
284 | $startParenthesisIndex = $this->tokens->getNextMeaningfulToken($index); |
||
285 | $startParenthesisToken = $this->tokens[$startParenthesisIndex]; |
||
286 | |||
287 | // skip & for `function & () {}` syntax |
||
288 | if ($startParenthesisToken->isGivenKind(CT::T_RETURN_REF)) { |
||
289 | $startParenthesisIndex = $this->tokens->getNextMeaningfulToken($startParenthesisIndex); |
||
290 | $startParenthesisToken = $this->tokens[$startParenthesisIndex]; |
||
291 | } |
||
292 | |||
293 | return $startParenthesisToken->equals('('); |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * Check if the T_STRING under given index is a constant invocation. |
||
298 | */ |
||
299 | public function isConstantInvocation(int $index): bool |
||
300 | { |
||
301 | if (!$this->tokens[$index]->isGivenKind(T_STRING)) { |
||
302 | throw new \LogicException(sprintf('No T_STRING at given index %d, got "%s".', $index, $this->tokens[$index]->getName())); |
||
303 | } |
||
304 | |||
305 | $nextIndex = $this->tokens->getNextMeaningfulToken($index); |
||
306 | |||
307 | if ( |
||
308 | $this->tokens[$nextIndex]->equalsAny(['(', '{']) |
||
309 | || $this->tokens[$nextIndex]->isGivenKind([T_AS, T_DOUBLE_COLON, T_ELLIPSIS, T_NS_SEPARATOR, CT::T_RETURN_REF, CT::T_TYPE_ALTERNATION, T_VARIABLE]) |
||
310 | ) { |
||
311 | return false; |
||
312 | } |
||
313 | |||
314 | $prevIndex = $this->tokens->getPrevMeaningfulToken($index); |
||
315 | |||
316 | if ($this->tokens[$prevIndex]->isGivenKind([T_AS, T_CLASS, T_CONST, T_DOUBLE_COLON, T_FUNCTION, T_GOTO, CT::T_GROUP_IMPORT_BRACE_OPEN, T_INTERFACE, T_TRAIT, CT::T_TYPE_COLON]) || $this->tokens[$prevIndex]->isObjectOperator()) { |
||
317 | return false; |
||
318 | } |
||
319 | |||
320 | while ($this->tokens[$prevIndex]->isGivenKind([CT::T_NAMESPACE_OPERATOR, T_NS_SEPARATOR, T_STRING])) { |
||
321 | $prevIndex = $this->tokens->getPrevMeaningfulToken($prevIndex); |
||
322 | } |
||
323 | |||
324 | if ($this->tokens[$prevIndex]->isGivenKind([CT::T_CONST_IMPORT, T_EXTENDS, CT::T_FUNCTION_IMPORT, T_IMPLEMENTS, T_INSTANCEOF, T_INSTEADOF, T_NAMESPACE, T_NEW, CT::T_NULLABLE_TYPE, CT::T_TYPE_COLON, T_USE, CT::T_USE_TRAIT])) { |
||
325 | return false; |
||
326 | } |
||
327 | |||
328 | // `FOO & $bar` could be: |
||
329 | // - function reference parameter: function baz(Foo & $bar) {} |
||
330 | // - bit operator: $x = FOO & $bar; |
||
331 | if ($this->tokens[$nextIndex]->equals('&') && $this->tokens[$this->tokens->getNextMeaningfulToken($nextIndex)]->isGivenKind(T_VARIABLE)) { |
||
332 | $checkIndex = $this->tokens->getPrevTokenOfKind($prevIndex, [';', '{', '}', [T_FUNCTION], [T_OPEN_TAG], [T_OPEN_TAG_WITH_ECHO]]); |
||
333 | |||
334 | if ($this->tokens[$checkIndex]->isGivenKind(T_FUNCTION)) { |
||
335 | return false; |
||
336 | } |
||
337 | } |
||
338 | |||
339 | // check for `extends`/`implements`/`use` list |
||
340 | if ($this->tokens[$prevIndex]->equals(',')) { |
||
341 | $checkIndex = $prevIndex; |
||
342 | while ($this->tokens[$checkIndex]->equalsAny([',', [T_AS], [CT::T_NAMESPACE_OPERATOR], [T_NS_SEPARATOR], [T_STRING]])) { |
||
343 | $checkIndex = $this->tokens->getPrevMeaningfulToken($checkIndex); |
||
344 | } |
||
345 | |||
346 | if ($this->tokens[$checkIndex]->isGivenKind([T_EXTENDS, CT::T_GROUP_IMPORT_BRACE_OPEN, T_IMPLEMENTS, T_USE, CT::T_USE_TRAIT])) { |
||
347 | return false; |
||
348 | } |
||
349 | } |
||
350 | |||
351 | // check for array in double quoted string: `"..$foo[bar].."` |
||
352 | if ($this->tokens[$prevIndex]->equals('[') && $this->tokens[$nextIndex]->equals(']')) { |
||
353 | $checkToken = $this->tokens[$this->tokens->getNextMeaningfulToken($nextIndex)]; |
||
354 | |||
355 | if ($checkToken->equals('"') || $checkToken->isGivenKind([T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES, T_ENCAPSED_AND_WHITESPACE, T_VARIABLE])) { |
||
356 | return false; |
||
357 | } |
||
358 | } |
||
359 | |||
360 | // check for attribute: `#[Foo]` |
||
361 | if (AttributeAnalyzer::isAttribute($this->tokens, $index)) { |
||
362 | return false; |
||
363 | } |
||
364 | |||
365 | // check for goto label |
||
366 | if ($this->tokens[$nextIndex]->equals(':')) { |
||
367 | if (null === $this->gotoLabelAnalyzer) { |
||
368 | $this->gotoLabelAnalyzer = new GotoLabelAnalyzer(); |
||
369 | } |
||
370 | |||
371 | if ($this->gotoLabelAnalyzer->belongsToGoToLabel($this->tokens, $nextIndex)) { |
||
372 | return false; |
||
373 | } |
||
374 | } |
||
375 | |||
376 | // check for non-capturing catches |
||
377 | while ($this->tokens[$prevIndex]->isGivenKind([CT::T_TYPE_ALTERNATION, T_STRING])) { |
||
378 | $prevIndex = $this->tokens->getPrevMeaningfulToken($prevIndex); |
||
379 | } |
||
380 | |||
381 | if ($this->tokens[$prevIndex]->equals('(')) { |
||
382 | $prevPrevIndex = $this->tokens->getPrevMeaningfulToken($prevIndex); |
||
383 | if ($this->tokens[$prevPrevIndex]->isGivenKind(T_CATCH)) { |
||
384 | return false; |
||
385 | } |
||
386 | } |
||
387 | |||
388 | return true; |
||
389 | } |
||
390 | |||
391 | /** |
||
392 | * Checks if there is an unary successor operator under given index. |
||
393 | */ |
||
394 | public function isUnarySuccessorOperator(int $index): bool |
||
395 | { |
||
396 | static $allowedPrevToken = [ |
||
397 | ']', |
||
398 | [T_STRING], |
||
399 | [T_VARIABLE], |
||
400 | [CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE], |
||
401 | [CT::T_DYNAMIC_PROP_BRACE_CLOSE], |
||
402 | [CT::T_DYNAMIC_VAR_BRACE_CLOSE], |
||
403 | ]; |
||
404 | |||
405 | $tokens = $this->tokens; |
||
406 | $token = $tokens[$index]; |
||
407 | |||
408 | if (!$token->isGivenKind([T_INC, T_DEC])) { |
||
409 | return false; |
||
410 | } |
||
411 | |||
412 | $prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)]; |
||
413 | |||
414 | return $prevToken->equalsAny($allowedPrevToken); |
||
415 | } |
||
416 | |||
417 | /** |
||
418 | * Checks if there is an unary predecessor operator under given index. |
||
419 | */ |
||
420 | public function isUnaryPredecessorOperator(int $index): bool |
||
421 | { |
||
422 | static $potentialSuccessorOperator = [T_INC, T_DEC]; |
||
423 | |||
424 | static $potentialBinaryOperator = ['+', '-', '&', [CT::T_RETURN_REF]]; |
||
425 | |||
426 | static $otherOperators; |
||
427 | if (null === $otherOperators) { |
||
428 | $otherOperators = ['!', '~', '@', [T_ELLIPSIS]]; |
||
429 | } |
||
430 | |||
431 | static $disallowedPrevTokens; |
||
432 | if (null === $disallowedPrevTokens) { |
||
433 | $disallowedPrevTokens = [ |
||
434 | ']', |
||
435 | '}', |
||
436 | ')', |
||
437 | '"', |
||
438 | '`', |
||
439 | [CT::T_ARRAY_SQUARE_BRACE_CLOSE], |
||
440 | [CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE], |
||
441 | [CT::T_DYNAMIC_PROP_BRACE_CLOSE], |
||
442 | [CT::T_DYNAMIC_VAR_BRACE_CLOSE], |
||
443 | [T_CLASS_C], |
||
444 | [T_CONSTANT_ENCAPSED_STRING], |
||
445 | [T_DEC], |
||
446 | [T_DIR], |
||
447 | [T_DNUMBER], |
||
448 | [T_FILE], |
||
449 | [T_FUNC_C], |
||
450 | [T_INC], |
||
451 | [T_LINE], |
||
452 | [T_LNUMBER], |
||
453 | [T_METHOD_C], |
||
454 | [T_NS_C], |
||
455 | [T_STRING], |
||
456 | [T_TRAIT_C], |
||
457 | [T_VARIABLE], |
||
458 | ]; |
||
459 | } |
||
460 | |||
461 | $tokens = $this->tokens; |
||
462 | $token = $tokens[$index]; |
||
463 | |||
464 | if ($token->isGivenKind($potentialSuccessorOperator)) { |
||
465 | return !$this->isUnarySuccessorOperator($index); |
||
466 | } |
||
467 | |||
468 | if ($token->equalsAny($otherOperators)) { |
||
469 | return true; |
||
470 | } |
||
471 | |||
472 | if (!$token->equalsAny($potentialBinaryOperator)) { |
||
473 | return false; |
||
474 | } |
||
475 | |||
476 | $prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)]; |
||
477 | |||
478 | if (!$prevToken->equalsAny($disallowedPrevTokens)) { |
||
479 | return true; |
||
480 | } |
||
481 | |||
482 | if (!$token->equals('&') || !$prevToken->isGivenKind(T_STRING)) { |
||
483 | return false; |
||
484 | } |
||
485 | |||
486 | static $searchTokens = [ |
||
487 | ';', |
||
488 | '{', |
||
489 | '}', |
||
490 | [T_FUNCTION], |
||
491 | [T_OPEN_TAG], |
||
492 | [T_OPEN_TAG_WITH_ECHO], |
||
493 | ]; |
||
494 | $prevToken = $tokens[$tokens->getPrevTokenOfKind($index, $searchTokens)]; |
||
495 | |||
496 | return $prevToken->isGivenKind(T_FUNCTION); |
||
497 | } |
||
498 | |||
499 | /** |
||
500 | * Checks if there is a binary operator under given index. |
||
501 | */ |
||
502 | public function isBinaryOperator(int $index): bool |
||
503 | { |
||
504 | static $nonArrayOperators = [ |
||
505 | '=' => true, |
||
506 | '*' => true, |
||
507 | '/' => true, |
||
508 | '%' => true, |
||
509 | '<' => true, |
||
510 | '>' => true, |
||
511 | '|' => true, |
||
512 | '^' => true, |
||
513 | '.' => true, |
||
514 | ]; |
||
515 | |||
516 | static $potentialUnaryNonArrayOperators = [ |
||
517 | '+' => true, |
||
518 | '-' => true, |
||
519 | '&' => true, |
||
520 | ]; |
||
521 | |||
522 | static $arrayOperators; |
||
523 | if (null === $arrayOperators) { |
||
524 | $arrayOperators = [ |
||
525 | T_AND_EQUAL => true, // &= |
||
526 | T_BOOLEAN_AND => true, // && |
||
527 | T_BOOLEAN_OR => true, // || |
||
528 | T_CONCAT_EQUAL => true, // .= |
||
529 | T_DIV_EQUAL => true, // /= |
||
530 | T_DOUBLE_ARROW => true, // => |
||
531 | T_IS_EQUAL => true, // == |
||
532 | T_IS_GREATER_OR_EQUAL => true, // >= |
||
533 | T_IS_IDENTICAL => true, // === |
||
534 | T_IS_NOT_EQUAL => true, // !=, <> |
||
535 | T_IS_NOT_IDENTICAL => true, // !== |
||
536 | T_IS_SMALLER_OR_EQUAL => true, // <= |
||
537 | T_LOGICAL_AND => true, // and |
||
538 | T_LOGICAL_OR => true, // or |
||
539 | T_LOGICAL_XOR => true, // xor |
||
540 | T_MINUS_EQUAL => true, // -= |
||
541 | T_MOD_EQUAL => true, // %= |
||
542 | T_MUL_EQUAL => true, // *= |
||
543 | T_OR_EQUAL => true, // |= |
||
544 | T_PLUS_EQUAL => true, // += |
||
545 | T_POW => true, // ** |
||
546 | T_POW_EQUAL => true, // **= |
||
547 | T_SL => true, // << |
||
548 | T_SL_EQUAL => true, // <<= |
||
549 | T_SR => true, // >> |
||
550 | T_SR_EQUAL => true, // >>= |
||
551 | T_XOR_EQUAL => true, // ^= |
||
552 | T_SPACESHIP => true, // <=> |
||
553 | T_COALESCE => true, // ?? |
||
554 | ]; |
||
555 | |||
556 | // @TODO: drop condition when PHP 7.4+ is required |
||
557 | if (\defined('T_COALESCE_EQUAL')) { |
||
558 | $arrayOperators[T_COALESCE_EQUAL] = true; // ??= |
||
559 | } |
||
560 | } |
||
561 | |||
562 | $tokens = $this->tokens; |
||
563 | $token = $tokens[$index]; |
||
564 | |||
565 | if ($token->isArray()) { |
||
566 | return isset($arrayOperators[$token->getId()]); |
||
567 | } |
||
568 | |||
569 | if (isset($nonArrayOperators[$token->getContent()])) { |
||
570 | return true; |
||
571 | } |
||
572 | |||
573 | if (isset($potentialUnaryNonArrayOperators[$token->getContent()])) { |
||
574 | return !$this->isUnaryPredecessorOperator($index); |
||
575 | } |
||
576 | |||
577 | return false; |
||
578 | } |
||
579 | |||
580 | /** |
||
581 | * Check if `T_WHILE` token at given index is `do { ... } while ();` syntax |
||
582 | * and not `while () { ...}`. |
||
583 | */ |
||
584 | public function isWhilePartOfDoWhile(int $index): bool |
||
585 | { |
||
586 | $tokens = $this->tokens; |
||
587 | $token = $tokens[$index]; |
||
588 | |||
589 | if (!$token->isGivenKind(T_WHILE)) { |
||
590 | throw new \LogicException(sprintf('No T_WHILE at given index %d, got "%s".', $index, $token->getName())); |
||
591 | } |
||
592 | |||
593 | $endIndex = $tokens->getPrevMeaningfulToken($index); |
||
594 | if (!$tokens[$endIndex]->equals('}')) { |
||
595 | return false; |
||
596 | } |
||
597 | |||
598 | $startIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $endIndex); |
||
599 | $beforeStartIndex = $tokens->getPrevMeaningfulToken($startIndex); |
||
600 | |||
601 | return $tokens[$beforeStartIndex]->isGivenKind(T_DO); |
||
602 | } |
||
603 | |||
604 | public function isSuperGlobal(int $index): bool |
||
625 | } |
||
626 | |||
627 | /** |
||
628 | * Find classy elements. |
||
629 | * |
||
630 | * Searches in tokens from the classy (start) index till the end (index) of the classy. |
||
631 | * Returns an array; first value is the index until the method has analysed (int), second the found classy elements (array). |
||
632 | * |
||
633 | * @param int $classIndex classy index |
||
634 | */ |
||
635 | private function findClassyElements(int $classIndex, int $index): array |
||
759 | } |
||
760 | } |
||
761 |