Mercurial > hg > isophonics-drupal-site
comparison vendor/squizlabs/php_codesniffer/src/Tokenizers/PHP.php @ 17:129ea1e6d783
Update, including to Drupal core 8.6.10
author | Chris Cannam |
---|---|
date | Thu, 28 Feb 2019 13:21:36 +0000 |
parents | |
children | af1871eacc83 |
comparison
equal
deleted
inserted
replaced
16:c2387f117808 | 17:129ea1e6d783 |
---|---|
1 <?php | |
2 /** | |
3 * Tokenizes PHP code. | |
4 * | |
5 * @author Greg Sherwood <gsherwood@squiz.net> | |
6 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) | |
7 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence | |
8 */ | |
9 | |
10 namespace PHP_CodeSniffer\Tokenizers; | |
11 | |
12 use PHP_CodeSniffer\Util; | |
13 | |
14 class PHP extends Tokenizer | |
15 { | |
16 | |
17 | |
18 /** | |
19 * A list of tokens that are allowed to open a scope. | |
20 * | |
21 * This array also contains information about what kind of token the scope | |
22 * opener uses to open and close the scope, if the token strictly requires | |
23 * an opener, if the token can share a scope closer, and who it can be shared | |
24 * with. An example of a token that shares a scope closer is a CASE scope. | |
25 * | |
26 * @var array | |
27 */ | |
28 public $scopeOpeners = [ | |
29 T_IF => [ | |
30 'start' => [ | |
31 T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, | |
32 T_COLON => T_COLON, | |
33 ], | |
34 'end' => [ | |
35 T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, | |
36 T_ENDIF => T_ENDIF, | |
37 T_ELSE => T_ELSE, | |
38 T_ELSEIF => T_ELSEIF, | |
39 ], | |
40 'strict' => false, | |
41 'shared' => false, | |
42 'with' => [ | |
43 T_ELSE => T_ELSE, | |
44 T_ELSEIF => T_ELSEIF, | |
45 ], | |
46 ], | |
47 T_TRY => [ | |
48 'start' => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET], | |
49 'end' => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET], | |
50 'strict' => true, | |
51 'shared' => false, | |
52 'with' => [], | |
53 ], | |
54 T_CATCH => [ | |
55 'start' => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET], | |
56 'end' => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET], | |
57 'strict' => true, | |
58 'shared' => false, | |
59 'with' => [], | |
60 ], | |
61 T_FINALLY => [ | |
62 'start' => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET], | |
63 'end' => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET], | |
64 'strict' => true, | |
65 'shared' => false, | |
66 'with' => [], | |
67 ], | |
68 T_ELSE => [ | |
69 'start' => [ | |
70 T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, | |
71 T_COLON => T_COLON, | |
72 ], | |
73 'end' => [ | |
74 T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, | |
75 T_ENDIF => T_ENDIF, | |
76 ], | |
77 'strict' => false, | |
78 'shared' => false, | |
79 'with' => [ | |
80 T_IF => T_IF, | |
81 T_ELSEIF => T_ELSEIF, | |
82 ], | |
83 ], | |
84 T_ELSEIF => [ | |
85 'start' => [ | |
86 T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, | |
87 T_COLON => T_COLON, | |
88 ], | |
89 'end' => [ | |
90 T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, | |
91 T_ENDIF => T_ENDIF, | |
92 T_ELSE => T_ELSE, | |
93 T_ELSEIF => T_ELSEIF, | |
94 ], | |
95 'strict' => false, | |
96 'shared' => false, | |
97 'with' => [ | |
98 T_IF => T_IF, | |
99 T_ELSE => T_ELSE, | |
100 ], | |
101 ], | |
102 T_FOR => [ | |
103 'start' => [ | |
104 T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, | |
105 T_COLON => T_COLON, | |
106 ], | |
107 'end' => [ | |
108 T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, | |
109 T_ENDFOR => T_ENDFOR, | |
110 ], | |
111 'strict' => false, | |
112 'shared' => false, | |
113 'with' => [], | |
114 ], | |
115 T_FOREACH => [ | |
116 'start' => [ | |
117 T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, | |
118 T_COLON => T_COLON, | |
119 ], | |
120 'end' => [ | |
121 T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, | |
122 T_ENDFOREACH => T_ENDFOREACH, | |
123 ], | |
124 'strict' => false, | |
125 'shared' => false, | |
126 'with' => [], | |
127 ], | |
128 T_INTERFACE => [ | |
129 'start' => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET], | |
130 'end' => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET], | |
131 'strict' => true, | |
132 'shared' => false, | |
133 'with' => [], | |
134 ], | |
135 T_FUNCTION => [ | |
136 'start' => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET], | |
137 'end' => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET], | |
138 'strict' => true, | |
139 'shared' => false, | |
140 'with' => [], | |
141 ], | |
142 T_CLASS => [ | |
143 'start' => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET], | |
144 'end' => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET], | |
145 'strict' => true, | |
146 'shared' => false, | |
147 'with' => [], | |
148 ], | |
149 T_TRAIT => [ | |
150 'start' => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET], | |
151 'end' => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET], | |
152 'strict' => true, | |
153 'shared' => false, | |
154 'with' => [], | |
155 ], | |
156 T_USE => [ | |
157 'start' => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET], | |
158 'end' => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET], | |
159 'strict' => false, | |
160 'shared' => false, | |
161 'with' => [], | |
162 ], | |
163 T_DECLARE => [ | |
164 'start' => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET], | |
165 'end' => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET], | |
166 'strict' => false, | |
167 'shared' => false, | |
168 'with' => [], | |
169 ], | |
170 T_NAMESPACE => [ | |
171 'start' => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET], | |
172 'end' => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET], | |
173 'strict' => false, | |
174 'shared' => false, | |
175 'with' => [], | |
176 ], | |
177 T_WHILE => [ | |
178 'start' => [ | |
179 T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, | |
180 T_COLON => T_COLON, | |
181 ], | |
182 'end' => [ | |
183 T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, | |
184 T_ENDWHILE => T_ENDWHILE, | |
185 ], | |
186 'strict' => false, | |
187 'shared' => false, | |
188 'with' => [], | |
189 ], | |
190 T_DO => [ | |
191 'start' => [T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET], | |
192 'end' => [T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET], | |
193 'strict' => true, | |
194 'shared' => false, | |
195 'with' => [], | |
196 ], | |
197 T_SWITCH => [ | |
198 'start' => [ | |
199 T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, | |
200 T_COLON => T_COLON, | |
201 ], | |
202 'end' => [ | |
203 T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, | |
204 T_ENDSWITCH => T_ENDSWITCH, | |
205 ], | |
206 'strict' => true, | |
207 'shared' => false, | |
208 'with' => [], | |
209 ], | |
210 T_CASE => [ | |
211 'start' => [ | |
212 T_COLON => T_COLON, | |
213 T_SEMICOLON => T_SEMICOLON, | |
214 ], | |
215 'end' => [ | |
216 T_BREAK => T_BREAK, | |
217 T_RETURN => T_RETURN, | |
218 T_CONTINUE => T_CONTINUE, | |
219 T_THROW => T_THROW, | |
220 T_EXIT => T_EXIT, | |
221 ], | |
222 'strict' => true, | |
223 'shared' => true, | |
224 'with' => [ | |
225 T_DEFAULT => T_DEFAULT, | |
226 T_CASE => T_CASE, | |
227 T_SWITCH => T_SWITCH, | |
228 ], | |
229 ], | |
230 T_DEFAULT => [ | |
231 'start' => [ | |
232 T_COLON => T_COLON, | |
233 T_SEMICOLON => T_SEMICOLON, | |
234 ], | |
235 'end' => [ | |
236 T_BREAK => T_BREAK, | |
237 T_RETURN => T_RETURN, | |
238 T_CONTINUE => T_CONTINUE, | |
239 T_THROW => T_THROW, | |
240 T_EXIT => T_EXIT, | |
241 ], | |
242 'strict' => true, | |
243 'shared' => true, | |
244 'with' => [ | |
245 T_CASE => T_CASE, | |
246 T_SWITCH => T_SWITCH, | |
247 ], | |
248 ], | |
249 T_START_HEREDOC => [ | |
250 'start' => [T_START_HEREDOC => T_START_HEREDOC], | |
251 'end' => [T_END_HEREDOC => T_END_HEREDOC], | |
252 'strict' => true, | |
253 'shared' => false, | |
254 'with' => [], | |
255 ], | |
256 T_START_NOWDOC => [ | |
257 'start' => [T_START_NOWDOC => T_START_NOWDOC], | |
258 'end' => [T_END_NOWDOC => T_END_NOWDOC], | |
259 'strict' => true, | |
260 'shared' => false, | |
261 'with' => [], | |
262 ], | |
263 ]; | |
264 | |
265 /** | |
266 * A list of tokens that end the scope. | |
267 * | |
268 * This array is just a unique collection of the end tokens | |
269 * from the _scopeOpeners array. The data is duplicated here to | |
270 * save time during parsing of the file. | |
271 * | |
272 * @var array | |
273 */ | |
274 public $endScopeTokens = [ | |
275 T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, | |
276 T_ENDIF => T_ENDIF, | |
277 T_ENDFOR => T_ENDFOR, | |
278 T_ENDFOREACH => T_ENDFOREACH, | |
279 T_ENDWHILE => T_ENDWHILE, | |
280 T_ENDSWITCH => T_ENDSWITCH, | |
281 T_BREAK => T_BREAK, | |
282 T_END_HEREDOC => T_END_HEREDOC, | |
283 ]; | |
284 | |
285 /** | |
286 * Known lengths of tokens. | |
287 * | |
288 * @var array<int, int> | |
289 */ | |
290 public $knownLengths = [ | |
291 T_ABSTRACT => 8, | |
292 T_AND_EQUAL => 2, | |
293 T_ARRAY => 5, | |
294 T_AS => 2, | |
295 T_BOOLEAN_AND => 2, | |
296 T_BOOLEAN_OR => 2, | |
297 T_BREAK => 5, | |
298 T_CALLABLE => 8, | |
299 T_CASE => 4, | |
300 T_CATCH => 5, | |
301 T_CLASS => 5, | |
302 T_CLASS_C => 9, | |
303 T_CLONE => 5, | |
304 T_CONCAT_EQUAL => 2, | |
305 T_CONST => 5, | |
306 T_CONTINUE => 8, | |
307 T_CURLY_OPEN => 2, | |
308 T_DEC => 2, | |
309 T_DECLARE => 7, | |
310 T_DEFAULT => 7, | |
311 T_DIR => 7, | |
312 T_DIV_EQUAL => 2, | |
313 T_DO => 2, | |
314 T_DOLLAR_OPEN_CURLY_BRACES => 2, | |
315 T_DOUBLE_ARROW => 2, | |
316 T_DOUBLE_COLON => 2, | |
317 T_ECHO => 4, | |
318 T_ELSE => 4, | |
319 T_ELSEIF => 6, | |
320 T_EMPTY => 5, | |
321 T_ENDDECLARE => 10, | |
322 T_ENDFOR => 6, | |
323 T_ENDFOREACH => 10, | |
324 T_ENDIF => 5, | |
325 T_ENDSWITCH => 9, | |
326 T_ENDWHILE => 8, | |
327 T_EVAL => 4, | |
328 T_EXTENDS => 7, | |
329 T_FILE => 8, | |
330 T_FINAL => 5, | |
331 T_FINALLY => 7, | |
332 T_FOR => 3, | |
333 T_FOREACH => 7, | |
334 T_FUNCTION => 8, | |
335 T_FUNC_C => 12, | |
336 T_GLOBAL => 6, | |
337 T_GOTO => 4, | |
338 T_HALT_COMPILER => 15, | |
339 T_IF => 2, | |
340 T_IMPLEMENTS => 10, | |
341 T_INC => 2, | |
342 T_INCLUDE => 7, | |
343 T_INCLUDE_ONCE => 12, | |
344 T_INSTANCEOF => 10, | |
345 T_INSTEADOF => 9, | |
346 T_INTERFACE => 9, | |
347 T_ISSET => 5, | |
348 T_IS_EQUAL => 2, | |
349 T_IS_GREATER_OR_EQUAL => 2, | |
350 T_IS_IDENTICAL => 3, | |
351 T_IS_NOT_EQUAL => 2, | |
352 T_IS_NOT_IDENTICAL => 3, | |
353 T_IS_SMALLER_OR_EQUAL => 2, | |
354 T_LINE => 8, | |
355 T_LIST => 4, | |
356 T_LOGICAL_AND => 3, | |
357 T_LOGICAL_OR => 2, | |
358 T_LOGICAL_XOR => 3, | |
359 T_METHOD_C => 10, | |
360 T_MINUS_EQUAL => 2, | |
361 T_POW_EQUAL => 3, | |
362 T_MOD_EQUAL => 2, | |
363 T_MUL_EQUAL => 2, | |
364 T_NAMESPACE => 9, | |
365 T_NS_C => 13, | |
366 T_NS_SEPARATOR => 1, | |
367 T_NEW => 3, | |
368 T_OBJECT_OPERATOR => 2, | |
369 T_OPEN_TAG_WITH_ECHO => 3, | |
370 T_OR_EQUAL => 2, | |
371 T_PLUS_EQUAL => 2, | |
372 T_PRINT => 5, | |
373 T_PRIVATE => 7, | |
374 T_PUBLIC => 6, | |
375 T_PROTECTED => 9, | |
376 T_REQUIRE => 7, | |
377 T_REQUIRE_ONCE => 12, | |
378 T_RETURN => 6, | |
379 T_STATIC => 6, | |
380 T_SWITCH => 6, | |
381 T_THROW => 5, | |
382 T_TRAIT => 5, | |
383 T_TRAIT_C => 9, | |
384 T_TRY => 3, | |
385 T_UNSET => 5, | |
386 T_USE => 3, | |
387 T_VAR => 3, | |
388 T_WHILE => 5, | |
389 T_XOR_EQUAL => 2, | |
390 T_YIELD => 5, | |
391 T_OPEN_CURLY_BRACKET => 1, | |
392 T_CLOSE_CURLY_BRACKET => 1, | |
393 T_OPEN_SQUARE_BRACKET => 1, | |
394 T_CLOSE_SQUARE_BRACKET => 1, | |
395 T_OPEN_PARENTHESIS => 1, | |
396 T_CLOSE_PARENTHESIS => 1, | |
397 T_COLON => 1, | |
398 T_STRING_CONCAT => 1, | |
399 T_INLINE_THEN => 1, | |
400 T_INLINE_ELSE => 1, | |
401 T_NULLABLE => 1, | |
402 T_NULL => 4, | |
403 T_FALSE => 5, | |
404 T_TRUE => 4, | |
405 T_SEMICOLON => 1, | |
406 T_EQUAL => 1, | |
407 T_MULTIPLY => 1, | |
408 T_DIVIDE => 1, | |
409 T_PLUS => 1, | |
410 T_MINUS => 1, | |
411 T_MODULUS => 1, | |
412 T_POW => 2, | |
413 T_SPACESHIP => 3, | |
414 T_COALESCE => 2, | |
415 T_COALESCE_EQUAL => 3, | |
416 T_BITWISE_AND => 1, | |
417 T_BITWISE_OR => 1, | |
418 T_BITWISE_XOR => 1, | |
419 T_SL => 2, | |
420 T_SR => 2, | |
421 T_SL_EQUAL => 3, | |
422 T_SR_EQUAL => 3, | |
423 T_GREATER_THAN => 1, | |
424 T_LESS_THAN => 1, | |
425 T_BOOLEAN_NOT => 1, | |
426 T_SELF => 4, | |
427 T_PARENT => 6, | |
428 T_COMMA => 1, | |
429 T_THIS => 4, | |
430 T_CLOSURE => 8, | |
431 T_BACKTICK => 1, | |
432 T_OPEN_SHORT_ARRAY => 1, | |
433 T_CLOSE_SHORT_ARRAY => 1, | |
434 ]; | |
435 | |
436 | |
437 /** | |
438 * A cache of different token types, resolved into arrays. | |
439 * | |
440 * @var array | |
441 * @see standardiseToken() | |
442 */ | |
443 private static $resolveTokenCache = []; | |
444 | |
445 | |
446 /** | |
447 * Creates an array of tokens when given some PHP code. | |
448 * | |
449 * Starts by using token_get_all() but does a lot of extra processing | |
450 * to insert information about the context of the token. | |
451 * | |
452 * @param string $string The string to tokenize. | |
453 * | |
454 * @return array | |
455 */ | |
456 protected function tokenize($string) | |
457 { | |
458 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
459 echo "\t*** START PHP TOKENIZING ***".PHP_EOL; | |
460 $isWin = false; | |
461 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { | |
462 $isWin = true; | |
463 } | |
464 } | |
465 | |
466 $tokens = @token_get_all($string); | |
467 $finalTokens = []; | |
468 | |
469 $newStackPtr = 0; | |
470 $numTokens = count($tokens); | |
471 $lastNotEmptyToken = 0; | |
472 | |
473 $insideInlineIf = []; | |
474 $insideUseGroup = false; | |
475 | |
476 $commentTokenizer = new Comment(); | |
477 | |
478 for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) { | |
479 // Special case for tokens we have needed to blank out. | |
480 if ($tokens[$stackPtr] === null) { | |
481 continue; | |
482 } | |
483 | |
484 $token = (array) $tokens[$stackPtr]; | |
485 $tokenIsArray = isset($token[1]); | |
486 | |
487 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
488 if ($tokenIsArray === true) { | |
489 $type = Util\Tokens::tokenName($token[0]); | |
490 $content = Util\Common::prepareForOutput($token[1]); | |
491 } else { | |
492 $newToken = self::resolveSimpleToken($token[0]); | |
493 $type = $newToken['type']; | |
494 $content = Util\Common::prepareForOutput($token[0]); | |
495 } | |
496 | |
497 echo "\tProcess token "; | |
498 if ($tokenIsArray === true) { | |
499 echo "[$stackPtr]"; | |
500 } else { | |
501 echo " $stackPtr "; | |
502 } | |
503 | |
504 echo ": $type => $content"; | |
505 }//end if | |
506 | |
507 if ($newStackPtr > 0 && $finalTokens[($newStackPtr - 1)]['code'] !== T_WHITESPACE) { | |
508 $lastNotEmptyToken = ($newStackPtr - 1); | |
509 } | |
510 | |
511 /* | |
512 If we are using \r\n newline characters, the \r and \n are sometimes | |
513 split over two tokens. This normally occurs after comments. We need | |
514 to merge these two characters together so that our line endings are | |
515 consistent for all lines. | |
516 */ | |
517 | |
518 if ($tokenIsArray === true && substr($token[1], -1) === "\r") { | |
519 if (isset($tokens[($stackPtr + 1)]) === true | |
520 && is_array($tokens[($stackPtr + 1)]) === true | |
521 && $tokens[($stackPtr + 1)][1][0] === "\n" | |
522 ) { | |
523 $token[1] .= "\n"; | |
524 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
525 if ($isWin === true) { | |
526 echo '\n'; | |
527 } else { | |
528 echo "\033[30;1m\\n\033[0m"; | |
529 } | |
530 } | |
531 | |
532 if ($tokens[($stackPtr + 1)][1] === "\n") { | |
533 // This token's content has been merged into the previous, | |
534 // so we can skip it. | |
535 $tokens[($stackPtr + 1)] = ''; | |
536 } else { | |
537 $tokens[($stackPtr + 1)][1] = substr($tokens[($stackPtr + 1)][1], 1); | |
538 } | |
539 } | |
540 }//end if | |
541 | |
542 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
543 echo PHP_EOL; | |
544 } | |
545 | |
546 /* | |
547 Parse doc blocks into something that can be easily iterated over. | |
548 */ | |
549 | |
550 if ($tokenIsArray === true | |
551 && ($token[0] === T_DOC_COMMENT | |
552 || ($token[0] === T_COMMENT && strpos($token[1], '/**') === 0)) | |
553 ) { | |
554 $commentTokens = $commentTokenizer->tokenizeString($token[1], $this->eolChar, $newStackPtr); | |
555 foreach ($commentTokens as $commentToken) { | |
556 $finalTokens[$newStackPtr] = $commentToken; | |
557 $newStackPtr++; | |
558 } | |
559 | |
560 continue; | |
561 } | |
562 | |
563 /* | |
564 If this is a double quoted string, PHP will tokenize the whole | |
565 thing which causes problems with the scope map when braces are | |
566 within the string. So we need to merge the tokens together to | |
567 provide a single string. | |
568 */ | |
569 | |
570 if ($tokenIsArray === false && ($token[0] === '"' || $token[0] === 'b"')) { | |
571 // Binary casts need a special token. | |
572 if ($token[0] === 'b"') { | |
573 $finalTokens[$newStackPtr] = [ | |
574 'code' => T_BINARY_CAST, | |
575 'type' => 'T_BINARY_CAST', | |
576 'content' => 'b', | |
577 ]; | |
578 $newStackPtr++; | |
579 } | |
580 | |
581 $tokenContent = '"'; | |
582 $nestedVars = []; | |
583 for ($i = ($stackPtr + 1); $i < $numTokens; $i++) { | |
584 $subToken = (array) $tokens[$i]; | |
585 $subTokenIsArray = isset($subToken[1]); | |
586 | |
587 if ($subTokenIsArray === true) { | |
588 $tokenContent .= $subToken[1]; | |
589 if ($subToken[1] === '{' | |
590 && $subToken[0] !== T_ENCAPSED_AND_WHITESPACE | |
591 ) { | |
592 $nestedVars[] = $i; | |
593 } | |
594 } else { | |
595 $tokenContent .= $subToken[0]; | |
596 if ($subToken[0] === '}') { | |
597 array_pop($nestedVars); | |
598 } | |
599 } | |
600 | |
601 if ($subTokenIsArray === false | |
602 && $subToken[0] === '"' | |
603 && empty($nestedVars) === true | |
604 ) { | |
605 // We found the other end of the double quoted string. | |
606 break; | |
607 } | |
608 }//end for | |
609 | |
610 $stackPtr = $i; | |
611 | |
612 // Convert each line within the double quoted string to a | |
613 // new token, so it conforms with other multiple line tokens. | |
614 $tokenLines = explode($this->eolChar, $tokenContent); | |
615 $numLines = count($tokenLines); | |
616 $newToken = []; | |
617 | |
618 for ($j = 0; $j < $numLines; $j++) { | |
619 $newToken['content'] = $tokenLines[$j]; | |
620 if ($j === ($numLines - 1)) { | |
621 if ($tokenLines[$j] === '') { | |
622 break; | |
623 } | |
624 } else { | |
625 $newToken['content'] .= $this->eolChar; | |
626 } | |
627 | |
628 $newToken['code'] = T_DOUBLE_QUOTED_STRING; | |
629 $newToken['type'] = 'T_DOUBLE_QUOTED_STRING'; | |
630 $finalTokens[$newStackPtr] = $newToken; | |
631 $newStackPtr++; | |
632 } | |
633 | |
634 // Continue, as we're done with this token. | |
635 continue; | |
636 }//end if | |
637 | |
638 /* | |
639 Detect binary casting and assign the casts their own token. | |
640 */ | |
641 | |
642 if ($tokenIsArray === true | |
643 && $token[0] === T_CONSTANT_ENCAPSED_STRING | |
644 && (substr($token[1], 0, 2) === 'b"' | |
645 || substr($token[1], 0, 2) === "b'") | |
646 ) { | |
647 $finalTokens[$newStackPtr] = [ | |
648 'code' => T_BINARY_CAST, | |
649 'type' => 'T_BINARY_CAST', | |
650 'content' => 'b', | |
651 ]; | |
652 $newStackPtr++; | |
653 $token[1] = substr($token[1], 1); | |
654 } | |
655 | |
656 if ($tokenIsArray === true | |
657 && $token[0] === T_STRING_CAST | |
658 && preg_match('`^\(\s*binary\s*\)$`i', $token[1]) === 1 | |
659 ) { | |
660 $finalTokens[$newStackPtr] = [ | |
661 'code' => T_BINARY_CAST, | |
662 'type' => 'T_BINARY_CAST', | |
663 'content' => $token[1], | |
664 ]; | |
665 $newStackPtr++; | |
666 continue; | |
667 } | |
668 | |
669 /* | |
670 If this is a heredoc, PHP will tokenize the whole | |
671 thing which causes problems when heredocs don't | |
672 contain real PHP code, which is almost never. | |
673 We want to leave the start and end heredoc tokens | |
674 alone though. | |
675 */ | |
676 | |
677 if ($tokenIsArray === true && $token[0] === T_START_HEREDOC) { | |
678 // Add the start heredoc token to the final array. | |
679 $finalTokens[$newStackPtr] = self::standardiseToken($token); | |
680 | |
681 // Check if this is actually a nowdoc and use a different token | |
682 // to help the sniffs. | |
683 $nowdoc = false; | |
684 if (strpos($token[1], "'") !== false) { | |
685 $finalTokens[$newStackPtr]['code'] = T_START_NOWDOC; | |
686 $finalTokens[$newStackPtr]['type'] = 'T_START_NOWDOC'; | |
687 $nowdoc = true; | |
688 } | |
689 | |
690 $tokenContent = ''; | |
691 for ($i = ($stackPtr + 1); $i < $numTokens; $i++) { | |
692 $subTokenIsArray = is_array($tokens[$i]); | |
693 if ($subTokenIsArray === true | |
694 && $tokens[$i][0] === T_END_HEREDOC | |
695 ) { | |
696 // We found the other end of the heredoc. | |
697 break; | |
698 } | |
699 | |
700 if ($subTokenIsArray === true) { | |
701 $tokenContent .= $tokens[$i][1]; | |
702 } else { | |
703 $tokenContent .= $tokens[$i]; | |
704 } | |
705 } | |
706 | |
707 if ($i === $numTokens) { | |
708 // We got to the end of the file and never | |
709 // found the closing token, so this probably wasn't | |
710 // a heredoc. | |
711 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
712 $type = $finalTokens[$newStackPtr]['type']; | |
713 echo "\t\t* failed to find the end of the here/nowdoc".PHP_EOL; | |
714 echo "\t\t* token $stackPtr changed from $type to T_STRING".PHP_EOL; | |
715 } | |
716 | |
717 $finalTokens[$newStackPtr]['code'] = T_STRING; | |
718 $finalTokens[$newStackPtr]['type'] = 'T_STRING'; | |
719 $newStackPtr++; | |
720 continue; | |
721 } | |
722 | |
723 $stackPtr = $i; | |
724 $newStackPtr++; | |
725 | |
726 // Convert each line within the heredoc to a | |
727 // new token, so it conforms with other multiple line tokens. | |
728 $tokenLines = explode($this->eolChar, $tokenContent); | |
729 $numLines = count($tokenLines); | |
730 $newToken = []; | |
731 | |
732 for ($j = 0; $j < $numLines; $j++) { | |
733 $newToken['content'] = $tokenLines[$j]; | |
734 if ($j === ($numLines - 1)) { | |
735 if ($tokenLines[$j] === '') { | |
736 break; | |
737 } | |
738 } else { | |
739 $newToken['content'] .= $this->eolChar; | |
740 } | |
741 | |
742 if ($nowdoc === true) { | |
743 $newToken['code'] = T_NOWDOC; | |
744 $newToken['type'] = 'T_NOWDOC'; | |
745 } else { | |
746 $newToken['code'] = T_HEREDOC; | |
747 $newToken['type'] = 'T_HEREDOC'; | |
748 } | |
749 | |
750 $finalTokens[$newStackPtr] = $newToken; | |
751 $newStackPtr++; | |
752 }//end for | |
753 | |
754 // Add the end heredoc token to the final array. | |
755 $finalTokens[$newStackPtr] = self::standardiseToken($tokens[$stackPtr]); | |
756 | |
757 if ($nowdoc === true) { | |
758 $finalTokens[$newStackPtr]['code'] = T_END_NOWDOC; | |
759 $finalTokens[$newStackPtr]['type'] = 'T_END_NOWDOC'; | |
760 } | |
761 | |
762 $newStackPtr++; | |
763 | |
764 // Continue, as we're done with this token. | |
765 continue; | |
766 }//end if | |
767 | |
768 /* | |
769 Before PHP 7.0, the "yield from" was tokenized as | |
770 T_YIELD, T_WHITESPACE and T_STRING. So look for | |
771 and change this token in earlier versions. | |
772 */ | |
773 | |
774 if (PHP_VERSION_ID < 70000 | |
775 && PHP_VERSION_ID >= 50500 | |
776 && $tokenIsArray === true | |
777 && $token[0] === T_YIELD | |
778 && isset($tokens[($stackPtr + 1)]) === true | |
779 && isset($tokens[($stackPtr + 2)]) === true | |
780 && $tokens[($stackPtr + 1)][0] === T_WHITESPACE | |
781 && $tokens[($stackPtr + 2)][0] === T_STRING | |
782 && strtolower($tokens[($stackPtr + 2)][1]) === 'from' | |
783 ) { | |
784 // Could be multi-line, so just the token stack. | |
785 $token[0] = T_YIELD_FROM; | |
786 $token[1] = $token[1].$tokens[($stackPtr + 1)][1].$tokens[($stackPtr + 2)][1]; | |
787 | |
788 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
789 for ($i = ($stackPtr + 1); $i <= ($stackPtr + 2); $i++) { | |
790 $type = Util\Tokens::tokenName($tokens[$i][0]); | |
791 $content = Util\Common::prepareForOutput($tokens[$i][1]); | |
792 echo "\t\t* token $i merged into T_YIELD_FROM; was: $type => $content".PHP_EOL; | |
793 } | |
794 } | |
795 | |
796 $tokens[($stackPtr + 1)] = null; | |
797 $tokens[($stackPtr + 2)] = null; | |
798 } | |
799 | |
800 /* | |
801 Before PHP 5.5, the yield keyword was tokenized as | |
802 T_STRING. So look for and change this token in | |
803 earlier versions. | |
804 Checks also if it is just "yield" or "yield from". | |
805 */ | |
806 | |
807 if (PHP_VERSION_ID < 50500 | |
808 && $tokenIsArray === true | |
809 && $token[0] === T_STRING | |
810 && strtolower($token[1]) === 'yield' | |
811 ) { | |
812 if (isset($tokens[($stackPtr + 1)]) === true | |
813 && isset($tokens[($stackPtr + 2)]) === true | |
814 && $tokens[($stackPtr + 1)][0] === T_WHITESPACE | |
815 && $tokens[($stackPtr + 2)][0] === T_STRING | |
816 && strtolower($tokens[($stackPtr + 2)][1]) === 'from' | |
817 ) { | |
818 // Could be multi-line, so just just the token stack. | |
819 $token[0] = T_YIELD_FROM; | |
820 $token[1] = $token[1].$tokens[($stackPtr + 1)][1].$tokens[($stackPtr + 2)][1]; | |
821 | |
822 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
823 for ($i = ($stackPtr + 1); $i <= ($stackPtr + 2); $i++) { | |
824 $type = Util\Tokens::tokenName($tokens[$i][0]); | |
825 $content = Util\Common::prepareForOutput($tokens[$i][1]); | |
826 echo "\t\t* token $i merged into T_YIELD_FROM; was: $type => $content".PHP_EOL; | |
827 } | |
828 } | |
829 | |
830 $tokens[($stackPtr + 1)] = null; | |
831 $tokens[($stackPtr + 2)] = null; | |
832 } else { | |
833 $newToken = []; | |
834 $newToken['code'] = T_YIELD; | |
835 $newToken['type'] = 'T_YIELD'; | |
836 $newToken['content'] = $token[1]; | |
837 $finalTokens[$newStackPtr] = $newToken; | |
838 | |
839 $newStackPtr++; | |
840 continue; | |
841 }//end if | |
842 }//end if | |
843 | |
844 /* | |
845 Before PHP 5.6, the ... operator was tokenized as three | |
846 T_STRING_CONCAT tokens in a row. So look for and combine | |
847 these tokens in earlier versions. | |
848 */ | |
849 | |
850 if ($tokenIsArray === false | |
851 && $token[0] === '.' | |
852 && isset($tokens[($stackPtr + 1)]) === true | |
853 && isset($tokens[($stackPtr + 2)]) === true | |
854 && $tokens[($stackPtr + 1)] === '.' | |
855 && $tokens[($stackPtr + 2)] === '.' | |
856 ) { | |
857 $newToken = []; | |
858 $newToken['code'] = T_ELLIPSIS; | |
859 $newToken['type'] = 'T_ELLIPSIS'; | |
860 $newToken['content'] = '...'; | |
861 $finalTokens[$newStackPtr] = $newToken; | |
862 | |
863 $newStackPtr++; | |
864 $stackPtr += 2; | |
865 continue; | |
866 } | |
867 | |
868 /* | |
869 Before PHP 5.6, the ** operator was tokenized as two | |
870 T_MULTIPLY tokens in a row. So look for and combine | |
871 these tokens in earlier versions. | |
872 */ | |
873 | |
874 if ($tokenIsArray === false | |
875 && $token[0] === '*' | |
876 && isset($tokens[($stackPtr + 1)]) === true | |
877 && $tokens[($stackPtr + 1)] === '*' | |
878 ) { | |
879 $newToken = []; | |
880 $newToken['code'] = T_POW; | |
881 $newToken['type'] = 'T_POW'; | |
882 $newToken['content'] = '**'; | |
883 $finalTokens[$newStackPtr] = $newToken; | |
884 | |
885 $newStackPtr++; | |
886 $stackPtr++; | |
887 continue; | |
888 } | |
889 | |
890 /* | |
891 Before PHP 5.6, the **= operator was tokenized as | |
892 T_MULTIPLY followed by T_MUL_EQUAL. So look for and combine | |
893 these tokens in earlier versions. | |
894 */ | |
895 | |
896 if ($tokenIsArray === false | |
897 && $token[0] === '*' | |
898 && isset($tokens[($stackPtr + 1)]) === true | |
899 && is_array($tokens[($stackPtr + 1)]) === true | |
900 && $tokens[($stackPtr + 1)][1] === '*=' | |
901 ) { | |
902 $newToken = []; | |
903 $newToken['code'] = T_POW_EQUAL; | |
904 $newToken['type'] = 'T_POW_EQUAL'; | |
905 $newToken['content'] = '**='; | |
906 $finalTokens[$newStackPtr] = $newToken; | |
907 | |
908 $newStackPtr++; | |
909 $stackPtr++; | |
910 continue; | |
911 } | |
912 | |
913 /* | |
914 Before PHP 7, the ??= operator was tokenized as | |
915 T_INLINE_THEN, T_INLINE_THEN, T_EQUAL. | |
916 Between PHP 7.0 and 7.2, the ??= operator was tokenized as | |
917 T_COALESCE, T_EQUAL. | |
918 So look for and combine these tokens in earlier versions. | |
919 */ | |
920 | |
921 if (($tokenIsArray === false | |
922 && $token[0] === '?' | |
923 && isset($tokens[($stackPtr + 1)]) === true | |
924 && $tokens[($stackPtr + 1)][0] === '?' | |
925 && isset($tokens[($stackPtr + 2)]) === true | |
926 && $tokens[($stackPtr + 2)][0] === '=') | |
927 || ($tokenIsArray === true | |
928 && $token[0] === T_COALESCE | |
929 && isset($tokens[($stackPtr + 1)]) === true | |
930 && $tokens[($stackPtr + 1)][0] === '=') | |
931 ) { | |
932 $newToken = []; | |
933 $newToken['code'] = T_COALESCE_EQUAL; | |
934 $newToken['type'] = 'T_COALESCE_EQUAL'; | |
935 $newToken['content'] = '??='; | |
936 $finalTokens[$newStackPtr] = $newToken; | |
937 | |
938 $newStackPtr++; | |
939 $stackPtr++; | |
940 | |
941 if ($tokenIsArray === false) { | |
942 // Pre PHP 7. | |
943 $stackPtr++; | |
944 } | |
945 | |
946 continue; | |
947 } | |
948 | |
949 /* | |
950 Before PHP 7, the ?? operator was tokenized as | |
951 T_INLINE_THEN followed by T_INLINE_THEN. | |
952 So look for and combine these tokens in earlier versions. | |
953 */ | |
954 | |
955 if ($tokenIsArray === false | |
956 && $token[0] === '?' | |
957 && isset($tokens[($stackPtr + 1)]) === true | |
958 && $tokens[($stackPtr + 1)][0] === '?' | |
959 ) { | |
960 $newToken = []; | |
961 $newToken['code'] = T_COALESCE; | |
962 $newToken['type'] = 'T_COALESCE'; | |
963 $newToken['content'] = '??'; | |
964 $finalTokens[$newStackPtr] = $newToken; | |
965 | |
966 $newStackPtr++; | |
967 $stackPtr++; | |
968 continue; | |
969 } | |
970 | |
971 /* | |
972 Convert ? to T_NULLABLE OR T_INLINE_THEN | |
973 */ | |
974 | |
975 if ($tokenIsArray === false && $token[0] === '?') { | |
976 $newToken = []; | |
977 $newToken['content'] = '?'; | |
978 | |
979 $prevNonEmpty = null; | |
980 for ($i = ($stackPtr - 1); $i >= 0; $i--) { | |
981 if (is_array($tokens[$i]) === true) { | |
982 $tokenType = $tokens[$i][0]; | |
983 } else { | |
984 $tokenType = $tokens[$i]; | |
985 } | |
986 | |
987 if ($prevNonEmpty === null | |
988 && isset(Util\Tokens::$emptyTokens[$tokenType]) === false | |
989 ) { | |
990 // Found the previous non-empty token. | |
991 if ($tokenType === ':' || $tokenType === ',') { | |
992 $newToken['code'] = T_NULLABLE; | |
993 $newToken['type'] = 'T_NULLABLE'; | |
994 break; | |
995 } | |
996 | |
997 $prevNonEmpty = $tokenType; | |
998 } | |
999 | |
1000 if ($tokenType === T_FUNCTION) { | |
1001 $newToken['code'] = T_NULLABLE; | |
1002 $newToken['type'] = 'T_NULLABLE'; | |
1003 break; | |
1004 } else if (in_array($tokenType, [T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, '=', '{', ';']) === true) { | |
1005 $newToken['code'] = T_INLINE_THEN; | |
1006 $newToken['type'] = 'T_INLINE_THEN'; | |
1007 | |
1008 $insideInlineIf[] = $stackPtr; | |
1009 break; | |
1010 } | |
1011 }//end for | |
1012 | |
1013 $finalTokens[$newStackPtr] = $newToken; | |
1014 $newStackPtr++; | |
1015 continue; | |
1016 }//end if | |
1017 | |
1018 /* | |
1019 Tokens after a double colon may be look like scope openers, | |
1020 such as when writing code like Foo::NAMESPACE, but they are | |
1021 only ever variables or strings. | |
1022 */ | |
1023 | |
1024 if ($stackPtr > 1 | |
1025 && (is_array($tokens[($stackPtr - 1)]) === true | |
1026 && $tokens[($stackPtr - 1)][0] === T_PAAMAYIM_NEKUDOTAYIM) | |
1027 && $tokenIsArray === true | |
1028 && $token[0] !== T_STRING | |
1029 && $token[0] !== T_VARIABLE | |
1030 && $token[0] !== T_DOLLAR | |
1031 && isset(Util\Tokens::$emptyTokens[$token[0]]) === false | |
1032 ) { | |
1033 $newToken = []; | |
1034 $newToken['code'] = T_STRING; | |
1035 $newToken['type'] = 'T_STRING'; | |
1036 $newToken['content'] = $token[1]; | |
1037 $finalTokens[$newStackPtr] = $newToken; | |
1038 | |
1039 $newStackPtr++; | |
1040 continue; | |
1041 } | |
1042 | |
1043 /* | |
1044 The string-like token after a function keyword should always be | |
1045 tokenized as T_STRING even if it appears to be a different token, | |
1046 such as when writing code like: function default(): foo | |
1047 so go forward and change the token type before it is processed. | |
1048 */ | |
1049 | |
1050 if ($tokenIsArray === true | |
1051 && $token[0] === T_FUNCTION | |
1052 && $finalTokens[$lastNotEmptyToken]['code'] !== T_USE | |
1053 ) { | |
1054 for ($x = ($stackPtr + 1); $x < $numTokens; $x++) { | |
1055 if (is_array($tokens[$x]) === false | |
1056 || isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === false | |
1057 ) { | |
1058 // Non-empty content. | |
1059 break; | |
1060 } | |
1061 } | |
1062 | |
1063 if ($x < $numTokens && is_array($tokens[$x]) === true) { | |
1064 $tokens[$x][0] = T_STRING; | |
1065 } | |
1066 | |
1067 /* | |
1068 This is a special condition for T_ARRAY tokens used for | |
1069 function return types. We want to keep the parenthesis map clean, | |
1070 so let's tag these tokens as T_STRING. | |
1071 */ | |
1072 | |
1073 // Go looking for the colon to start the return type hint. | |
1074 // Start by finding the closing parenthesis of the function. | |
1075 $parenthesisStack = []; | |
1076 $parenthesisCloser = false; | |
1077 for ($x = ($stackPtr + 1); $x < $numTokens; $x++) { | |
1078 if (is_array($tokens[$x]) === false && $tokens[$x] === '(') { | |
1079 $parenthesisStack[] = $x; | |
1080 } else if (is_array($tokens[$x]) === false && $tokens[$x] === ')') { | |
1081 array_pop($parenthesisStack); | |
1082 if (empty($parenthesisStack) === true) { | |
1083 $parenthesisCloser = $x; | |
1084 break; | |
1085 } | |
1086 } | |
1087 } | |
1088 | |
1089 if ($parenthesisCloser !== false) { | |
1090 for ($x = ($parenthesisCloser + 1); $x < $numTokens; $x++) { | |
1091 if (is_array($tokens[$x]) === false | |
1092 || isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === false | |
1093 ) { | |
1094 // Non-empty content. | |
1095 if (is_array($tokens[$x]) === true && $tokens[$x][0] === T_USE) { | |
1096 // Found a use statements, so search ahead for the closing parenthesis. | |
1097 for ($x += 1; $x < $numTokens; $x++) { | |
1098 if (is_array($tokens[$x]) === false && $tokens[$x] === ')') { | |
1099 continue(2); | |
1100 } | |
1101 } | |
1102 } | |
1103 | |
1104 break; | |
1105 } | |
1106 } | |
1107 | |
1108 if (isset($tokens[$x]) === true | |
1109 && is_array($tokens[$x]) === false | |
1110 && $tokens[$x] === ':' | |
1111 ) { | |
1112 $allowed = [ | |
1113 T_STRING => T_STRING, | |
1114 T_ARRAY => T_ARRAY, | |
1115 T_CALLABLE => T_CALLABLE, | |
1116 T_SELF => T_SELF, | |
1117 T_PARENT => T_PARENT, | |
1118 T_NS_SEPARATOR => T_NS_SEPARATOR, | |
1119 ]; | |
1120 | |
1121 $allowed += Util\Tokens::$emptyTokens; | |
1122 | |
1123 // Find the start of the return type. | |
1124 for ($x += 1; $x < $numTokens; $x++) { | |
1125 if (is_array($tokens[$x]) === true | |
1126 && isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === true | |
1127 ) { | |
1128 // Whitespace or coments before the return type. | |
1129 continue; | |
1130 } | |
1131 | |
1132 if (is_array($tokens[$x]) === false && $tokens[$x] === '?') { | |
1133 // Found a nullable operator, so skip it. | |
1134 // But also covert the token to save the tokenizer | |
1135 // a bit of time later on. | |
1136 $tokens[$x] = [ | |
1137 T_NULLABLE, | |
1138 '?', | |
1139 ]; | |
1140 | |
1141 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1142 echo "\t\t* token $x changed from ? to T_NULLABLE".PHP_EOL; | |
1143 } | |
1144 | |
1145 continue; | |
1146 } | |
1147 | |
1148 break; | |
1149 }//end for | |
1150 | |
1151 // Any T_ARRAY tokens we find between here and the next | |
1152 // token that can't be part of the return type need to be | |
1153 // converted to T_STRING tokens. | |
1154 for ($x; $x < $numTokens; $x++) { | |
1155 if (is_array($tokens[$x]) === false || isset($allowed[$tokens[$x][0]]) === false) { | |
1156 break; | |
1157 } else if ($tokens[$x][0] === T_ARRAY) { | |
1158 $tokens[$x][0] = T_STRING; | |
1159 | |
1160 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1161 echo "\t\t* token $x changed from T_ARRAY to T_STRING".PHP_EOL; | |
1162 } | |
1163 } | |
1164 } | |
1165 }//end if | |
1166 }//end if | |
1167 }//end if | |
1168 | |
1169 /* | |
1170 Before PHP 7, the <=> operator was tokenized as | |
1171 T_IS_SMALLER_OR_EQUAL followed by T_GREATER_THAN. | |
1172 So look for and combine these tokens in earlier versions. | |
1173 */ | |
1174 | |
1175 if ($tokenIsArray === true | |
1176 && $token[0] === T_IS_SMALLER_OR_EQUAL | |
1177 && isset($tokens[($stackPtr + 1)]) === true | |
1178 && $tokens[($stackPtr + 1)][0] === '>' | |
1179 ) { | |
1180 $newToken = []; | |
1181 $newToken['code'] = T_SPACESHIP; | |
1182 $newToken['type'] = 'T_SPACESHIP'; | |
1183 $newToken['content'] = '<=>'; | |
1184 $finalTokens[$newStackPtr] = $newToken; | |
1185 | |
1186 $newStackPtr++; | |
1187 $stackPtr++; | |
1188 continue; | |
1189 } | |
1190 | |
1191 /* | |
1192 PHP doesn't assign a token to goto labels, so we have to. | |
1193 These are just string tokens with a single colon after them. Double | |
1194 colons are already tokenized and so don't interfere with this check. | |
1195 But we do have to account for CASE statements, that look just like | |
1196 goto labels. | |
1197 */ | |
1198 | |
1199 if ($tokenIsArray === true | |
1200 && $token[0] === T_STRING | |
1201 && isset($tokens[($stackPtr + 1)]) === true | |
1202 && $tokens[($stackPtr + 1)] === ':' | |
1203 && $tokens[($stackPtr - 1)][0] !== T_PAAMAYIM_NEKUDOTAYIM | |
1204 ) { | |
1205 $stopTokens = [ | |
1206 T_CASE => true, | |
1207 T_SEMICOLON => true, | |
1208 T_OPEN_CURLY_BRACKET => true, | |
1209 T_INLINE_THEN => true, | |
1210 ]; | |
1211 | |
1212 for ($x = ($newStackPtr - 1); $x > 0; $x--) { | |
1213 if (isset($stopTokens[$finalTokens[$x]['code']]) === true) { | |
1214 break; | |
1215 } | |
1216 } | |
1217 | |
1218 if ($finalTokens[$x]['code'] !== T_CASE | |
1219 && $finalTokens[$x]['code'] !== T_INLINE_THEN | |
1220 ) { | |
1221 $finalTokens[$newStackPtr] = [ | |
1222 'content' => $token[1].':', | |
1223 'code' => T_GOTO_LABEL, | |
1224 'type' => 'T_GOTO_LABEL', | |
1225 ]; | |
1226 | |
1227 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1228 echo "\t\t* token $stackPtr changed from T_STRING to T_GOTO_LABEL".PHP_EOL; | |
1229 echo "\t\t* skipping T_COLON token ".($stackPtr + 1).PHP_EOL; | |
1230 } | |
1231 | |
1232 $newStackPtr++; | |
1233 $stackPtr++; | |
1234 continue; | |
1235 } | |
1236 }//end if | |
1237 | |
1238 /* | |
1239 If this token has newlines in its content, split each line up | |
1240 and create a new token for each line. We do this so it's easier | |
1241 to ascertain where errors occur on a line. | |
1242 Note that $token[1] is the token's content. | |
1243 */ | |
1244 | |
1245 if ($tokenIsArray === true && strpos($token[1], $this->eolChar) !== false) { | |
1246 $tokenLines = explode($this->eolChar, $token[1]); | |
1247 $numLines = count($tokenLines); | |
1248 $newToken = [ | |
1249 'type' => Util\Tokens::tokenName($token[0]), | |
1250 'code' => $token[0], | |
1251 'content' => '', | |
1252 ]; | |
1253 | |
1254 for ($i = 0; $i < $numLines; $i++) { | |
1255 $newToken['content'] = $tokenLines[$i]; | |
1256 if ($i === ($numLines - 1)) { | |
1257 if ($tokenLines[$i] === '') { | |
1258 break; | |
1259 } | |
1260 } else { | |
1261 $newToken['content'] .= $this->eolChar; | |
1262 } | |
1263 | |
1264 $finalTokens[$newStackPtr] = $newToken; | |
1265 $newStackPtr++; | |
1266 } | |
1267 } else { | |
1268 if ($tokenIsArray === true && $token[0] === T_STRING) { | |
1269 // Some T_STRING tokens should remain that way | |
1270 // due to their context. | |
1271 $context = [ | |
1272 T_OBJECT_OPERATOR => true, | |
1273 T_FUNCTION => true, | |
1274 T_CLASS => true, | |
1275 T_EXTENDS => true, | |
1276 T_IMPLEMENTS => true, | |
1277 T_NEW => true, | |
1278 T_CONST => true, | |
1279 T_NS_SEPARATOR => true, | |
1280 T_USE => true, | |
1281 T_NAMESPACE => true, | |
1282 T_PAAMAYIM_NEKUDOTAYIM => true, | |
1283 ]; | |
1284 if (isset($context[$finalTokens[$lastNotEmptyToken]['code']]) === true) { | |
1285 // Special case for syntax like: return new self | |
1286 // where self should not be a string. | |
1287 if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW | |
1288 && strtolower($token[1]) === 'self' | |
1289 ) { | |
1290 $finalTokens[$newStackPtr] = [ | |
1291 'content' => $token[1], | |
1292 'code' => T_SELF, | |
1293 'type' => 'T_SELF', | |
1294 ]; | |
1295 } else { | |
1296 $finalTokens[$newStackPtr] = [ | |
1297 'content' => $token[1], | |
1298 'code' => T_STRING, | |
1299 'type' => 'T_STRING', | |
1300 ]; | |
1301 } | |
1302 | |
1303 $newStackPtr++; | |
1304 continue; | |
1305 }//end if | |
1306 }//end if | |
1307 | |
1308 $newToken = null; | |
1309 if ($tokenIsArray === false) { | |
1310 if (isset(self::$resolveTokenCache[$token[0]]) === true) { | |
1311 $newToken = self::$resolveTokenCache[$token[0]]; | |
1312 } | |
1313 } else { | |
1314 $cacheKey = null; | |
1315 if ($token[0] === T_STRING) { | |
1316 $cacheKey = strtolower($token[1]); | |
1317 } else if ($token[0] !== T_CURLY_OPEN) { | |
1318 $cacheKey = $token[0]; | |
1319 } | |
1320 | |
1321 if ($cacheKey !== null && isset(self::$resolveTokenCache[$cacheKey]) === true) { | |
1322 $newToken = self::$resolveTokenCache[$cacheKey]; | |
1323 $newToken['content'] = $token[1]; | |
1324 } | |
1325 } | |
1326 | |
1327 if ($newToken === null) { | |
1328 $newToken = self::standardiseToken($token); | |
1329 } | |
1330 | |
1331 // Convert colons that are actually the ELSE component of an | |
1332 // inline IF statement. | |
1333 if (empty($insideInlineIf) === false && $newToken['code'] === T_COLON) { | |
1334 // Make sure this isn't the return type separator of a closure. | |
1335 $isReturnType = false; | |
1336 for ($i = ($stackPtr - 1); $i > 0; $i--) { | |
1337 if (is_array($tokens[$i]) === false | |
1338 || ($tokens[$i][0] !== T_DOC_COMMENT | |
1339 && $tokens[$i][0] !== T_COMMENT | |
1340 && $tokens[$i][0] !== T_WHITESPACE) | |
1341 ) { | |
1342 break; | |
1343 } | |
1344 } | |
1345 | |
1346 if ($tokens[$i] === ')') { | |
1347 $parenCount = 1; | |
1348 for ($i--; $i > 0; $i--) { | |
1349 if ($tokens[$i] === '(') { | |
1350 $parenCount--; | |
1351 if ($parenCount === 0) { | |
1352 break; | |
1353 } | |
1354 } else if ($tokens[$i] === ')') { | |
1355 $parenCount++; | |
1356 } | |
1357 } | |
1358 | |
1359 // We've found the open parenthesis, so if the previous | |
1360 // non-empty token is FUNCTION or USE, this is a closure. | |
1361 for ($i--; $i > 0; $i--) { | |
1362 if (is_array($tokens[$i]) === false | |
1363 || ($tokens[$i][0] !== T_DOC_COMMENT | |
1364 && $tokens[$i][0] !== T_COMMENT | |
1365 && $tokens[$i][0] !== T_WHITESPACE) | |
1366 ) { | |
1367 break; | |
1368 } | |
1369 } | |
1370 | |
1371 if ($tokens[$i][0] === T_FUNCTION || $tokens[$i][0] === T_USE) { | |
1372 $isReturnType = true; | |
1373 } | |
1374 }//end if | |
1375 | |
1376 if ($isReturnType === false) { | |
1377 array_pop($insideInlineIf); | |
1378 $newToken['code'] = T_INLINE_ELSE; | |
1379 $newToken['type'] = 'T_INLINE_ELSE'; | |
1380 } | |
1381 }//end if | |
1382 | |
1383 // This is a special condition for T_ARRAY tokens used for | |
1384 // type hinting function arguments as being arrays. We want to keep | |
1385 // the parenthesis map clean, so let's tag these tokens as | |
1386 // T_STRING. | |
1387 if ($newToken['code'] === T_ARRAY) { | |
1388 for ($i = $stackPtr; $i < $numTokens; $i++) { | |
1389 if ($tokens[$i] === '(') { | |
1390 break; | |
1391 } else if ($tokens[$i][0] === T_VARIABLE) { | |
1392 $newToken['code'] = T_STRING; | |
1393 $newToken['type'] = 'T_STRING'; | |
1394 break; | |
1395 } | |
1396 } | |
1397 } | |
1398 | |
1399 // This is a special case when checking PHP 5.5+ code in PHP < 5.5 | |
1400 // where "finally" should be T_FINALLY instead of T_STRING. | |
1401 if ($newToken['code'] === T_STRING | |
1402 && strtolower($newToken['content']) === 'finally' | |
1403 ) { | |
1404 $newToken['code'] = T_FINALLY; | |
1405 $newToken['type'] = 'T_FINALLY'; | |
1406 } | |
1407 | |
1408 // This is a special case for the PHP 5.5 classname::class syntax | |
1409 // where "class" should be T_STRING instead of T_CLASS. | |
1410 if (($newToken['code'] === T_CLASS | |
1411 || $newToken['code'] === T_FUNCTION) | |
1412 && $finalTokens[$lastNotEmptyToken]['code'] === T_DOUBLE_COLON | |
1413 ) { | |
1414 $newToken['code'] = T_STRING; | |
1415 $newToken['type'] = 'T_STRING'; | |
1416 } | |
1417 | |
1418 // This is a special case for PHP 5.6 use function and use const | |
1419 // where "function" and "const" should be T_STRING instead of T_FUNCTION | |
1420 // and T_CONST. | |
1421 if (($newToken['code'] === T_FUNCTION | |
1422 || $newToken['code'] === T_CONST) | |
1423 && ($finalTokens[$lastNotEmptyToken]['code'] === T_USE || $insideUseGroup === true) | |
1424 ) { | |
1425 $newToken['code'] = T_STRING; | |
1426 $newToken['type'] = 'T_STRING'; | |
1427 } | |
1428 | |
1429 // This is a special case for use groups in PHP 7+ where leaving | |
1430 // the curly braces as their normal tokens would confuse | |
1431 // the scope map and sniffs. | |
1432 if ($newToken['code'] === T_OPEN_CURLY_BRACKET | |
1433 && $finalTokens[$lastNotEmptyToken]['code'] === T_NS_SEPARATOR | |
1434 ) { | |
1435 $newToken['code'] = T_OPEN_USE_GROUP; | |
1436 $newToken['type'] = 'T_OPEN_USE_GROUP'; | |
1437 $insideUseGroup = true; | |
1438 } | |
1439 | |
1440 if ($insideUseGroup === true && $newToken['code'] === T_CLOSE_CURLY_BRACKET) { | |
1441 $newToken['code'] = T_CLOSE_USE_GROUP; | |
1442 $newToken['type'] = 'T_CLOSE_USE_GROUP'; | |
1443 $insideUseGroup = false; | |
1444 } | |
1445 | |
1446 $finalTokens[$newStackPtr] = $newToken; | |
1447 $newStackPtr++; | |
1448 }//end if | |
1449 }//end for | |
1450 | |
1451 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1452 echo "\t*** END PHP TOKENIZING ***".PHP_EOL; | |
1453 } | |
1454 | |
1455 return $finalTokens; | |
1456 | |
1457 }//end tokenize() | |
1458 | |
1459 | |
1460 /** | |
1461 * Performs additional processing after main tokenizing. | |
1462 * | |
1463 * This additional processing checks for CASE statements that are using curly | |
1464 * braces for scope openers and closers. It also turns some T_FUNCTION tokens | |
1465 * into T_CLOSURE when they are not standard function definitions. It also | |
1466 * detects short array syntax and converts those square brackets into new tokens. | |
1467 * It also corrects some usage of the static and class keywords. It also | |
1468 * assigns tokens to function return types. | |
1469 * | |
1470 * @return void | |
1471 */ | |
1472 protected function processAdditional() | |
1473 { | |
1474 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1475 echo "\t*** START ADDITIONAL PHP PROCESSING ***".PHP_EOL; | |
1476 } | |
1477 | |
1478 $numTokens = count($this->tokens); | |
1479 for ($i = ($numTokens - 1); $i >= 0; $i--) { | |
1480 // Check for any unset scope conditions due to alternate IF/ENDIF syntax. | |
1481 if (isset($this->tokens[$i]['scope_opener']) === true | |
1482 && isset($this->tokens[$i]['scope_condition']) === false | |
1483 ) { | |
1484 $this->tokens[$i]['scope_condition'] = $this->tokens[$this->tokens[$i]['scope_opener']]['scope_condition']; | |
1485 } | |
1486 | |
1487 if ($this->tokens[$i]['code'] === T_FUNCTION) { | |
1488 /* | |
1489 Detect functions that are actually closures and | |
1490 assign them a different token. | |
1491 */ | |
1492 | |
1493 if (isset($this->tokens[$i]['scope_opener']) === true) { | |
1494 for ($x = ($i + 1); $x < $numTokens; $x++) { | |
1495 if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false | |
1496 && $this->tokens[$x]['code'] !== T_BITWISE_AND | |
1497 ) { | |
1498 break; | |
1499 } | |
1500 } | |
1501 | |
1502 if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) { | |
1503 $this->tokens[$i]['code'] = T_CLOSURE; | |
1504 $this->tokens[$i]['type'] = 'T_CLOSURE'; | |
1505 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1506 $line = $this->tokens[$i]['line']; | |
1507 echo "\t* token $i on line $line changed from T_FUNCTION to T_CLOSURE".PHP_EOL; | |
1508 } | |
1509 | |
1510 for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) { | |
1511 if (isset($this->tokens[$x]['conditions'][$i]) === false) { | |
1512 continue; | |
1513 } | |
1514 | |
1515 $this->tokens[$x]['conditions'][$i] = T_CLOSURE; | |
1516 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1517 $type = $this->tokens[$x]['type']; | |
1518 echo "\t\t* cleaned $x ($type) *".PHP_EOL; | |
1519 } | |
1520 } | |
1521 } | |
1522 }//end if | |
1523 | |
1524 continue; | |
1525 } else if ($this->tokens[$i]['code'] === T_CLASS && isset($this->tokens[$i]['scope_opener']) === true) { | |
1526 /* | |
1527 Detect anonymous classes and assign them a different token. | |
1528 */ | |
1529 | |
1530 for ($x = ($i + 1); $x < $numTokens; $x++) { | |
1531 if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) { | |
1532 break; | |
1533 } | |
1534 } | |
1535 | |
1536 if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS | |
1537 || $this->tokens[$x]['code'] === T_OPEN_CURLY_BRACKET | |
1538 || $this->tokens[$x]['code'] === T_EXTENDS | |
1539 || $this->tokens[$x]['code'] === T_IMPLEMENTS | |
1540 ) { | |
1541 $this->tokens[$i]['code'] = T_ANON_CLASS; | |
1542 $this->tokens[$i]['type'] = 'T_ANON_CLASS'; | |
1543 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1544 $line = $this->tokens[$i]['line']; | |
1545 echo "\t* token $i on line $line changed from T_CLASS to T_ANON_CLASS".PHP_EOL; | |
1546 } | |
1547 | |
1548 for ($x = ($this->tokens[$i]['scope_opener'] + 1); $x < $this->tokens[$i]['scope_closer']; $x++) { | |
1549 if (isset($this->tokens[$x]['conditions'][$i]) === false) { | |
1550 continue; | |
1551 } | |
1552 | |
1553 $this->tokens[$x]['conditions'][$i] = T_ANON_CLASS; | |
1554 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1555 $type = $this->tokens[$x]['type']; | |
1556 echo "\t\t* cleaned $x ($type) *".PHP_EOL; | |
1557 } | |
1558 } | |
1559 } | |
1560 | |
1561 continue; | |
1562 } else if ($this->tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET) { | |
1563 if (isset($this->tokens[$i]['bracket_closer']) === false) { | |
1564 continue; | |
1565 } | |
1566 | |
1567 // Unless there is a variable or a bracket before this token, | |
1568 // it is the start of an array being defined using the short syntax. | |
1569 $isShortArray = false; | |
1570 $allowed = [ | |
1571 T_CLOSE_SQUARE_BRACKET => T_CLOSE_SQUARE_BRACKET, | |
1572 T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, | |
1573 T_CLOSE_PARENTHESIS => T_CLOSE_PARENTHESIS, | |
1574 T_VARIABLE => T_VARIABLE, | |
1575 T_OBJECT_OPERATOR => T_OBJECT_OPERATOR, | |
1576 T_STRING => T_STRING, | |
1577 T_CONSTANT_ENCAPSED_STRING => T_CONSTANT_ENCAPSED_STRING, | |
1578 ]; | |
1579 | |
1580 for ($x = ($i - 1); $x >= 0; $x--) { | |
1581 // If we hit a scope opener, the statement has ended | |
1582 // without finding anything, so it's probably an array | |
1583 // using PHP 7.1 short list syntax. | |
1584 if (isset($this->tokens[$x]['scope_opener']) === true) { | |
1585 $isShortArray = true; | |
1586 break; | |
1587 } | |
1588 | |
1589 if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) { | |
1590 if (isset($allowed[$this->tokens[$x]['code']]) === false) { | |
1591 $isShortArray = true; | |
1592 } | |
1593 | |
1594 break; | |
1595 } | |
1596 } | |
1597 | |
1598 if ($isShortArray === true) { | |
1599 $this->tokens[$i]['code'] = T_OPEN_SHORT_ARRAY; | |
1600 $this->tokens[$i]['type'] = 'T_OPEN_SHORT_ARRAY'; | |
1601 | |
1602 $closer = $this->tokens[$i]['bracket_closer']; | |
1603 $this->tokens[$closer]['code'] = T_CLOSE_SHORT_ARRAY; | |
1604 $this->tokens[$closer]['type'] = 'T_CLOSE_SHORT_ARRAY'; | |
1605 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1606 $line = $this->tokens[$i]['line']; | |
1607 echo "\t* token $i on line $line changed from T_OPEN_SQUARE_BRACKET to T_OPEN_SHORT_ARRAY".PHP_EOL; | |
1608 $line = $this->tokens[$closer]['line']; | |
1609 echo "\t* token $closer on line $line changed from T_CLOSE_SQUARE_BRACKET to T_CLOSE_SHORT_ARRAY".PHP_EOL; | |
1610 } | |
1611 } | |
1612 | |
1613 continue; | |
1614 } else if ($this->tokens[$i]['code'] === T_STATIC) { | |
1615 for ($x = ($i - 1); $x > 0; $x--) { | |
1616 if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) { | |
1617 break; | |
1618 } | |
1619 } | |
1620 | |
1621 if ($this->tokens[$x]['code'] === T_INSTANCEOF) { | |
1622 $this->tokens[$i]['code'] = T_STRING; | |
1623 $this->tokens[$i]['type'] = 'T_STRING'; | |
1624 | |
1625 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1626 $line = $this->tokens[$i]['line']; | |
1627 echo "\t* token $i on line $line changed from T_STATIC to T_STRING".PHP_EOL; | |
1628 } | |
1629 } | |
1630 | |
1631 continue; | |
1632 } else if ($this->tokens[$i]['code'] === T_TRUE | |
1633 || $this->tokens[$i]['code'] === T_FALSE | |
1634 || $this->tokens[$i]['code'] === T_NULL | |
1635 ) { | |
1636 for ($x = ($i + 1); $i < $numTokens; $x++) { | |
1637 if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) { | |
1638 // Non-whitespace content. | |
1639 break; | |
1640 } | |
1641 } | |
1642 | |
1643 $context = [ | |
1644 T_OBJECT_OPERATOR => true, | |
1645 T_NS_SEPARATOR => true, | |
1646 T_PAAMAYIM_NEKUDOTAYIM => true, | |
1647 ]; | |
1648 if (isset($context[$this->tokens[$x]['code']]) === true) { | |
1649 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1650 $line = $this->tokens[$i]['line']; | |
1651 $type = $this->tokens[$i]['type']; | |
1652 echo "\t* token $i on line $line changed from $type to T_STRING".PHP_EOL; | |
1653 } | |
1654 | |
1655 $this->tokens[$i]['code'] = T_STRING; | |
1656 $this->tokens[$i]['type'] = 'T_STRING'; | |
1657 } | |
1658 } else if ($this->tokens[$i]['code'] === T_CONST) { | |
1659 // Context sensitive keywords support. | |
1660 for ($x = ($i + 1); $i < $numTokens; $x++) { | |
1661 if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) { | |
1662 // Non-whitespace content. | |
1663 break; | |
1664 } | |
1665 } | |
1666 | |
1667 if ($this->tokens[$x]['code'] !== T_STRING) { | |
1668 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1669 $line = $this->tokens[$x]['line']; | |
1670 $type = $this->tokens[$x]['type']; | |
1671 echo "\t* token $x on line $line changed from $type to T_STRING".PHP_EOL; | |
1672 } | |
1673 | |
1674 $this->tokens[$x]['code'] = T_STRING; | |
1675 $this->tokens[$x]['type'] = 'T_STRING'; | |
1676 } | |
1677 }//end if | |
1678 | |
1679 if (($this->tokens[$i]['code'] !== T_CASE | |
1680 && $this->tokens[$i]['code'] !== T_DEFAULT) | |
1681 || isset($this->tokens[$i]['scope_opener']) === false | |
1682 ) { | |
1683 // Only interested in CASE and DEFAULT statements from here on in. | |
1684 continue; | |
1685 } | |
1686 | |
1687 $scopeOpener = $this->tokens[$i]['scope_opener']; | |
1688 $scopeCloser = $this->tokens[$i]['scope_closer']; | |
1689 | |
1690 // If the first char after the opener is a curly brace | |
1691 // and that brace has been ignored, it is actually | |
1692 // opening this case statement and the opener and closer are | |
1693 // probably set incorrectly. | |
1694 for ($x = ($scopeOpener + 1); $x < $numTokens; $x++) { | |
1695 if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) { | |
1696 // Non-whitespace content. | |
1697 break; | |
1698 } | |
1699 } | |
1700 | |
1701 if ($this->tokens[$x]['code'] === T_CASE || $this->tokens[$x]['code'] === T_DEFAULT) { | |
1702 // Special case for multiple CASE statements that share the same | |
1703 // closer. Because we are going backwards through the file, this next | |
1704 // CASE statement is already fixed, so just use its closer and don't | |
1705 // worry about fixing anything. | |
1706 $newCloser = $this->tokens[$x]['scope_closer']; | |
1707 $this->tokens[$i]['scope_closer'] = $newCloser; | |
1708 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1709 $oldType = $this->tokens[$scopeCloser]['type']; | |
1710 $newType = $this->tokens[$newCloser]['type']; | |
1711 $line = $this->tokens[$i]['line']; | |
1712 echo "\t* token $i (T_CASE) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL; | |
1713 } | |
1714 | |
1715 continue; | |
1716 } | |
1717 | |
1718 if ($this->tokens[$x]['code'] !== T_OPEN_CURLY_BRACKET | |
1719 || isset($this->tokens[$x]['scope_condition']) === true | |
1720 ) { | |
1721 // Not a CASE/DEFAULT with a curly brace opener. | |
1722 continue; | |
1723 } | |
1724 | |
1725 // The closer for this CASE/DEFAULT should be the closing curly brace and | |
1726 // not whatever it already is. The opener needs to be the opening curly | |
1727 // brace so everything matches up. | |
1728 $newCloser = $this->tokens[$x]['bracket_closer']; | |
1729 foreach ([$i, $x, $newCloser] as $index) { | |
1730 $this->tokens[$index]['scope_condition'] = $i; | |
1731 $this->tokens[$index]['scope_opener'] = $x; | |
1732 $this->tokens[$index]['scope_closer'] = $newCloser; | |
1733 } | |
1734 | |
1735 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1736 $line = $this->tokens[$i]['line']; | |
1737 $tokenType = $this->tokens[$i]['type']; | |
1738 | |
1739 $oldType = $this->tokens[$scopeOpener]['type']; | |
1740 $newType = $this->tokens[$x]['type']; | |
1741 echo "\t* token $i ($tokenType) on line $line opener changed from $scopeOpener ($oldType) to $x ($newType)".PHP_EOL; | |
1742 | |
1743 $oldType = $this->tokens[$scopeCloser]['type']; | |
1744 $newType = $this->tokens[$newCloser]['type']; | |
1745 echo "\t* token $i ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL; | |
1746 } | |
1747 | |
1748 if ($this->tokens[$scopeOpener]['scope_condition'] === $i) { | |
1749 unset($this->tokens[$scopeOpener]['scope_condition']); | |
1750 unset($this->tokens[$scopeOpener]['scope_opener']); | |
1751 unset($this->tokens[$scopeOpener]['scope_closer']); | |
1752 } | |
1753 | |
1754 if ($this->tokens[$scopeCloser]['scope_condition'] === $i) { | |
1755 unset($this->tokens[$scopeCloser]['scope_condition']); | |
1756 unset($this->tokens[$scopeCloser]['scope_opener']); | |
1757 unset($this->tokens[$scopeCloser]['scope_closer']); | |
1758 } else { | |
1759 // We were using a shared closer. All tokens that were | |
1760 // sharing this closer with us, except for the scope condition | |
1761 // and it's opener, need to now point to the new closer. | |
1762 $condition = $this->tokens[$scopeCloser]['scope_condition']; | |
1763 $start = ($this->tokens[$condition]['scope_opener'] + 1); | |
1764 for ($y = $start; $y < $scopeCloser; $y++) { | |
1765 if (isset($this->tokens[$y]['scope_closer']) === true | |
1766 && $this->tokens[$y]['scope_closer'] === $scopeCloser | |
1767 ) { | |
1768 $this->tokens[$y]['scope_closer'] = $newCloser; | |
1769 | |
1770 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1771 $line = $this->tokens[$y]['line']; | |
1772 $tokenType = $this->tokens[$y]['type']; | |
1773 $oldType = $this->tokens[$scopeCloser]['type']; | |
1774 $newType = $this->tokens[$newCloser]['type']; | |
1775 echo "\t\t* token $y ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL; | |
1776 } | |
1777 } | |
1778 } | |
1779 }//end if | |
1780 | |
1781 unset($this->tokens[$x]['bracket_opener']); | |
1782 unset($this->tokens[$x]['bracket_closer']); | |
1783 unset($this->tokens[$newCloser]['bracket_opener']); | |
1784 unset($this->tokens[$newCloser]['bracket_closer']); | |
1785 $this->tokens[$scopeCloser]['conditions'][] = $i; | |
1786 | |
1787 // Now fix up all the tokens that think they are | |
1788 // inside the CASE/DEFAULT statement when they are really outside. | |
1789 for ($x = $newCloser; $x < $scopeCloser; $x++) { | |
1790 foreach ($this->tokens[$x]['conditions'] as $num => $oldCond) { | |
1791 if ($oldCond === $this->tokens[$i]['code']) { | |
1792 $oldConditions = $this->tokens[$x]['conditions']; | |
1793 unset($this->tokens[$x]['conditions'][$num]); | |
1794 | |
1795 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1796 $type = $this->tokens[$x]['type']; | |
1797 $oldConds = ''; | |
1798 foreach ($oldConditions as $condition) { | |
1799 $oldConds .= Util\Tokens::tokenName($condition).','; | |
1800 } | |
1801 | |
1802 $oldConds = rtrim($oldConds, ','); | |
1803 | |
1804 $newConds = ''; | |
1805 foreach ($this->tokens[$x]['conditions'] as $condition) { | |
1806 $newConds .= Util\Tokens::tokenName($condition).','; | |
1807 } | |
1808 | |
1809 $newConds = rtrim($newConds, ','); | |
1810 | |
1811 echo "\t\t* cleaned $x ($type) *".PHP_EOL; | |
1812 echo "\t\t\t=> conditions changed from $oldConds to $newConds".PHP_EOL; | |
1813 } | |
1814 | |
1815 break; | |
1816 }//end if | |
1817 }//end foreach | |
1818 }//end for | |
1819 }//end for | |
1820 | |
1821 if (PHP_CODESNIFFER_VERBOSITY > 1) { | |
1822 echo "\t*** END ADDITIONAL PHP PROCESSING ***".PHP_EOL; | |
1823 } | |
1824 | |
1825 }//end processAdditional() | |
1826 | |
1827 | |
1828 /** | |
1829 * Takes a token produced from <code>token_get_all()</code> and produces a | |
1830 * more uniform token. | |
1831 * | |
1832 * @param string|array $token The token to convert. | |
1833 * | |
1834 * @return array The new token. | |
1835 */ | |
1836 public static function standardiseToken($token) | |
1837 { | |
1838 if (isset($token[1]) === false) { | |
1839 if (isset(self::$resolveTokenCache[$token[0]]) === true) { | |
1840 return self::$resolveTokenCache[$token[0]]; | |
1841 } | |
1842 } else { | |
1843 $cacheKey = null; | |
1844 if ($token[0] === T_STRING) { | |
1845 $cacheKey = strtolower($token[1]); | |
1846 } else if ($token[0] !== T_CURLY_OPEN) { | |
1847 $cacheKey = $token[0]; | |
1848 } | |
1849 | |
1850 if ($cacheKey !== null && isset(self::$resolveTokenCache[$cacheKey]) === true) { | |
1851 $newToken = self::$resolveTokenCache[$cacheKey]; | |
1852 $newToken['content'] = $token[1]; | |
1853 return $newToken; | |
1854 } | |
1855 } | |
1856 | |
1857 if (isset($token[1]) === false) { | |
1858 return self::resolveSimpleToken($token[0]); | |
1859 } | |
1860 | |
1861 if ($token[0] === T_STRING) { | |
1862 switch ($cacheKey) { | |
1863 case 'false': | |
1864 $newToken['type'] = 'T_FALSE'; | |
1865 break; | |
1866 case 'true': | |
1867 $newToken['type'] = 'T_TRUE'; | |
1868 break; | |
1869 case 'null': | |
1870 $newToken['type'] = 'T_NULL'; | |
1871 break; | |
1872 case 'self': | |
1873 $newToken['type'] = 'T_SELF'; | |
1874 break; | |
1875 case 'parent': | |
1876 $newToken['type'] = 'T_PARENT'; | |
1877 break; | |
1878 default: | |
1879 $newToken['type'] = 'T_STRING'; | |
1880 break; | |
1881 } | |
1882 | |
1883 $newToken['code'] = constant($newToken['type']); | |
1884 | |
1885 self::$resolveTokenCache[$cacheKey] = $newToken; | |
1886 } else if ($token[0] === T_CURLY_OPEN) { | |
1887 $newToken = [ | |
1888 'code' => T_OPEN_CURLY_BRACKET, | |
1889 'type' => 'T_OPEN_CURLY_BRACKET', | |
1890 ]; | |
1891 } else { | |
1892 $newToken = [ | |
1893 'code' => $token[0], | |
1894 'type' => Util\Tokens::tokenName($token[0]), | |
1895 ]; | |
1896 | |
1897 self::$resolveTokenCache[$token[0]] = $newToken; | |
1898 }//end if | |
1899 | |
1900 $newToken['content'] = $token[1]; | |
1901 return $newToken; | |
1902 | |
1903 }//end standardiseToken() | |
1904 | |
1905 | |
1906 /** | |
1907 * Converts simple tokens into a format that conforms to complex tokens | |
1908 * produced by token_get_all(). | |
1909 * | |
1910 * Simple tokens are tokens that are not in array form when produced from | |
1911 * token_get_all(). | |
1912 * | |
1913 * @param string $token The simple token to convert. | |
1914 * | |
1915 * @return array The new token in array format. | |
1916 */ | |
1917 public static function resolveSimpleToken($token) | |
1918 { | |
1919 $newToken = []; | |
1920 | |
1921 switch ($token) { | |
1922 case '{': | |
1923 $newToken['type'] = 'T_OPEN_CURLY_BRACKET'; | |
1924 break; | |
1925 case '}': | |
1926 $newToken['type'] = 'T_CLOSE_CURLY_BRACKET'; | |
1927 break; | |
1928 case '[': | |
1929 $newToken['type'] = 'T_OPEN_SQUARE_BRACKET'; | |
1930 break; | |
1931 case ']': | |
1932 $newToken['type'] = 'T_CLOSE_SQUARE_BRACKET'; | |
1933 break; | |
1934 case '(': | |
1935 $newToken['type'] = 'T_OPEN_PARENTHESIS'; | |
1936 break; | |
1937 case ')': | |
1938 $newToken['type'] = 'T_CLOSE_PARENTHESIS'; | |
1939 break; | |
1940 case ':': | |
1941 $newToken['type'] = 'T_COLON'; | |
1942 break; | |
1943 case '.': | |
1944 $newToken['type'] = 'T_STRING_CONCAT'; | |
1945 break; | |
1946 case ';': | |
1947 $newToken['type'] = 'T_SEMICOLON'; | |
1948 break; | |
1949 case '=': | |
1950 $newToken['type'] = 'T_EQUAL'; | |
1951 break; | |
1952 case '*': | |
1953 $newToken['type'] = 'T_MULTIPLY'; | |
1954 break; | |
1955 case '/': | |
1956 $newToken['type'] = 'T_DIVIDE'; | |
1957 break; | |
1958 case '+': | |
1959 $newToken['type'] = 'T_PLUS'; | |
1960 break; | |
1961 case '-': | |
1962 $newToken['type'] = 'T_MINUS'; | |
1963 break; | |
1964 case '%': | |
1965 $newToken['type'] = 'T_MODULUS'; | |
1966 break; | |
1967 case '^': | |
1968 $newToken['type'] = 'T_BITWISE_XOR'; | |
1969 break; | |
1970 case '&': | |
1971 $newToken['type'] = 'T_BITWISE_AND'; | |
1972 break; | |
1973 case '|': | |
1974 $newToken['type'] = 'T_BITWISE_OR'; | |
1975 break; | |
1976 case '~': | |
1977 $newToken['type'] = 'T_BITWISE_NOT'; | |
1978 break; | |
1979 case '<': | |
1980 $newToken['type'] = 'T_LESS_THAN'; | |
1981 break; | |
1982 case '>': | |
1983 $newToken['type'] = 'T_GREATER_THAN'; | |
1984 break; | |
1985 case '!': | |
1986 $newToken['type'] = 'T_BOOLEAN_NOT'; | |
1987 break; | |
1988 case ',': | |
1989 $newToken['type'] = 'T_COMMA'; | |
1990 break; | |
1991 case '@': | |
1992 $newToken['type'] = 'T_ASPERAND'; | |
1993 break; | |
1994 case '$': | |
1995 $newToken['type'] = 'T_DOLLAR'; | |
1996 break; | |
1997 case '`': | |
1998 $newToken['type'] = 'T_BACKTICK'; | |
1999 break; | |
2000 default: | |
2001 $newToken['type'] = 'T_NONE'; | |
2002 break; | |
2003 }//end switch | |
2004 | |
2005 $newToken['code'] = constant($newToken['type']); | |
2006 $newToken['content'] = $token; | |
2007 | |
2008 self::$resolveTokenCache[$token] = $newToken; | |
2009 return $newToken; | |
2010 | |
2011 }//end resolveSimpleToken() | |
2012 | |
2013 | |
2014 }//end class |