annotate vendor/squizlabs/php_codesniffer/CodeSniffer/Tokenizers/PHP.php @ 2:92f882872392

Trusted hosts, + remove migration modules
author Chris Cannam
date Tue, 05 Dec 2017 09:26:43 +0000
parents 4c8ae668cc8c
children 7a779792577d
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@0 725 for ($i = ($stackPtr - 1); $i >= 0; $i--) {
Chris@0 726 if (is_array($tokens[$i]) === true) {
Chris@0 727 $tokenType = $tokens[$i][0];
Chris@0 728 } else {
Chris@0 729 $tokenType = $tokens[$i];
Chris@0 730 }
Chris@0 731
Chris@0 732 if ($tokenType === T_FUNCTION) {
Chris@0 733 $newToken['code'] = T_NULLABLE;
Chris@0 734 $newToken['type'] = 'T_NULLABLE';
Chris@0 735 break;
Chris@0 736 } else if (in_array($tokenType, array(T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, '{', ';')) === true) {
Chris@0 737 $newToken['code'] = T_INLINE_THEN;
Chris@0 738 $newToken['type'] = 'T_INLINE_THEN';
Chris@0 739
Chris@0 740 $insideInlineIf[] = $stackPtr;
Chris@0 741 break;
Chris@0 742 }
Chris@0 743 }
Chris@0 744
Chris@0 745 $finalTokens[$newStackPtr] = $newToken;
Chris@0 746 $newStackPtr++;
Chris@0 747 continue;
Chris@0 748 }//end if
Chris@0 749
Chris@0 750 /*
Chris@0 751 Tokens after a double colon may be look like scope openers,
Chris@0 752 such as when writing code like Foo::NAMESPACE, but they are
Chris@0 753 only ever variables or strings.
Chris@0 754 */
Chris@0 755
Chris@0 756 if ($stackPtr > 1
Chris@0 757 && (is_array($tokens[($stackPtr - 1)]) === true
Chris@0 758 && $tokens[($stackPtr - 1)][0] === T_PAAMAYIM_NEKUDOTAYIM)
Chris@0 759 && $tokenIsArray === true
Chris@0 760 && $token[0] !== T_STRING
Chris@0 761 && $token[0] !== T_VARIABLE
Chris@0 762 && $token[0] !== T_DOLLAR
Chris@0 763 && isset(PHP_CodeSniffer_Tokens::$emptyTokens[$token[0]]) === false
Chris@0 764 ) {
Chris@0 765 $newToken = array();
Chris@0 766 $newToken['code'] = T_STRING;
Chris@0 767 $newToken['type'] = 'T_STRING';
Chris@0 768 $newToken['content'] = $token[1];
Chris@0 769 $finalTokens[$newStackPtr] = $newToken;
Chris@0 770
Chris@0 771 $newStackPtr++;
Chris@0 772 continue;
Chris@0 773 }
Chris@0 774
Chris@0 775 /*
Chris@0 776 The string-like token after a function keyword should always be
Chris@0 777 tokenized as T_STRING even if it appears to be a different token,
Chris@0 778 such as when writing code like: function default(): foo
Chris@0 779 so go forward and change the token type before it is processed.
Chris@0 780 */
Chris@0 781
Chris@0 782 if ($tokenIsArray === true && $token[0] === T_FUNCTION) {
Chris@0 783 for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
Chris@0 784 if (is_array($tokens[$x]) === false
Chris@0 785 || isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x][0]]) === false
Chris@0 786 ) {
Chris@0 787 // Non-empty content.
Chris@0 788 break;
Chris@0 789 }
Chris@0 790 }
Chris@0 791
Chris@0 792 if ($x < $numTokens && is_array($tokens[$x]) === true) {
Chris@0 793 $tokens[$x][0] = T_STRING;
Chris@0 794 }
Chris@0 795 }
Chris@0 796
Chris@0 797 /*
Chris@0 798 Before PHP 7, the <=> operator was tokenized as
Chris@0 799 T_IS_SMALLER_OR_EQUAL followed by T_GREATER_THAN.
Chris@0 800 So look for and combine these tokens in earlier versions.
Chris@0 801 */
Chris@0 802
Chris@0 803 if ($tokenIsArray === true
Chris@0 804 && $token[0] === T_IS_SMALLER_OR_EQUAL
Chris@0 805 && isset($tokens[($stackPtr + 1)]) === true
Chris@0 806 && $tokens[($stackPtr + 1)][0] === '>'
Chris@0 807 ) {
Chris@0 808 $newToken = array();
Chris@0 809 $newToken['code'] = T_SPACESHIP;
Chris@0 810 $newToken['type'] = 'T_SPACESHIP';
Chris@0 811 $newToken['content'] = '<=>';
Chris@0 812 $finalTokens[$newStackPtr] = $newToken;
Chris@0 813
Chris@0 814 $newStackPtr++;
Chris@0 815 $stackPtr++;
Chris@0 816 continue;
Chris@0 817 }
Chris@0 818
Chris@0 819 /*
Chris@0 820 Emulate traits in PHP versions less than 5.4.
Chris@0 821 */
Chris@0 822
Chris@0 823 if ($tokenIsArray === true
Chris@0 824 && $token[0] === T_STRING
Chris@0 825 && strtolower($token[1]) === 'trait'
Chris@0 826 && $tokens[($stackPtr - 1)][0] !== T_OBJECT_OPERATOR
Chris@0 827 && $tokens[($stackPtr - 1)][0] !== T_PAAMAYIM_NEKUDOTAYIM
Chris@0 828 ) {
Chris@0 829 $finalTokens[$newStackPtr] = array(
Chris@0 830 'content' => $token[1],
Chris@0 831 'code' => T_TRAIT,
Chris@0 832 'type' => 'T_TRAIT',
Chris@0 833 );
Chris@0 834
Chris@0 835 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 836 echo "\t\t* token $stackPtr changed from T_STRING to T_TRAIT".PHP_EOL;
Chris@0 837 }
Chris@0 838
Chris@0 839 $newStackPtr++;
Chris@0 840 continue;
Chris@0 841 }
Chris@0 842
Chris@0 843 /*
Chris@0 844 PHP doesn't assign a token to goto labels, so we have to.
Chris@0 845 These are just string tokens with a single colon after them. Double
Chris@0 846 colons are already tokenized and so don't interfere with this check.
Chris@0 847 But we do have to account for CASE statements, that look just like
Chris@0 848 goto labels.
Chris@0 849 */
Chris@0 850
Chris@0 851 if ($tokenIsArray === true
Chris@0 852 && $token[0] === T_STRING
Chris@0 853 && isset($tokens[($stackPtr + 1)]) === true
Chris@0 854 && $tokens[($stackPtr + 1)] === ':'
Chris@0 855 && $tokens[($stackPtr - 1)][0] !== T_PAAMAYIM_NEKUDOTAYIM
Chris@0 856 ) {
Chris@0 857 $stopTokens = array(
Chris@0 858 T_CASE => true,
Chris@0 859 T_SEMICOLON => true,
Chris@0 860 T_OPEN_CURLY_BRACKET => true,
Chris@0 861 T_INLINE_THEN => true,
Chris@0 862 );
Chris@0 863
Chris@0 864 for ($x = ($newStackPtr - 1); $x > 0; $x--) {
Chris@0 865 if (isset($stopTokens[$finalTokens[$x]['code']]) === true) {
Chris@0 866 break;
Chris@0 867 }
Chris@0 868 }
Chris@0 869
Chris@0 870 if ($finalTokens[$x]['code'] !== T_CASE
Chris@0 871 && $finalTokens[$x]['code'] !== T_INLINE_THEN
Chris@0 872 ) {
Chris@0 873 $finalTokens[$newStackPtr] = array(
Chris@0 874 'content' => $token[1].':',
Chris@0 875 'code' => T_GOTO_LABEL,
Chris@0 876 'type' => 'T_GOTO_LABEL',
Chris@0 877 );
Chris@0 878
Chris@0 879 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 880 echo "\t\t* token $stackPtr changed from T_STRING to T_GOTO_LABEL".PHP_EOL;
Chris@0 881 echo "\t\t* skipping T_COLON token ".($stackPtr + 1).PHP_EOL;
Chris@0 882 }
Chris@0 883
Chris@0 884 $newStackPtr++;
Chris@0 885 $stackPtr++;
Chris@0 886 continue;
Chris@0 887 }
Chris@0 888 }//end if
Chris@0 889
Chris@0 890 /*
Chris@0 891 HHVM 3.5 tokenizes "else[\s]+if" as a T_ELSEIF token while PHP
Chris@0 892 proper only tokenizes "elseif" as a T_ELSEIF token. So split
Chris@0 893 up the HHVM token to make it looks like proper PHP.
Chris@0 894 */
Chris@0 895
Chris@0 896 if ($tokenIsArray === true
Chris@0 897 && $token[0] === T_ELSEIF
Chris@0 898 && strtolower($token[1]) !== 'elseif'
Chris@0 899 ) {
Chris@0 900 $finalTokens[$newStackPtr] = array(
Chris@0 901 'content' => substr($token[1], 0, 4),
Chris@0 902 'code' => T_ELSE,
Chris@0 903 'type' => 'T_ELSE',
Chris@0 904 );
Chris@0 905
Chris@0 906 $newStackPtr++;
Chris@0 907 $finalTokens[$newStackPtr] = array(
Chris@0 908 'content' => substr($token[1], 4, -2),
Chris@0 909 'code' => T_WHITESPACE,
Chris@0 910 'type' => 'T_WHITESPACE',
Chris@0 911 );
Chris@0 912
Chris@0 913 $newStackPtr++;
Chris@0 914 $finalTokens[$newStackPtr] = array(
Chris@0 915 'content' => substr($token[1], -2),
Chris@0 916 'code' => T_IF,
Chris@0 917 'type' => 'T_IF',
Chris@0 918 );
Chris@0 919
Chris@0 920 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 921 echo "\t\t* token $stackPtr changed from T_ELSEIF to T_ELSE/T_WHITESPACE/T_IF".PHP_EOL;
Chris@0 922 }
Chris@0 923
Chris@0 924 $newStackPtr++;
Chris@0 925 continue;
Chris@0 926 }//end if
Chris@0 927
Chris@0 928 /*
Chris@0 929 HHVM 3.5 and 3.6 tokenizes a hashbang line such as #!/usr/bin/php
Chris@0 930 as T_HASHANG while PHP proper uses T_INLINE_HTML.
Chris@0 931 */
Chris@0 932
Chris@0 933 if ($tokenIsArray === true && token_name($token[0]) === 'T_HASHBANG') {
Chris@0 934 $finalTokens[$newStackPtr] = array(
Chris@0 935 'content' => $token[1],
Chris@0 936 'code' => T_INLINE_HTML,
Chris@0 937 'type' => 'T_INLINE_HTML',
Chris@0 938 );
Chris@0 939
Chris@0 940 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 941 echo "\t\t* token $stackPtr changed from T_HASHBANG to T_INLINE_HTML".PHP_EOL;
Chris@0 942 }
Chris@0 943
Chris@0 944 $newStackPtr++;
Chris@0 945 continue;
Chris@0 946 }//end if
Chris@0 947
Chris@0 948 /*
Chris@0 949 If this token has newlines in its content, split each line up
Chris@0 950 and create a new token for each line. We do this so it's easier
Chris@0 951 to ascertain where errors occur on a line.
Chris@0 952 Note that $token[1] is the token's content.
Chris@0 953 */
Chris@0 954
Chris@0 955 if ($tokenIsArray === true && strpos($token[1], $eolChar) !== false) {
Chris@0 956 $tokenLines = explode($eolChar, $token[1]);
Chris@0 957 $numLines = count($tokenLines);
Chris@0 958 $newToken = array(
Chris@0 959 'type' => token_name($token[0]),
Chris@0 960 'code' => $token[0],
Chris@0 961 'content' => '',
Chris@0 962 );
Chris@0 963
Chris@0 964 for ($i = 0; $i < $numLines; $i++) {
Chris@0 965 $newToken['content'] = $tokenLines[$i];
Chris@0 966 if ($i === ($numLines - 1)) {
Chris@0 967 if ($tokenLines[$i] === '') {
Chris@0 968 break;
Chris@0 969 }
Chris@0 970 } else {
Chris@0 971 $newToken['content'] .= $eolChar;
Chris@0 972 }
Chris@0 973
Chris@0 974 $finalTokens[$newStackPtr] = $newToken;
Chris@0 975 $newStackPtr++;
Chris@0 976 }
Chris@0 977 } else {
Chris@0 978 if ($tokenIsArray === true && $token[0] === T_STRING) {
Chris@0 979 // Some T_STRING tokens should remain that way
Chris@0 980 // due to their context.
Chris@0 981 $context = array(
Chris@0 982 T_OBJECT_OPERATOR => true,
Chris@0 983 T_FUNCTION => true,
Chris@0 984 T_CLASS => true,
Chris@0 985 T_EXTENDS => true,
Chris@0 986 T_IMPLEMENTS => true,
Chris@0 987 T_NEW => true,
Chris@0 988 T_CONST => true,
Chris@0 989 T_NS_SEPARATOR => true,
Chris@0 990 T_USE => true,
Chris@0 991 T_NAMESPACE => true,
Chris@0 992 T_PAAMAYIM_NEKUDOTAYIM => true,
Chris@0 993 );
Chris@0 994 if (isset($context[$finalTokens[$lastNotEmptyToken]['code']]) === true) {
Chris@0 995 // Special case for syntax like: return new self
Chris@0 996 // where self should not be a string.
Chris@0 997 if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW
Chris@0 998 && strtolower($token[1]) === 'self'
Chris@0 999 ) {
Chris@0 1000 $finalTokens[$newStackPtr] = array(
Chris@0 1001 'content' => $token[1],
Chris@0 1002 'code' => T_SELF,
Chris@0 1003 'type' => 'T_SELF',
Chris@0 1004 );
Chris@0 1005 } else {
Chris@0 1006 $finalTokens[$newStackPtr] = array(
Chris@0 1007 'content' => $token[1],
Chris@0 1008 'code' => T_STRING,
Chris@0 1009 'type' => 'T_STRING',
Chris@0 1010 );
Chris@0 1011 }
Chris@0 1012
Chris@0 1013 $newStackPtr++;
Chris@0 1014 continue;
Chris@0 1015 }//end if
Chris@0 1016 }//end if
Chris@0 1017
Chris@0 1018 $newToken = null;
Chris@0 1019 if ($tokenIsArray === false) {
Chris@0 1020 if (isset(self::$_resolveTokenCache[$token[0]]) === true) {
Chris@0 1021 $newToken = self::$_resolveTokenCache[$token[0]];
Chris@0 1022 }
Chris@0 1023 } else {
Chris@0 1024 $cacheKey = null;
Chris@0 1025 if ($token[0] === T_STRING) {
Chris@0 1026 $cacheKey = strtolower($token[1]);
Chris@0 1027 } else if ($token[0] !== T_CURLY_OPEN) {
Chris@0 1028 $cacheKey = $token[0];
Chris@0 1029 }
Chris@0 1030
Chris@0 1031 if ($cacheKey !== null && isset(self::$_resolveTokenCache[$cacheKey]) === true) {
Chris@0 1032 $newToken = self::$_resolveTokenCache[$cacheKey];
Chris@0 1033 $newToken['content'] = $token[1];
Chris@0 1034 }
Chris@0 1035 }
Chris@0 1036
Chris@0 1037 if ($newToken === null) {
Chris@0 1038 $newToken = self::standardiseToken($token);
Chris@0 1039 }
Chris@0 1040
Chris@0 1041 // Convert colons that are actually the ELSE component of an
Chris@0 1042 // inline IF statement.
Chris@0 1043 if (empty($insideInlineIf) === false && $newToken['code'] === T_COLON) {
Chris@0 1044 array_pop($insideInlineIf);
Chris@0 1045 $newToken['code'] = T_INLINE_ELSE;
Chris@0 1046 $newToken['type'] = 'T_INLINE_ELSE';
Chris@0 1047 }
Chris@0 1048
Chris@0 1049 // This is a special condition for T_ARRAY tokens used for
Chris@0 1050 // type hinting function arguments as being arrays. We want to keep
Chris@0 1051 // the parenthesis map clean, so let's tag these tokens as
Chris@0 1052 // T_ARRAY_HINT.
Chris@0 1053 if ($newToken['code'] === T_ARRAY) {
Chris@0 1054 for ($i = $stackPtr; $i < $numTokens; $i++) {
Chris@0 1055 if ($tokens[$i] === '(') {
Chris@0 1056 break;
Chris@0 1057 } else if ($tokens[$i][0] === T_VARIABLE) {
Chris@0 1058 $newToken['code'] = T_ARRAY_HINT;
Chris@0 1059 $newToken['type'] = 'T_ARRAY_HINT';
Chris@0 1060 break;
Chris@0 1061 }
Chris@0 1062 }
Chris@0 1063 }
Chris@0 1064
Chris@0 1065 // This is a special case when checking PHP 5.5+ code in PHP < 5.5
Chris@0 1066 // where "finally" should be T_FINALLY instead of T_STRING.
Chris@0 1067 if ($newToken['code'] === T_STRING
Chris@0 1068 && strtolower($newToken['content']) === 'finally'
Chris@0 1069 ) {
Chris@0 1070 $newToken['code'] = T_FINALLY;
Chris@0 1071 $newToken['type'] = 'T_FINALLY';
Chris@0 1072 }
Chris@0 1073
Chris@0 1074 // This is a special case for the PHP 5.5 classname::class syntax
Chris@0 1075 // where "class" should be T_STRING instead of T_CLASS.
Chris@0 1076 if (($newToken['code'] === T_CLASS
Chris@0 1077 || $newToken['code'] === T_FUNCTION)
Chris@0 1078 && $finalTokens[($newStackPtr - 1)]['code'] === T_DOUBLE_COLON
Chris@0 1079 ) {
Chris@0 1080 $newToken['code'] = T_STRING;
Chris@0 1081 $newToken['type'] = 'T_STRING';
Chris@0 1082 }
Chris@0 1083
Chris@0 1084 // This is a special case for PHP 5.6 use function and use const
Chris@0 1085 // where "function" and "const" should be T_STRING instead of T_FUNCTION
Chris@0 1086 // and T_CONST.
Chris@0 1087 if (($newToken['code'] === T_FUNCTION
Chris@0 1088 || $newToken['code'] === T_CONST)
Chris@0 1089 && $finalTokens[$lastNotEmptyToken]['code'] === T_USE
Chris@0 1090 ) {
Chris@0 1091 $newToken['code'] = T_STRING;
Chris@0 1092 $newToken['type'] = 'T_STRING';
Chris@0 1093 }
Chris@0 1094
Chris@0 1095 // This is a special case for use groups in PHP 7+ where leaving
Chris@0 1096 // the curly braces as their normal tokens would confuse
Chris@0 1097 // the scope map and sniffs.
Chris@0 1098 if ($newToken['code'] === T_OPEN_CURLY_BRACKET
Chris@0 1099 && $finalTokens[$lastNotEmptyToken]['code'] === T_NS_SEPARATOR
Chris@0 1100 ) {
Chris@0 1101 $newToken['code'] = T_OPEN_USE_GROUP;
Chris@0 1102 $newToken['type'] = 'T_OPEN_USE_GROUP';
Chris@0 1103 $insideUseGroup = true;
Chris@0 1104 }
Chris@0 1105
Chris@0 1106 if ($insideUseGroup === true && $newToken['code'] === T_CLOSE_CURLY_BRACKET) {
Chris@0 1107 $newToken['code'] = T_CLOSE_USE_GROUP;
Chris@0 1108 $newToken['type'] = 'T_CLOSE_USE_GROUP';
Chris@0 1109 $insideUseGroup = false;
Chris@0 1110 }
Chris@0 1111
Chris@0 1112 $finalTokens[$newStackPtr] = $newToken;
Chris@0 1113 $newStackPtr++;
Chris@0 1114 }//end if
Chris@0 1115 }//end for
Chris@0 1116
Chris@0 1117 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1118 echo "\t*** END PHP TOKENIZING ***".PHP_EOL;
Chris@0 1119 }
Chris@0 1120
Chris@0 1121 return $finalTokens;
Chris@0 1122
Chris@0 1123 }//end tokenizeString()
Chris@0 1124
Chris@0 1125
Chris@0 1126 /**
Chris@0 1127 * Performs additional processing after main tokenizing.
Chris@0 1128 *
Chris@0 1129 * This additional processing checks for CASE statements that are using curly
Chris@0 1130 * braces for scope openers and closers. It also turns some T_FUNCTION tokens
Chris@0 1131 * into T_CLOSURE when they are not standard function definitions. It also
Chris@0 1132 * detects short array syntax and converts those square brackets into new tokens.
Chris@0 1133 * It also corrects some usage of the static and class keywords. It also
Chris@0 1134 * assigns tokens to function return types.
Chris@0 1135 *
Chris@0 1136 * @param array $tokens The array of tokens to process.
Chris@0 1137 * @param string $eolChar The EOL character to use for splitting strings.
Chris@0 1138 *
Chris@0 1139 * @return void
Chris@0 1140 */
Chris@0 1141 public function processAdditional(&$tokens, $eolChar)
Chris@0 1142 {
Chris@0 1143 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1144 echo "\t*** START ADDITIONAL PHP PROCESSING ***".PHP_EOL;
Chris@0 1145 }
Chris@0 1146
Chris@0 1147 $numTokens = count($tokens);
Chris@0 1148 for ($i = ($numTokens - 1); $i >= 0; $i--) {
Chris@0 1149 // Check for any unset scope conditions due to alternate IF/ENDIF syntax.
Chris@0 1150 if (isset($tokens[$i]['scope_opener']) === true
Chris@0 1151 && isset($tokens[$i]['scope_condition']) === false
Chris@0 1152 ) {
Chris@0 1153 $tokens[$i]['scope_condition'] = $tokens[$tokens[$i]['scope_opener']]['scope_condition'];
Chris@0 1154 }
Chris@0 1155
Chris@0 1156 if ($tokens[$i]['code'] === T_FUNCTION) {
Chris@0 1157 /*
Chris@0 1158 Detect functions that are actually closures and
Chris@0 1159 assign them a different token.
Chris@0 1160 */
Chris@0 1161
Chris@0 1162 if (isset($tokens[$i]['scope_opener']) === true) {
Chris@0 1163 for ($x = ($i + 1); $x < $numTokens; $x++) {
Chris@0 1164 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false
Chris@0 1165 && $tokens[$x]['code'] !== T_BITWISE_AND
Chris@0 1166 ) {
Chris@0 1167 break;
Chris@0 1168 }
Chris@0 1169 }
Chris@0 1170
Chris@0 1171 if ($tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
Chris@0 1172 $tokens[$i]['code'] = T_CLOSURE;
Chris@0 1173 $tokens[$i]['type'] = 'T_CLOSURE';
Chris@0 1174 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1175 $line = $tokens[$i]['line'];
Chris@0 1176 echo "\t* token $i on line $line changed from T_FUNCTION to T_CLOSURE".PHP_EOL;
Chris@0 1177 }
Chris@0 1178
Chris@0 1179 for ($x = ($tokens[$i]['scope_opener'] + 1); $x < $tokens[$i]['scope_closer']; $x++) {
Chris@0 1180 if (isset($tokens[$x]['conditions'][$i]) === false) {
Chris@0 1181 continue;
Chris@0 1182 }
Chris@0 1183
Chris@0 1184 $tokens[$x]['conditions'][$i] = T_CLOSURE;
Chris@0 1185 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1186 $type = $tokens[$x]['type'];
Chris@0 1187 echo "\t\t* cleaned $x ($type) *".PHP_EOL;
Chris@0 1188 }
Chris@0 1189 }
Chris@0 1190 }
Chris@0 1191
Chris@0 1192 $tokenAfterReturnTypeHint = $tokens[$i]['scope_opener'];
Chris@0 1193 } else if (isset($tokens[$i]['parenthesis_closer']) === true) {
Chris@0 1194 $tokenAfterReturnTypeHint = null;
Chris@0 1195 for ($x = ($tokens[$i]['parenthesis_closer'] + 1); $x < $numTokens; $x++) {
Chris@0 1196 if ($tokens[$x]['code'] === T_SEMICOLON) {
Chris@0 1197 $tokenAfterReturnTypeHint = $x;
Chris@0 1198 break;
Chris@0 1199 }
Chris@0 1200 }
Chris@0 1201
Chris@0 1202 if ($tokenAfterReturnTypeHint === null) {
Chris@0 1203 // Probably a syntax error.
Chris@0 1204 continue;
Chris@0 1205 }
Chris@0 1206 } else {
Chris@0 1207 // Probably a syntax error.
Chris@0 1208 continue;
Chris@0 1209 }//end if
Chris@0 1210
Chris@0 1211 /*
Chris@0 1212 Detect function return values and assign them
Chris@0 1213 a special token, because PHP doesn't.
Chris@0 1214 */
Chris@0 1215
Chris@0 1216 for ($x = ($tokenAfterReturnTypeHint - 1); $x > $i; $x--) {
Chris@0 1217 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
Chris@0 1218 if (in_array($tokens[$x]['code'], array(T_STRING, T_ARRAY, T_ARRAY_HINT, T_CALLABLE, T_SELF, T_PARENT), true) === true) {
Chris@0 1219 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1220 $line = $tokens[$x]['line'];
Chris@0 1221 $type = $tokens[$x]['type'];
Chris@0 1222 echo "\t* token $x on line $line changed from $type to T_RETURN_TYPE".PHP_EOL;
Chris@0 1223 }
Chris@0 1224
Chris@0 1225 $tokens[$x]['code'] = T_RETURN_TYPE;
Chris@0 1226 $tokens[$x]['type'] = 'T_RETURN_TYPE';
Chris@0 1227 }
Chris@0 1228
Chris@0 1229 break;
Chris@0 1230 }
Chris@0 1231 }
Chris@0 1232
Chris@0 1233 continue;
Chris@0 1234 } else if ($tokens[$i]['code'] === T_CLASS && isset($tokens[$i]['scope_opener']) === true) {
Chris@0 1235 /*
Chris@0 1236 Detect anonymous classes and assign them a different token.
Chris@0 1237 */
Chris@0 1238
Chris@0 1239 for ($x = ($i + 1); $x < $numTokens; $x++) {
Chris@0 1240 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
Chris@0 1241 break;
Chris@0 1242 }
Chris@0 1243 }
Chris@0 1244
Chris@0 1245 if ($tokens[$x]['code'] === T_OPEN_PARENTHESIS
Chris@0 1246 || $tokens[$x]['code'] === T_OPEN_CURLY_BRACKET
Chris@0 1247 || $tokens[$x]['code'] === T_EXTENDS
Chris@0 1248 || $tokens[$x]['code'] === T_IMPLEMENTS
Chris@0 1249 ) {
Chris@0 1250 $tokens[$i]['code'] = T_ANON_CLASS;
Chris@0 1251 $tokens[$i]['type'] = 'T_ANON_CLASS';
Chris@0 1252 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1253 $line = $tokens[$i]['line'];
Chris@0 1254 echo "\t* token $i on line $line changed from T_CLASS to T_ANON_CLASS".PHP_EOL;
Chris@0 1255 }
Chris@0 1256
Chris@0 1257 for ($x = ($tokens[$i]['scope_opener'] + 1); $x < $tokens[$i]['scope_closer']; $x++) {
Chris@0 1258 if (isset($tokens[$x]['conditions'][$i]) === false) {
Chris@0 1259 continue;
Chris@0 1260 }
Chris@0 1261
Chris@0 1262 $tokens[$x]['conditions'][$i] = T_ANON_CLASS;
Chris@0 1263 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1264 $type = $tokens[$x]['type'];
Chris@0 1265 echo "\t\t* cleaned $x ($type) *".PHP_EOL;
Chris@0 1266 }
Chris@0 1267 }
Chris@0 1268 }
Chris@0 1269
Chris@0 1270 continue;
Chris@0 1271 } else if ($tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET) {
Chris@0 1272 if (isset($tokens[$i]['bracket_closer']) === false) {
Chris@0 1273 continue;
Chris@0 1274 }
Chris@0 1275
Chris@0 1276 // Unless there is a variable or a bracket before this token,
Chris@0 1277 // it is the start of an array being defined using the short syntax.
Chris@0 1278 $isShortArray = false;
Chris@0 1279 $allowed = array(
Chris@0 1280 T_CLOSE_SQUARE_BRACKET => T_CLOSE_SQUARE_BRACKET,
Chris@0 1281 T_CLOSE_PARENTHESIS => T_CLOSE_PARENTHESIS,
Chris@0 1282 T_VARIABLE => T_VARIABLE,
Chris@0 1283 T_OBJECT_OPERATOR => T_OBJECT_OPERATOR,
Chris@0 1284 T_STRING => T_STRING,
Chris@0 1285 );
Chris@0 1286
Chris@0 1287 for ($x = ($i - 1); $x > 0; $x--) {
Chris@0 1288 // If we hit a scope opener, the statement has ended
Chris@0 1289 // without finding anything, so it's probably an array
Chris@0 1290 // using PHP 7.1 short list syntax.
Chris@0 1291 if (isset($tokens[$x]['scope_opener']) === true) {
Chris@0 1292 $isShortArray = true;
Chris@0 1293 break;
Chris@0 1294 }
Chris@0 1295
Chris@0 1296 if (isset($tokens[$x]['bracket_opener']) === true
Chris@0 1297 && $x > $tokens[$x]['bracket_opener']
Chris@0 1298 ) {
Chris@0 1299 $x = $tokens[$x]['bracket_opener'];
Chris@0 1300 continue;
Chris@0 1301 }
Chris@0 1302
Chris@0 1303 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
Chris@0 1304 if (isset($allowed[$tokens[$x]['code']]) === false) {
Chris@0 1305 $isShortArray = true;
Chris@0 1306 }
Chris@0 1307
Chris@0 1308 break;
Chris@0 1309 }
Chris@0 1310 }//end for
Chris@0 1311
Chris@0 1312 if ($isShortArray === true) {
Chris@0 1313 $tokens[$i]['code'] = T_OPEN_SHORT_ARRAY;
Chris@0 1314 $tokens[$i]['type'] = 'T_OPEN_SHORT_ARRAY';
Chris@0 1315
Chris@0 1316 $closer = $tokens[$i]['bracket_closer'];
Chris@0 1317 $tokens[$closer]['code'] = T_CLOSE_SHORT_ARRAY;
Chris@0 1318 $tokens[$closer]['type'] = 'T_CLOSE_SHORT_ARRAY';
Chris@0 1319 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1320 $line = $tokens[$i]['line'];
Chris@0 1321 echo "\t* token $i on line $line changed from T_OPEN_SQUARE_BRACKET to T_OPEN_SHORT_ARRAY".PHP_EOL;
Chris@0 1322 $line = $tokens[$closer]['line'];
Chris@0 1323 echo "\t* token $closer on line $line changed from T_CLOSE_SQUARE_BRACKET to T_CLOSE_SHORT_ARRAY".PHP_EOL;
Chris@0 1324 }
Chris@0 1325 }
Chris@0 1326
Chris@0 1327 continue;
Chris@0 1328 } else if ($tokens[$i]['code'] === T_STATIC) {
Chris@0 1329 for ($x = ($i - 1); $x > 0; $x--) {
Chris@0 1330 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
Chris@0 1331 break;
Chris@0 1332 }
Chris@0 1333 }
Chris@0 1334
Chris@0 1335 if ($tokens[$x]['code'] === T_INSTANCEOF) {
Chris@0 1336 $tokens[$i]['code'] = T_STRING;
Chris@0 1337 $tokens[$i]['type'] = 'T_STRING';
Chris@0 1338
Chris@0 1339 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1340 $line = $tokens[$i]['line'];
Chris@0 1341 echo "\t* token $i on line $line changed from T_STATIC to T_STRING".PHP_EOL;
Chris@0 1342 }
Chris@0 1343 }
Chris@0 1344
Chris@0 1345 continue;
Chris@0 1346 } else if ($tokens[$i]['code'] === T_ECHO && $tokens[$i]['content'] === '<?=') {
Chris@0 1347 // HHVM tokenizes <?= as T_ECHO but it should be T_OPEN_TAG_WITH_ECHO.
Chris@0 1348 $tokens[$i]['code'] = T_OPEN_TAG_WITH_ECHO;
Chris@0 1349 $tokens[$i]['type'] = 'T_OPEN_TAG_WITH_ECHO';
Chris@0 1350
Chris@0 1351 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1352 $line = $tokens[$i]['line'];
Chris@0 1353 echo "\t* token $i on line $line changed from T_ECHO to T_OPEN_TAG_WITH_ECHO".PHP_EOL;
Chris@0 1354 }
Chris@0 1355 } else if ($tokens[$i]['code'] === T_TRUE
Chris@0 1356 || $tokens[$i]['code'] === T_FALSE
Chris@0 1357 || $tokens[$i]['code'] === T_NULL
Chris@0 1358 ) {
Chris@0 1359 for ($x = ($i + 1); $i < $numTokens; $x++) {
Chris@0 1360 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
Chris@0 1361 // Non-whitespace content.
Chris@0 1362 break;
Chris@0 1363 }
Chris@0 1364 }
Chris@0 1365
Chris@0 1366 $context = array(
Chris@0 1367 T_OBJECT_OPERATOR => true,
Chris@0 1368 T_NS_SEPARATOR => true,
Chris@0 1369 T_PAAMAYIM_NEKUDOTAYIM => true,
Chris@0 1370 );
Chris@0 1371 if (isset($context[$tokens[$x]['code']]) === true) {
Chris@0 1372 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1373 $line = $tokens[$i]['line'];
Chris@0 1374 $type = $tokens[$i]['type'];
Chris@0 1375 echo "\t* token $i on line $line changed from $type to T_STRING".PHP_EOL;
Chris@0 1376 }
Chris@0 1377
Chris@0 1378 $tokens[$i]['code'] = T_STRING;
Chris@0 1379 $tokens[$i]['type'] = 'T_STRING';
Chris@0 1380 }
Chris@0 1381 } else if ($tokens[$i]['code'] === T_CONST) {
Chris@0 1382 // Context sensitive keywords support.
Chris@0 1383 for ($x = ($i + 1); $i < $numTokens; $x++) {
Chris@0 1384 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
Chris@0 1385 // Non-whitespace content.
Chris@0 1386 break;
Chris@0 1387 }
Chris@0 1388 }
Chris@0 1389
Chris@0 1390 if ($tokens[$x]['code'] !== T_STRING) {
Chris@0 1391 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1392 $line = $tokens[$x]['line'];
Chris@0 1393 $type = $tokens[$x]['type'];
Chris@0 1394 echo "\t* token $x on line $line changed from $type to T_STRING".PHP_EOL;
Chris@0 1395 }
Chris@0 1396
Chris@0 1397 $tokens[$x]['code'] = T_STRING;
Chris@0 1398 $tokens[$x]['type'] = 'T_STRING';
Chris@0 1399 }
Chris@0 1400 }//end if
Chris@0 1401
Chris@0 1402 if (($tokens[$i]['code'] !== T_CASE
Chris@0 1403 && $tokens[$i]['code'] !== T_DEFAULT)
Chris@0 1404 || isset($tokens[$i]['scope_opener']) === false
Chris@0 1405 ) {
Chris@0 1406 // Only interested in CASE and DEFAULT statements from here on in.
Chris@0 1407 continue;
Chris@0 1408 }
Chris@0 1409
Chris@0 1410 $scopeOpener = $tokens[$i]['scope_opener'];
Chris@0 1411 $scopeCloser = $tokens[$i]['scope_closer'];
Chris@0 1412
Chris@0 1413 // If the first char after the opener is a curly brace
Chris@0 1414 // and that brace has been ignored, it is actually
Chris@0 1415 // opening this case statement and the opener and closer are
Chris@0 1416 // probably set incorrectly.
Chris@0 1417 for ($x = ($scopeOpener + 1); $x < $numTokens; $x++) {
Chris@0 1418 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
Chris@0 1419 // Non-whitespace content.
Chris@0 1420 break;
Chris@0 1421 }
Chris@0 1422 }
Chris@0 1423
Chris@0 1424 if ($tokens[$x]['code'] === T_CASE || $tokens[$x]['code'] === T_DEFAULT) {
Chris@0 1425 // Special case for multiple CASE statements that share the same
Chris@0 1426 // closer. Because we are going backwards through the file, this next
Chris@0 1427 // CASE/DEFAULT statement is already fixed, so just use its closer
Chris@0 1428 // and don't worry about fixing anything.
Chris@0 1429 $newCloser = $tokens[$x]['scope_closer'];
Chris@0 1430 $tokens[$i]['scope_closer'] = $newCloser;
Chris@0 1431 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1432 $oldType = $tokens[$scopeCloser]['type'];
Chris@0 1433 $newType = $tokens[$newCloser]['type'];
Chris@0 1434 $line = $tokens[$i]['line'];
Chris@0 1435 echo "\t* token $i (T_CASE) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
Chris@0 1436 }
Chris@0 1437
Chris@0 1438 continue;
Chris@0 1439 }
Chris@0 1440
Chris@0 1441 if ($tokens[$x]['code'] !== T_OPEN_CURLY_BRACKET
Chris@0 1442 || isset($tokens[$x]['scope_condition']) === true
Chris@0 1443 ) {
Chris@0 1444 // Not a CASE/DEFAULT with a curly brace opener.
Chris@0 1445 continue;
Chris@0 1446 }
Chris@0 1447
Chris@0 1448 // The closer for this CASE/DEFAULT should be the closing curly brace and
Chris@0 1449 // not whatever it already is. The opener needs to be the opening curly
Chris@0 1450 // brace so everything matches up.
Chris@0 1451 $newCloser = $tokens[$x]['bracket_closer'];
Chris@0 1452 foreach (array($i, $x, $newCloser) as $index) {
Chris@0 1453 $tokens[$index]['scope_condition'] = $i;
Chris@0 1454 $tokens[$index]['scope_opener'] = $x;
Chris@0 1455 $tokens[$index]['scope_closer'] = $newCloser;
Chris@0 1456 }
Chris@0 1457
Chris@0 1458 unset($tokens[$scopeOpener]['scope_condition']);
Chris@0 1459 unset($tokens[$scopeOpener]['scope_opener']);
Chris@0 1460 unset($tokens[$scopeOpener]['scope_closer']);
Chris@0 1461 unset($tokens[$scopeCloser]['scope_condition']);
Chris@0 1462 unset($tokens[$scopeCloser]['scope_opener']);
Chris@0 1463 unset($tokens[$scopeCloser]['scope_closer']);
Chris@0 1464 unset($tokens[$x]['bracket_opener']);
Chris@0 1465 unset($tokens[$x]['bracket_closer']);
Chris@0 1466 unset($tokens[$newCloser]['bracket_opener']);
Chris@0 1467 unset($tokens[$newCloser]['bracket_closer']);
Chris@0 1468 $tokens[$scopeCloser]['conditions'][] = $i;
Chris@0 1469
Chris@0 1470 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1471 $line = $tokens[$i]['line'];
Chris@0 1472 $tokenType = $tokens[$i]['type'];
Chris@0 1473
Chris@0 1474 $oldType = $tokens[$scopeOpener]['type'];
Chris@0 1475 $newType = $tokens[$x]['type'];
Chris@0 1476 echo "\t* token $i ($tokenType) on line $line opener changed from $scopeOpener ($oldType) to $x ($newType)".PHP_EOL;
Chris@0 1477
Chris@0 1478 $oldType = $tokens[$scopeCloser]['type'];
Chris@0 1479 $newType = $tokens[$newCloser]['type'];
Chris@0 1480 echo "\t* token $i ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL;
Chris@0 1481 }
Chris@0 1482
Chris@0 1483 // Now fix up all the tokens that think they are
Chris@0 1484 // inside the CASE/DEFAULT statement when they are really outside.
Chris@0 1485 for ($x = $newCloser; $x < $scopeCloser; $x++) {
Chris@0 1486 foreach ($tokens[$x]['conditions'] as $num => $oldCond) {
Chris@0 1487 if ($oldCond === $tokens[$i]['code']) {
Chris@0 1488 $oldConditions = $tokens[$x]['conditions'];
Chris@0 1489 unset($tokens[$x]['conditions'][$num]);
Chris@0 1490
Chris@0 1491 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1492 $type = $tokens[$x]['type'];
Chris@0 1493 $oldConds = '';
Chris@0 1494 foreach ($oldConditions as $condition) {
Chris@0 1495 $oldConds .= token_name($condition).',';
Chris@0 1496 }
Chris@0 1497
Chris@0 1498 $oldConds = rtrim($oldConds, ',');
Chris@0 1499
Chris@0 1500 $newConds = '';
Chris@0 1501 foreach ($tokens[$x]['conditions'] as $condition) {
Chris@0 1502 $newConds .= token_name($condition).',';
Chris@0 1503 }
Chris@0 1504
Chris@0 1505 $newConds = rtrim($newConds, ',');
Chris@0 1506
Chris@0 1507 echo "\t\t* cleaned $x ($type) *".PHP_EOL;
Chris@0 1508 echo "\t\t\t=> conditions changed from $oldConds to $newConds".PHP_EOL;
Chris@0 1509 }
Chris@0 1510
Chris@0 1511 break;
Chris@0 1512 }//end if
Chris@0 1513 }//end foreach
Chris@0 1514 }//end for
Chris@0 1515 }//end for
Chris@0 1516
Chris@0 1517 if (PHP_CODESNIFFER_VERBOSITY > 1) {
Chris@0 1518 echo "\t*** END ADDITIONAL PHP PROCESSING ***".PHP_EOL;
Chris@0 1519 }
Chris@0 1520
Chris@0 1521 }//end processAdditional()
Chris@0 1522
Chris@0 1523
Chris@0 1524 /**
Chris@0 1525 * Takes a token produced from <code>token_get_all()</code> and produces a
Chris@0 1526 * more uniform token.
Chris@0 1527 *
Chris@0 1528 * @param string|array $token The token to convert.
Chris@0 1529 *
Chris@0 1530 * @return array The new token.
Chris@0 1531 */
Chris@0 1532 public static function standardiseToken($token)
Chris@0 1533 {
Chris@0 1534 if (isset($token[1]) === false) {
Chris@0 1535 if (isset(self::$_resolveTokenCache[$token[0]]) === true) {
Chris@0 1536 return self::$_resolveTokenCache[$token[0]];
Chris@0 1537 }
Chris@0 1538 } else {
Chris@0 1539 $cacheKey = null;
Chris@0 1540 if ($token[0] === T_STRING) {
Chris@0 1541 $cacheKey = strtolower($token[1]);
Chris@0 1542 } else if ($token[0] !== T_CURLY_OPEN) {
Chris@0 1543 $cacheKey = $token[0];
Chris@0 1544 }
Chris@0 1545
Chris@0 1546 if ($cacheKey !== null && isset(self::$_resolveTokenCache[$cacheKey]) === true) {
Chris@0 1547 $newToken = self::$_resolveTokenCache[$cacheKey];
Chris@0 1548 $newToken['content'] = $token[1];
Chris@0 1549 return $newToken;
Chris@0 1550 }
Chris@0 1551 }
Chris@0 1552
Chris@0 1553 if (isset($token[1]) === false) {
Chris@0 1554 return self::resolveSimpleToken($token[0]);
Chris@0 1555 }
Chris@0 1556
Chris@0 1557 if ($token[0] === T_STRING) {
Chris@0 1558 switch ($cacheKey) {
Chris@0 1559 case 'false':
Chris@0 1560 $newToken['type'] = 'T_FALSE';
Chris@0 1561 break;
Chris@0 1562 case 'true':
Chris@0 1563 $newToken['type'] = 'T_TRUE';
Chris@0 1564 break;
Chris@0 1565 case 'null':
Chris@0 1566 $newToken['type'] = 'T_NULL';
Chris@0 1567 break;
Chris@0 1568 case 'self':
Chris@0 1569 $newToken['type'] = 'T_SELF';
Chris@0 1570 break;
Chris@0 1571 case 'parent':
Chris@0 1572 $newToken['type'] = 'T_PARENT';
Chris@0 1573 break;
Chris@0 1574 default:
Chris@0 1575 $newToken['type'] = 'T_STRING';
Chris@0 1576 break;
Chris@0 1577 }
Chris@0 1578
Chris@0 1579 $newToken['code'] = constant($newToken['type']);
Chris@0 1580
Chris@0 1581 self::$_resolveTokenCache[$cacheKey] = $newToken;
Chris@0 1582 } else if ($token[0] === T_CURLY_OPEN) {
Chris@0 1583 $newToken = array(
Chris@0 1584 'code' => T_OPEN_CURLY_BRACKET,
Chris@0 1585 'type' => 'T_OPEN_CURLY_BRACKET',
Chris@0 1586 );
Chris@0 1587 } else {
Chris@0 1588 $newToken = array(
Chris@0 1589 'code' => $token[0],
Chris@0 1590 'type' => token_name($token[0]),
Chris@0 1591 );
Chris@0 1592
Chris@0 1593 self::$_resolveTokenCache[$token[0]] = $newToken;
Chris@0 1594 }//end if
Chris@0 1595
Chris@0 1596 $newToken['content'] = $token[1];
Chris@0 1597 return $newToken;
Chris@0 1598
Chris@0 1599 }//end standardiseToken()
Chris@0 1600
Chris@0 1601
Chris@0 1602 /**
Chris@0 1603 * Converts simple tokens into a format that conforms to complex tokens
Chris@0 1604 * produced by token_get_all().
Chris@0 1605 *
Chris@0 1606 * Simple tokens are tokens that are not in array form when produced from
Chris@0 1607 * token_get_all().
Chris@0 1608 *
Chris@0 1609 * @param string $token The simple token to convert.
Chris@0 1610 *
Chris@0 1611 * @return array The new token in array format.
Chris@0 1612 */
Chris@0 1613 public static function resolveSimpleToken($token)
Chris@0 1614 {
Chris@0 1615 $newToken = array();
Chris@0 1616
Chris@0 1617 switch ($token) {
Chris@0 1618 case '{':
Chris@0 1619 $newToken['type'] = 'T_OPEN_CURLY_BRACKET';
Chris@0 1620 break;
Chris@0 1621 case '}':
Chris@0 1622 $newToken['type'] = 'T_CLOSE_CURLY_BRACKET';
Chris@0 1623 break;
Chris@0 1624 case '[':
Chris@0 1625 $newToken['type'] = 'T_OPEN_SQUARE_BRACKET';
Chris@0 1626 break;
Chris@0 1627 case ']':
Chris@0 1628 $newToken['type'] = 'T_CLOSE_SQUARE_BRACKET';
Chris@0 1629 break;
Chris@0 1630 case '(':
Chris@0 1631 $newToken['type'] = 'T_OPEN_PARENTHESIS';
Chris@0 1632 break;
Chris@0 1633 case ')':
Chris@0 1634 $newToken['type'] = 'T_CLOSE_PARENTHESIS';
Chris@0 1635 break;
Chris@0 1636 case ':':
Chris@0 1637 $newToken['type'] = 'T_COLON';
Chris@0 1638 break;
Chris@0 1639 case '.':
Chris@0 1640 $newToken['type'] = 'T_STRING_CONCAT';
Chris@0 1641 break;
Chris@0 1642 case ';':
Chris@0 1643 $newToken['type'] = 'T_SEMICOLON';
Chris@0 1644 break;
Chris@0 1645 case '=':
Chris@0 1646 $newToken['type'] = 'T_EQUAL';
Chris@0 1647 break;
Chris@0 1648 case '*':
Chris@0 1649 $newToken['type'] = 'T_MULTIPLY';
Chris@0 1650 break;
Chris@0 1651 case '/':
Chris@0 1652 $newToken['type'] = 'T_DIVIDE';
Chris@0 1653 break;
Chris@0 1654 case '+':
Chris@0 1655 $newToken['type'] = 'T_PLUS';
Chris@0 1656 break;
Chris@0 1657 case '-':
Chris@0 1658 $newToken['type'] = 'T_MINUS';
Chris@0 1659 break;
Chris@0 1660 case '%':
Chris@0 1661 $newToken['type'] = 'T_MODULUS';
Chris@0 1662 break;
Chris@0 1663 case '^':
Chris@0 1664 $newToken['type'] = 'T_BITWISE_XOR';
Chris@0 1665 break;
Chris@0 1666 case '&':
Chris@0 1667 $newToken['type'] = 'T_BITWISE_AND';
Chris@0 1668 break;
Chris@0 1669 case '|':
Chris@0 1670 $newToken['type'] = 'T_BITWISE_OR';
Chris@0 1671 break;
Chris@0 1672 case '<':
Chris@0 1673 $newToken['type'] = 'T_LESS_THAN';
Chris@0 1674 break;
Chris@0 1675 case '>':
Chris@0 1676 $newToken['type'] = 'T_GREATER_THAN';
Chris@0 1677 break;
Chris@0 1678 case '!':
Chris@0 1679 $newToken['type'] = 'T_BOOLEAN_NOT';
Chris@0 1680 break;
Chris@0 1681 case ',':
Chris@0 1682 $newToken['type'] = 'T_COMMA';
Chris@0 1683 break;
Chris@0 1684 case '@':
Chris@0 1685 $newToken['type'] = 'T_ASPERAND';
Chris@0 1686 break;
Chris@0 1687 case '$':
Chris@0 1688 $newToken['type'] = 'T_DOLLAR';
Chris@0 1689 break;
Chris@0 1690 case '`':
Chris@0 1691 $newToken['type'] = 'T_BACKTICK';
Chris@0 1692 break;
Chris@0 1693 default:
Chris@0 1694 $newToken['type'] = 'T_NONE';
Chris@0 1695 break;
Chris@0 1696 }//end switch
Chris@0 1697
Chris@0 1698 $newToken['code'] = constant($newToken['type']);
Chris@0 1699 $newToken['content'] = $token;
Chris@0 1700
Chris@0 1701 self::$_resolveTokenCache[$token] = $newToken;
Chris@0 1702 return $newToken;
Chris@0 1703
Chris@0 1704 }//end resolveSimpleToken()
Chris@0 1705
Chris@0 1706
Chris@0 1707 }//end class