annotate vendor/squizlabs/php_codesniffer/CodeSniffer/Tokenizers/PHP.php @ 12:7a779792577d

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