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