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