Chris@0: Chris@0: * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) Chris@0: * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence Chris@0: * @link http://pear.php.net/package/PHP_CodeSniffer Chris@0: */ Chris@0: Chris@0: /** Chris@0: * Tokenizes PHP code. Chris@0: * Chris@0: * @category PHP Chris@0: * @package PHP_CodeSniffer Chris@0: * @author Greg Sherwood Chris@0: * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) Chris@0: * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence Chris@0: * @version Release: @package_version@ Chris@0: * @link http://pear.php.net/package/PHP_CodeSniffer Chris@0: */ Chris@0: class PHP_CodeSniffer_Tokenizers_PHP Chris@0: { Chris@0: Chris@0: /** Chris@0: * If TRUE, files that appear to be minified will not be processed. Chris@0: * Chris@0: * @var boolean Chris@0: */ Chris@0: public $skipMinified = false; Chris@0: Chris@0: /** Chris@0: * A list of tokens that are allowed to open a scope. Chris@0: * Chris@0: * This array also contains information about what kind of token the scope Chris@0: * opener uses to open and close the scope, if the token strictly requires Chris@0: * an opener, if the token can share a scope closer, and who it can be shared Chris@0: * with. An example of a token that shares a scope closer is a CASE scope. Chris@0: * Chris@0: * @var array Chris@0: */ Chris@0: public $scopeOpeners = array( Chris@0: T_IF => array( Chris@0: 'start' => array( Chris@0: T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, Chris@0: T_COLON => T_COLON, Chris@0: ), Chris@0: 'end' => array( Chris@0: T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, Chris@0: T_ENDIF => T_ENDIF, Chris@0: T_ELSE => T_ELSE, Chris@0: T_ELSEIF => T_ELSEIF, Chris@0: ), Chris@0: 'strict' => false, Chris@0: 'shared' => false, Chris@0: 'with' => array( Chris@0: T_ELSE => T_ELSE, Chris@0: T_ELSEIF => T_ELSEIF, Chris@0: ), Chris@0: ), Chris@0: T_TRY => array( Chris@0: 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET), Chris@0: 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET), Chris@0: 'strict' => true, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_CATCH => array( Chris@0: 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET), Chris@0: 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET), Chris@0: 'strict' => true, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_FINALLY => array( Chris@0: 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET), Chris@0: 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET), Chris@0: 'strict' => true, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_ELSE => array( Chris@0: 'start' => array( Chris@0: T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, Chris@0: T_COLON => T_COLON, Chris@0: ), Chris@0: 'end' => array( Chris@0: T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, Chris@0: T_ENDIF => T_ENDIF, Chris@0: ), Chris@0: 'strict' => false, Chris@0: 'shared' => false, Chris@0: 'with' => array( Chris@0: T_IF => T_IF, Chris@0: T_ELSEIF => T_ELSEIF, Chris@0: ), Chris@0: ), Chris@0: T_ELSEIF => array( Chris@0: 'start' => array( Chris@0: T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, Chris@0: T_COLON => T_COLON, Chris@0: ), Chris@0: 'end' => array( Chris@0: T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, Chris@0: T_ENDIF => T_ENDIF, Chris@0: T_ELSE => T_ELSE, Chris@0: T_ELSEIF => T_ELSEIF, Chris@0: ), Chris@0: 'strict' => false, Chris@0: 'shared' => false, Chris@0: 'with' => array( Chris@0: T_IF => T_IF, Chris@0: T_ELSE => T_ELSE, Chris@0: ), Chris@0: ), Chris@0: T_FOR => array( Chris@0: 'start' => array( Chris@0: T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, Chris@0: T_COLON => T_COLON, Chris@0: ), Chris@0: 'end' => array( Chris@0: T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, Chris@0: T_ENDFOR => T_ENDFOR, Chris@0: ), Chris@0: 'strict' => false, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_FOREACH => array( Chris@0: 'start' => array( Chris@0: T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, Chris@0: T_COLON => T_COLON, Chris@0: ), Chris@0: 'end' => array( Chris@0: T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, Chris@0: T_ENDFOREACH => T_ENDFOREACH, Chris@0: ), Chris@0: 'strict' => false, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_INTERFACE => array( Chris@0: 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET), Chris@0: 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET), Chris@0: 'strict' => true, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_FUNCTION => array( Chris@0: 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET), Chris@0: 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET), Chris@0: 'strict' => true, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_CLASS => array( Chris@0: 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET), Chris@0: 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET), Chris@0: 'strict' => true, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_TRAIT => array( Chris@0: 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET), Chris@0: 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET), Chris@0: 'strict' => true, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_USE => array( Chris@0: 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET), Chris@0: 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET), Chris@0: 'strict' => false, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_DECLARE => array( Chris@0: 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET), Chris@0: 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET), Chris@0: 'strict' => false, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_NAMESPACE => array( Chris@0: 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET), Chris@0: 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET), Chris@0: 'strict' => false, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_WHILE => array( Chris@0: 'start' => array( Chris@0: T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, Chris@0: T_COLON => T_COLON, Chris@0: ), Chris@0: 'end' => array( Chris@0: T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, Chris@0: T_ENDWHILE => T_ENDWHILE, Chris@0: ), Chris@0: 'strict' => false, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_DO => array( Chris@0: 'start' => array(T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET), Chris@0: 'end' => array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET), Chris@0: 'strict' => true, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_SWITCH => array( Chris@0: 'start' => array( Chris@0: T_OPEN_CURLY_BRACKET => T_OPEN_CURLY_BRACKET, Chris@0: T_COLON => T_COLON, Chris@0: ), Chris@0: 'end' => array( Chris@0: T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, Chris@0: T_ENDSWITCH => T_ENDSWITCH, Chris@0: ), Chris@0: 'strict' => true, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: T_CASE => array( Chris@0: 'start' => array( Chris@0: T_COLON => T_COLON, Chris@0: T_SEMICOLON => T_SEMICOLON, Chris@0: ), Chris@0: 'end' => array( Chris@0: T_BREAK => T_BREAK, Chris@0: T_RETURN => T_RETURN, Chris@0: T_CONTINUE => T_CONTINUE, Chris@0: T_THROW => T_THROW, Chris@0: T_EXIT => T_EXIT, Chris@0: ), Chris@0: 'strict' => true, Chris@0: 'shared' => true, Chris@0: 'with' => array( Chris@0: T_DEFAULT => T_DEFAULT, Chris@0: T_CASE => T_CASE, Chris@0: T_SWITCH => T_SWITCH, Chris@0: ), Chris@0: ), Chris@0: T_DEFAULT => array( Chris@0: 'start' => array( Chris@0: T_COLON => T_COLON, Chris@0: T_SEMICOLON => T_SEMICOLON, Chris@0: ), Chris@0: 'end' => array( Chris@0: T_BREAK => T_BREAK, Chris@0: T_RETURN => T_RETURN, Chris@0: T_CONTINUE => T_CONTINUE, Chris@0: T_THROW => T_THROW, Chris@0: T_EXIT => T_EXIT, Chris@0: ), Chris@0: 'strict' => true, Chris@0: 'shared' => true, Chris@0: 'with' => array( Chris@0: T_CASE => T_CASE, Chris@0: T_SWITCH => T_SWITCH, Chris@0: ), Chris@0: ), Chris@0: T_START_HEREDOC => array( Chris@0: 'start' => array(T_START_HEREDOC => T_START_HEREDOC), Chris@0: 'end' => array(T_END_HEREDOC => T_END_HEREDOC), Chris@0: 'strict' => true, Chris@0: 'shared' => false, Chris@0: 'with' => array(), Chris@0: ), Chris@0: ); Chris@0: Chris@0: /** Chris@0: * A list of tokens that end the scope. Chris@0: * Chris@0: * This array is just a unique collection of the end tokens Chris@0: * from the _scopeOpeners array. The data is duplicated here to Chris@0: * save time during parsing of the file. Chris@0: * Chris@0: * @var array Chris@0: */ Chris@0: public $endScopeTokens = array( Chris@0: T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, Chris@0: T_ENDIF => T_ENDIF, Chris@0: T_ENDFOR => T_ENDFOR, Chris@0: T_ENDFOREACH => T_ENDFOREACH, Chris@0: T_ENDWHILE => T_ENDWHILE, Chris@0: T_ENDSWITCH => T_ENDSWITCH, Chris@0: T_BREAK => T_BREAK, Chris@0: T_END_HEREDOC => T_END_HEREDOC, Chris@0: ); Chris@0: Chris@0: /** Chris@0: * A cache of different token types, resolved into arrays. Chris@0: * Chris@0: * @var array() Chris@0: * @see standardiseToken() Chris@0: */ Chris@0: private static $_resolveTokenCache = array(); Chris@0: Chris@0: Chris@0: /** Chris@0: * Creates an array of tokens when given some PHP code. Chris@0: * Chris@0: * Starts by using token_get_all() but does a lot of extra processing Chris@0: * to insert information about the context of the token. Chris@0: * Chris@0: * @param string $string The string to tokenize. Chris@0: * @param string $eolChar The EOL character to use for splitting strings. Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: public function tokenizeString($string, $eolChar='\n') Chris@0: { Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: echo "\t*** START PHP TOKENIZING ***".PHP_EOL; Chris@0: $isWin = false; Chris@0: if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { Chris@0: $isWin = true; Chris@0: } Chris@0: } Chris@0: Chris@0: $tokens = @token_get_all($string); Chris@0: $finalTokens = array(); Chris@0: Chris@0: $newStackPtr = 0; Chris@0: $numTokens = count($tokens); Chris@0: $lastNotEmptyToken = 0; Chris@0: Chris@0: $insideInlineIf = array(); Chris@0: $insideUseGroup = false; Chris@0: Chris@0: $commentTokenizer = new PHP_CodeSniffer_Tokenizers_Comment(); Chris@0: Chris@0: for ($stackPtr = 0; $stackPtr < $numTokens; $stackPtr++) { Chris@0: $token = (array) $tokens[$stackPtr]; Chris@0: $tokenIsArray = isset($token[1]); Chris@0: Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: if ($tokenIsArray === true) { Chris@0: $type = token_name($token[0]); Chris@0: $content = PHP_CodeSniffer::prepareForOutput($token[1]); Chris@0: } else { Chris@0: $newToken = self::resolveSimpleToken($token[0]); Chris@0: $type = $newToken['type']; Chris@0: $content = PHP_CodeSniffer::prepareForOutput($token[0]); Chris@0: } Chris@0: Chris@0: echo "\tProcess token "; Chris@0: if ($tokenIsArray === true) { Chris@0: echo "[$stackPtr]"; Chris@0: } else { Chris@0: echo " $stackPtr "; Chris@0: } Chris@0: Chris@0: echo ": $type => $content"; Chris@0: }//end if Chris@0: Chris@0: if ($newStackPtr > 0 && $finalTokens[($newStackPtr - 1)]['code'] !== T_WHITESPACE) { Chris@0: $lastNotEmptyToken = ($newStackPtr - 1); Chris@0: } Chris@0: Chris@0: /* Chris@0: If we are using \r\n newline characters, the \r and \n are sometimes Chris@0: split over two tokens. This normally occurs after comments. We need Chris@0: to merge these two characters together so that our line endings are Chris@0: consistent for all lines. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === true && substr($token[1], -1) === "\r") { Chris@0: if (isset($tokens[($stackPtr + 1)]) === true Chris@0: && is_array($tokens[($stackPtr + 1)]) === true Chris@0: && $tokens[($stackPtr + 1)][1][0] === "\n" Chris@0: ) { Chris@0: $token[1] .= "\n"; Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: if ($isWin === true) { Chris@0: echo '\n'; Chris@0: } else { Chris@0: echo "\033[30;1m\\n\033[0m"; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($tokens[($stackPtr + 1)][1] === "\n") { Chris@0: // This token's content has been merged into the previous, Chris@0: // so we can skip it. Chris@0: $tokens[($stackPtr + 1)] = ''; Chris@0: } else { Chris@0: $tokens[($stackPtr + 1)][1] = substr($tokens[($stackPtr + 1)][1], 1); Chris@0: } Chris@0: } Chris@0: }//end if Chris@0: Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: echo PHP_EOL; Chris@0: } Chris@0: Chris@0: /* Chris@0: Parse doc blocks into something that can be easily iterated over. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === true && $token[0] === T_DOC_COMMENT) { Chris@0: $commentTokens = $commentTokenizer->tokenizeString($token[1], $eolChar, $newStackPtr); Chris@0: foreach ($commentTokens as $commentToken) { Chris@0: $finalTokens[$newStackPtr] = $commentToken; Chris@0: $newStackPtr++; Chris@0: } Chris@0: Chris@0: continue; Chris@0: } Chris@0: Chris@0: /* Chris@0: If this is a double quoted string, PHP will tokenize the whole Chris@0: thing which causes problems with the scope map when braces are Chris@0: within the string. So we need to merge the tokens together to Chris@0: provide a single string. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === false && ($token[0] === '"' || $token[0] === 'b"')) { Chris@0: // Binary casts need a special token. Chris@0: if ($token[0] === 'b"') { Chris@0: $finalTokens[$newStackPtr] = array( Chris@0: 'code' => T_BINARY_CAST, Chris@0: 'type' => 'T_BINARY_CAST', Chris@0: 'content' => 'b', Chris@0: ); Chris@0: $newStackPtr++; Chris@0: } Chris@0: Chris@0: $tokenContent = '"'; Chris@0: $nestedVars = array(); Chris@0: for ($i = ($stackPtr + 1); $i < $numTokens; $i++) { Chris@0: $subToken = (array) $tokens[$i]; Chris@0: $subTokenIsArray = isset($subToken[1]); Chris@0: Chris@0: if ($subTokenIsArray === true) { Chris@0: $tokenContent .= $subToken[1]; Chris@0: if ($subToken[1] === '{' Chris@0: && $subToken[0] !== T_ENCAPSED_AND_WHITESPACE Chris@0: ) { Chris@0: $nestedVars[] = $i; Chris@0: } Chris@0: } else { Chris@0: $tokenContent .= $subToken[0]; Chris@0: if ($subToken[0] === '}') { Chris@0: array_pop($nestedVars); Chris@0: } Chris@0: } Chris@0: Chris@0: if ($subTokenIsArray === false Chris@0: && $subToken[0] === '"' Chris@0: && empty($nestedVars) === true Chris@0: ) { Chris@0: // We found the other end of the double quoted string. Chris@0: break; Chris@0: } Chris@0: }//end for Chris@0: Chris@0: $stackPtr = $i; Chris@0: Chris@0: // Convert each line within the double quoted string to a Chris@0: // new token, so it conforms with other multiple line tokens. Chris@0: $tokenLines = explode($eolChar, $tokenContent); Chris@0: $numLines = count($tokenLines); Chris@0: $newToken = array(); Chris@0: Chris@0: for ($j = 0; $j < $numLines; $j++) { Chris@0: $newToken['content'] = $tokenLines[$j]; Chris@0: if ($j === ($numLines - 1)) { Chris@0: if ($tokenLines[$j] === '') { Chris@0: break; Chris@0: } Chris@0: } else { Chris@0: $newToken['content'] .= $eolChar; Chris@0: } Chris@0: Chris@0: $newToken['code'] = T_DOUBLE_QUOTED_STRING; Chris@0: $newToken['type'] = 'T_DOUBLE_QUOTED_STRING'; Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: $newStackPtr++; Chris@0: } Chris@0: Chris@0: // Continue, as we're done with this token. Chris@0: continue; Chris@0: }//end if Chris@0: Chris@0: /* Chris@0: If this is a heredoc, PHP will tokenize the whole Chris@0: thing which causes problems when heredocs don't Chris@0: contain real PHP code, which is almost never. Chris@0: We want to leave the start and end heredoc tokens Chris@0: alone though. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === true && $token[0] === T_START_HEREDOC) { Chris@0: // Add the start heredoc token to the final array. Chris@0: $finalTokens[$newStackPtr] = self::standardiseToken($token); Chris@0: Chris@0: // Check if this is actually a nowdoc and use a different token Chris@0: // to help the sniffs. Chris@0: $nowdoc = false; Chris@0: if ($token[1][3] === "'") { Chris@0: $finalTokens[$newStackPtr]['code'] = T_START_NOWDOC; Chris@0: $finalTokens[$newStackPtr]['type'] = 'T_START_NOWDOC'; Chris@0: $nowdoc = true; Chris@0: } Chris@0: Chris@0: $tokenContent = ''; Chris@0: for ($i = ($stackPtr + 1); $i < $numTokens; $i++) { Chris@0: $subTokenIsArray = is_array($tokens[$i]); Chris@0: if ($subTokenIsArray === true Chris@0: && $tokens[$i][0] === T_END_HEREDOC Chris@0: ) { Chris@0: // We found the other end of the heredoc. Chris@0: break; Chris@0: } Chris@0: Chris@0: if ($subTokenIsArray === true) { Chris@0: $tokenContent .= $tokens[$i][1]; Chris@0: } else { Chris@0: $tokenContent .= $tokens[$i]; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($i === $numTokens) { Chris@0: // We got to the end of the file and never Chris@0: // found the closing token, so this probably wasn't Chris@0: // a heredoc. Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $type = $finalTokens[$newStackPtr]['type']; Chris@0: echo "\t\t* failed to find the end of the here/nowdoc".PHP_EOL; Chris@0: echo "\t\t* token $stackPtr changed from $type to T_STRING".PHP_EOL; Chris@0: } Chris@0: Chris@0: $finalTokens[$newStackPtr]['code'] = T_STRING; Chris@0: $finalTokens[$newStackPtr]['type'] = 'T_STRING'; Chris@0: $newStackPtr++; Chris@0: continue; Chris@0: } Chris@0: Chris@0: $stackPtr = $i; Chris@0: $newStackPtr++; Chris@0: Chris@0: // Convert each line within the heredoc to a Chris@0: // new token, so it conforms with other multiple line tokens. Chris@0: $tokenLines = explode($eolChar, $tokenContent); Chris@0: $numLines = count($tokenLines); Chris@0: $newToken = array(); Chris@0: Chris@0: for ($j = 0; $j < $numLines; $j++) { Chris@0: $newToken['content'] = $tokenLines[$j]; Chris@0: if ($j === ($numLines - 1)) { Chris@0: if ($tokenLines[$j] === '') { Chris@0: break; Chris@0: } Chris@0: } else { Chris@0: $newToken['content'] .= $eolChar; Chris@0: } Chris@0: Chris@0: if ($nowdoc === true) { Chris@0: $newToken['code'] = T_NOWDOC; Chris@0: $newToken['type'] = 'T_NOWDOC'; Chris@0: } else { Chris@0: $newToken['code'] = T_HEREDOC; Chris@0: $newToken['type'] = 'T_HEREDOC'; Chris@0: } Chris@0: Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: $newStackPtr++; Chris@0: }//end for Chris@0: Chris@0: // Add the end heredoc token to the final array. Chris@0: $finalTokens[$newStackPtr] = self::standardiseToken($tokens[$stackPtr]); Chris@0: Chris@0: if ($nowdoc === true) { Chris@0: $finalTokens[$newStackPtr]['code'] = T_END_NOWDOC; Chris@0: $finalTokens[$newStackPtr]['type'] = 'T_END_NOWDOC'; Chris@0: $nowdoc = true; Chris@0: } Chris@0: Chris@0: $newStackPtr++; Chris@0: Chris@0: // Continue, as we're done with this token. Chris@0: continue; Chris@0: }//end if Chris@0: Chris@0: /* Chris@0: Before PHP 5.6, the ... operator was tokenized as three Chris@0: T_STRING_CONCAT tokens in a row. So look for and combine Chris@0: these tokens in earlier versions. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === false Chris@0: && $token[0] === '.' Chris@0: && isset($tokens[($stackPtr + 1)]) === true Chris@0: && isset($tokens[($stackPtr + 2)]) === true Chris@0: && $tokens[($stackPtr + 1)] === '.' Chris@0: && $tokens[($stackPtr + 2)] === '.' Chris@0: ) { Chris@0: $newToken = array(); Chris@0: $newToken['code'] = T_ELLIPSIS; Chris@0: $newToken['type'] = 'T_ELLIPSIS'; Chris@0: $newToken['content'] = '...'; Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: Chris@0: $newStackPtr++; Chris@0: $stackPtr += 2; Chris@0: continue; Chris@0: } Chris@0: Chris@0: /* Chris@0: Before PHP 5.6, the ** operator was tokenized as two Chris@0: T_MULTIPLY tokens in a row. So look for and combine Chris@0: these tokens in earlier versions. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === false Chris@0: && $token[0] === '*' Chris@0: && isset($tokens[($stackPtr + 1)]) === true Chris@0: && $tokens[($stackPtr + 1)] === '*' Chris@0: ) { Chris@0: $newToken = array(); Chris@0: $newToken['code'] = T_POW; Chris@0: $newToken['type'] = 'T_POW'; Chris@0: $newToken['content'] = '**'; Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: Chris@0: $newStackPtr++; Chris@0: $stackPtr++; Chris@0: continue; Chris@0: } Chris@0: Chris@0: /* Chris@0: Before PHP 5.6, the **= operator was tokenized as Chris@0: T_MULTIPLY followed by T_MUL_EQUAL. So look for and combine Chris@0: these tokens in earlier versions. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === false Chris@0: && $token[0] === '*' Chris@0: && isset($tokens[($stackPtr + 1)]) === true Chris@0: && is_array($tokens[($stackPtr + 1)]) === true Chris@0: && $tokens[($stackPtr + 1)][1] === '*=' Chris@0: ) { Chris@0: $newToken = array(); Chris@0: $newToken['code'] = T_POW_EQUAL; Chris@0: $newToken['type'] = 'T_POW_EQUAL'; Chris@0: $newToken['content'] = '**='; Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: Chris@0: $newStackPtr++; Chris@0: $stackPtr++; Chris@0: continue; Chris@0: } Chris@0: Chris@0: /* Chris@0: Before PHP 7, the ??= operator was tokenized as Chris@0: T_INLINE_THEN, T_INLINE_THEN, T_EQUAL. Chris@0: Between PHP 7.0 and 7.2, the ??= operator was tokenized as Chris@0: T_COALESCE, T_EQUAL. Chris@0: So look for and combine these tokens in earlier versions. Chris@0: */ Chris@0: Chris@0: if (($tokenIsArray === false Chris@0: && $token[0] === '?' Chris@0: && isset($tokens[($stackPtr + 1)]) === true Chris@0: && $tokens[($stackPtr + 1)][0] === '?' Chris@0: && isset($tokens[($stackPtr + 2)]) === true Chris@0: && $tokens[($stackPtr + 2)][0] === '=') Chris@0: || ($tokenIsArray === true Chris@0: && $token[0] === T_COALESCE Chris@0: && isset($tokens[($stackPtr + 1)]) === true Chris@0: && $tokens[($stackPtr + 1)][0] === '=') Chris@0: ) { Chris@0: $newToken = array(); Chris@0: $newToken['code'] = T_COALESCE_EQUAL; Chris@0: $newToken['type'] = 'T_COALESCE_EQUAL'; Chris@0: $newToken['content'] = '??='; Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: Chris@0: $newStackPtr++; Chris@0: $stackPtr++; Chris@0: Chris@0: if ($tokenIsArray === false) { Chris@0: // Pre PHP 7. Chris@0: $stackPtr++; Chris@0: } Chris@0: Chris@0: continue; Chris@0: } Chris@0: Chris@0: /* Chris@0: Before PHP 7, the ?? operator was tokenized as Chris@0: T_INLINE_THEN followed by T_INLINE_THEN. Chris@0: So look for and combine these tokens in earlier versions. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === false Chris@0: && $token[0] === '?' Chris@0: && isset($tokens[($stackPtr + 1)]) === true Chris@0: && $tokens[($stackPtr + 1)][0] === '?' Chris@0: ) { Chris@0: $newToken = array(); Chris@0: $newToken['code'] = T_COALESCE; Chris@0: $newToken['type'] = 'T_COALESCE'; Chris@0: $newToken['content'] = '??'; Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: Chris@0: $newStackPtr++; Chris@0: $stackPtr++; Chris@0: continue; Chris@0: } Chris@0: Chris@0: /* Chris@0: Convert ? to T_NULLABLE OR T_INLINE_THEN Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === false && $token[0] === '?') { Chris@0: $newToken = array(); Chris@0: $newToken['content'] = '?'; Chris@0: Chris@0: for ($i = ($stackPtr - 1); $i >= 0; $i--) { Chris@0: if (is_array($tokens[$i]) === true) { Chris@0: $tokenType = $tokens[$i][0]; Chris@0: } else { Chris@0: $tokenType = $tokens[$i]; Chris@0: } Chris@0: Chris@0: if ($tokenType === T_FUNCTION) { Chris@0: $newToken['code'] = T_NULLABLE; Chris@0: $newToken['type'] = 'T_NULLABLE'; Chris@0: break; Chris@0: } else if (in_array($tokenType, array(T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, '{', ';')) === true) { Chris@0: $newToken['code'] = T_INLINE_THEN; Chris@0: $newToken['type'] = 'T_INLINE_THEN'; Chris@0: Chris@0: $insideInlineIf[] = $stackPtr; Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: $newStackPtr++; Chris@0: continue; Chris@0: }//end if Chris@0: Chris@0: /* Chris@0: Tokens after a double colon may be look like scope openers, Chris@0: such as when writing code like Foo::NAMESPACE, but they are Chris@0: only ever variables or strings. Chris@0: */ Chris@0: Chris@0: if ($stackPtr > 1 Chris@0: && (is_array($tokens[($stackPtr - 1)]) === true Chris@0: && $tokens[($stackPtr - 1)][0] === T_PAAMAYIM_NEKUDOTAYIM) Chris@0: && $tokenIsArray === true Chris@0: && $token[0] !== T_STRING Chris@0: && $token[0] !== T_VARIABLE Chris@0: && $token[0] !== T_DOLLAR Chris@0: && isset(PHP_CodeSniffer_Tokens::$emptyTokens[$token[0]]) === false Chris@0: ) { Chris@0: $newToken = array(); Chris@0: $newToken['code'] = T_STRING; Chris@0: $newToken['type'] = 'T_STRING'; Chris@0: $newToken['content'] = $token[1]; Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: Chris@0: $newStackPtr++; Chris@0: continue; Chris@0: } Chris@0: Chris@0: /* Chris@0: The string-like token after a function keyword should always be Chris@0: tokenized as T_STRING even if it appears to be a different token, Chris@0: such as when writing code like: function default(): foo Chris@0: so go forward and change the token type before it is processed. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === true && $token[0] === T_FUNCTION) { Chris@0: for ($x = ($stackPtr + 1); $x < $numTokens; $x++) { Chris@0: if (is_array($tokens[$x]) === false Chris@0: || isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x][0]]) === false Chris@0: ) { Chris@0: // Non-empty content. Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($x < $numTokens && is_array($tokens[$x]) === true) { Chris@0: $tokens[$x][0] = T_STRING; Chris@0: } Chris@0: } Chris@0: Chris@0: /* Chris@0: Before PHP 7, the <=> operator was tokenized as Chris@0: T_IS_SMALLER_OR_EQUAL followed by T_GREATER_THAN. Chris@0: So look for and combine these tokens in earlier versions. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === true Chris@0: && $token[0] === T_IS_SMALLER_OR_EQUAL Chris@0: && isset($tokens[($stackPtr + 1)]) === true Chris@0: && $tokens[($stackPtr + 1)][0] === '>' Chris@0: ) { Chris@0: $newToken = array(); Chris@0: $newToken['code'] = T_SPACESHIP; Chris@0: $newToken['type'] = 'T_SPACESHIP'; Chris@0: $newToken['content'] = '<=>'; Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: Chris@0: $newStackPtr++; Chris@0: $stackPtr++; Chris@0: continue; Chris@0: } Chris@0: Chris@0: /* Chris@0: Emulate traits in PHP versions less than 5.4. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === true Chris@0: && $token[0] === T_STRING Chris@0: && strtolower($token[1]) === 'trait' Chris@0: && $tokens[($stackPtr - 1)][0] !== T_OBJECT_OPERATOR Chris@0: && $tokens[($stackPtr - 1)][0] !== T_PAAMAYIM_NEKUDOTAYIM Chris@0: ) { Chris@0: $finalTokens[$newStackPtr] = array( Chris@0: 'content' => $token[1], Chris@0: 'code' => T_TRAIT, Chris@0: 'type' => 'T_TRAIT', Chris@0: ); Chris@0: Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: echo "\t\t* token $stackPtr changed from T_STRING to T_TRAIT".PHP_EOL; Chris@0: } Chris@0: Chris@0: $newStackPtr++; Chris@0: continue; Chris@0: } Chris@0: Chris@0: /* Chris@0: PHP doesn't assign a token to goto labels, so we have to. Chris@0: These are just string tokens with a single colon after them. Double Chris@0: colons are already tokenized and so don't interfere with this check. Chris@0: But we do have to account for CASE statements, that look just like Chris@0: goto labels. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === true Chris@0: && $token[0] === T_STRING Chris@0: && isset($tokens[($stackPtr + 1)]) === true Chris@0: && $tokens[($stackPtr + 1)] === ':' Chris@0: && $tokens[($stackPtr - 1)][0] !== T_PAAMAYIM_NEKUDOTAYIM Chris@0: ) { Chris@0: $stopTokens = array( Chris@0: T_CASE => true, Chris@0: T_SEMICOLON => true, Chris@0: T_OPEN_CURLY_BRACKET => true, Chris@0: T_INLINE_THEN => true, Chris@0: ); Chris@0: Chris@0: for ($x = ($newStackPtr - 1); $x > 0; $x--) { Chris@0: if (isset($stopTokens[$finalTokens[$x]['code']]) === true) { Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($finalTokens[$x]['code'] !== T_CASE Chris@0: && $finalTokens[$x]['code'] !== T_INLINE_THEN Chris@0: ) { Chris@0: $finalTokens[$newStackPtr] = array( Chris@0: 'content' => $token[1].':', Chris@0: 'code' => T_GOTO_LABEL, Chris@0: 'type' => 'T_GOTO_LABEL', Chris@0: ); Chris@0: Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: echo "\t\t* token $stackPtr changed from T_STRING to T_GOTO_LABEL".PHP_EOL; Chris@0: echo "\t\t* skipping T_COLON token ".($stackPtr + 1).PHP_EOL; Chris@0: } Chris@0: Chris@0: $newStackPtr++; Chris@0: $stackPtr++; Chris@0: continue; Chris@0: } Chris@0: }//end if Chris@0: Chris@0: /* Chris@0: HHVM 3.5 tokenizes "else[\s]+if" as a T_ELSEIF token while PHP Chris@0: proper only tokenizes "elseif" as a T_ELSEIF token. So split Chris@0: up the HHVM token to make it looks like proper PHP. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === true Chris@0: && $token[0] === T_ELSEIF Chris@0: && strtolower($token[1]) !== 'elseif' Chris@0: ) { Chris@0: $finalTokens[$newStackPtr] = array( Chris@0: 'content' => substr($token[1], 0, 4), Chris@0: 'code' => T_ELSE, Chris@0: 'type' => 'T_ELSE', Chris@0: ); Chris@0: Chris@0: $newStackPtr++; Chris@0: $finalTokens[$newStackPtr] = array( Chris@0: 'content' => substr($token[1], 4, -2), Chris@0: 'code' => T_WHITESPACE, Chris@0: 'type' => 'T_WHITESPACE', Chris@0: ); Chris@0: Chris@0: $newStackPtr++; Chris@0: $finalTokens[$newStackPtr] = array( Chris@0: 'content' => substr($token[1], -2), Chris@0: 'code' => T_IF, Chris@0: 'type' => 'T_IF', Chris@0: ); Chris@0: Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: echo "\t\t* token $stackPtr changed from T_ELSEIF to T_ELSE/T_WHITESPACE/T_IF".PHP_EOL; Chris@0: } Chris@0: Chris@0: $newStackPtr++; Chris@0: continue; Chris@0: }//end if Chris@0: Chris@0: /* Chris@0: HHVM 3.5 and 3.6 tokenizes a hashbang line such as #!/usr/bin/php Chris@0: as T_HASHANG while PHP proper uses T_INLINE_HTML. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === true && token_name($token[0]) === 'T_HASHBANG') { Chris@0: $finalTokens[$newStackPtr] = array( Chris@0: 'content' => $token[1], Chris@0: 'code' => T_INLINE_HTML, Chris@0: 'type' => 'T_INLINE_HTML', Chris@0: ); Chris@0: Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: echo "\t\t* token $stackPtr changed from T_HASHBANG to T_INLINE_HTML".PHP_EOL; Chris@0: } Chris@0: Chris@0: $newStackPtr++; Chris@0: continue; Chris@0: }//end if Chris@0: Chris@0: /* Chris@0: If this token has newlines in its content, split each line up Chris@0: and create a new token for each line. We do this so it's easier Chris@0: to ascertain where errors occur on a line. Chris@0: Note that $token[1] is the token's content. Chris@0: */ Chris@0: Chris@0: if ($tokenIsArray === true && strpos($token[1], $eolChar) !== false) { Chris@0: $tokenLines = explode($eolChar, $token[1]); Chris@0: $numLines = count($tokenLines); Chris@0: $newToken = array( Chris@0: 'type' => token_name($token[0]), Chris@0: 'code' => $token[0], Chris@0: 'content' => '', Chris@0: ); Chris@0: Chris@0: for ($i = 0; $i < $numLines; $i++) { Chris@0: $newToken['content'] = $tokenLines[$i]; Chris@0: if ($i === ($numLines - 1)) { Chris@0: if ($tokenLines[$i] === '') { Chris@0: break; Chris@0: } Chris@0: } else { Chris@0: $newToken['content'] .= $eolChar; Chris@0: } Chris@0: Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: $newStackPtr++; Chris@0: } Chris@0: } else { Chris@0: if ($tokenIsArray === true && $token[0] === T_STRING) { Chris@0: // Some T_STRING tokens should remain that way Chris@0: // due to their context. Chris@0: $context = array( Chris@0: T_OBJECT_OPERATOR => true, Chris@0: T_FUNCTION => true, Chris@0: T_CLASS => true, Chris@0: T_EXTENDS => true, Chris@0: T_IMPLEMENTS => true, Chris@0: T_NEW => true, Chris@0: T_CONST => true, Chris@0: T_NS_SEPARATOR => true, Chris@0: T_USE => true, Chris@0: T_NAMESPACE => true, Chris@0: T_PAAMAYIM_NEKUDOTAYIM => true, Chris@0: ); Chris@0: if (isset($context[$finalTokens[$lastNotEmptyToken]['code']]) === true) { Chris@0: // Special case for syntax like: return new self Chris@0: // where self should not be a string. Chris@0: if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW Chris@0: && strtolower($token[1]) === 'self' Chris@0: ) { Chris@0: $finalTokens[$newStackPtr] = array( Chris@0: 'content' => $token[1], Chris@0: 'code' => T_SELF, Chris@0: 'type' => 'T_SELF', Chris@0: ); Chris@0: } else { Chris@0: $finalTokens[$newStackPtr] = array( Chris@0: 'content' => $token[1], Chris@0: 'code' => T_STRING, Chris@0: 'type' => 'T_STRING', Chris@0: ); Chris@0: } Chris@0: Chris@0: $newStackPtr++; Chris@0: continue; Chris@0: }//end if Chris@0: }//end if Chris@0: Chris@0: $newToken = null; Chris@0: if ($tokenIsArray === false) { Chris@0: if (isset(self::$_resolveTokenCache[$token[0]]) === true) { Chris@0: $newToken = self::$_resolveTokenCache[$token[0]]; Chris@0: } Chris@0: } else { Chris@0: $cacheKey = null; Chris@0: if ($token[0] === T_STRING) { Chris@0: $cacheKey = strtolower($token[1]); Chris@0: } else if ($token[0] !== T_CURLY_OPEN) { Chris@0: $cacheKey = $token[0]; Chris@0: } Chris@0: Chris@0: if ($cacheKey !== null && isset(self::$_resolveTokenCache[$cacheKey]) === true) { Chris@0: $newToken = self::$_resolveTokenCache[$cacheKey]; Chris@0: $newToken['content'] = $token[1]; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($newToken === null) { Chris@0: $newToken = self::standardiseToken($token); Chris@0: } Chris@0: Chris@0: // Convert colons that are actually the ELSE component of an Chris@0: // inline IF statement. Chris@0: if (empty($insideInlineIf) === false && $newToken['code'] === T_COLON) { Chris@0: array_pop($insideInlineIf); Chris@0: $newToken['code'] = T_INLINE_ELSE; Chris@0: $newToken['type'] = 'T_INLINE_ELSE'; Chris@0: } Chris@0: Chris@0: // This is a special condition for T_ARRAY tokens used for Chris@0: // type hinting function arguments as being arrays. We want to keep Chris@0: // the parenthesis map clean, so let's tag these tokens as Chris@0: // T_ARRAY_HINT. Chris@0: if ($newToken['code'] === T_ARRAY) { Chris@0: for ($i = $stackPtr; $i < $numTokens; $i++) { Chris@0: if ($tokens[$i] === '(') { Chris@0: break; Chris@0: } else if ($tokens[$i][0] === T_VARIABLE) { Chris@0: $newToken['code'] = T_ARRAY_HINT; Chris@0: $newToken['type'] = 'T_ARRAY_HINT'; Chris@0: break; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: // This is a special case when checking PHP 5.5+ code in PHP < 5.5 Chris@0: // where "finally" should be T_FINALLY instead of T_STRING. Chris@0: if ($newToken['code'] === T_STRING Chris@0: && strtolower($newToken['content']) === 'finally' Chris@0: ) { Chris@0: $newToken['code'] = T_FINALLY; Chris@0: $newToken['type'] = 'T_FINALLY'; Chris@0: } Chris@0: Chris@0: // This is a special case for the PHP 5.5 classname::class syntax Chris@0: // where "class" should be T_STRING instead of T_CLASS. Chris@0: if (($newToken['code'] === T_CLASS Chris@0: || $newToken['code'] === T_FUNCTION) Chris@0: && $finalTokens[($newStackPtr - 1)]['code'] === T_DOUBLE_COLON Chris@0: ) { Chris@0: $newToken['code'] = T_STRING; Chris@0: $newToken['type'] = 'T_STRING'; Chris@0: } Chris@0: Chris@0: // This is a special case for PHP 5.6 use function and use const Chris@0: // where "function" and "const" should be T_STRING instead of T_FUNCTION Chris@0: // and T_CONST. Chris@0: if (($newToken['code'] === T_FUNCTION Chris@0: || $newToken['code'] === T_CONST) Chris@0: && $finalTokens[$lastNotEmptyToken]['code'] === T_USE Chris@0: ) { Chris@0: $newToken['code'] = T_STRING; Chris@0: $newToken['type'] = 'T_STRING'; Chris@0: } Chris@0: Chris@0: // This is a special case for use groups in PHP 7+ where leaving Chris@0: // the curly braces as their normal tokens would confuse Chris@0: // the scope map and sniffs. Chris@0: if ($newToken['code'] === T_OPEN_CURLY_BRACKET Chris@0: && $finalTokens[$lastNotEmptyToken]['code'] === T_NS_SEPARATOR Chris@0: ) { Chris@0: $newToken['code'] = T_OPEN_USE_GROUP; Chris@0: $newToken['type'] = 'T_OPEN_USE_GROUP'; Chris@0: $insideUseGroup = true; Chris@0: } Chris@0: Chris@0: if ($insideUseGroup === true && $newToken['code'] === T_CLOSE_CURLY_BRACKET) { Chris@0: $newToken['code'] = T_CLOSE_USE_GROUP; Chris@0: $newToken['type'] = 'T_CLOSE_USE_GROUP'; Chris@0: $insideUseGroup = false; Chris@0: } Chris@0: Chris@0: $finalTokens[$newStackPtr] = $newToken; Chris@0: $newStackPtr++; Chris@0: }//end if Chris@0: }//end for Chris@0: Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: echo "\t*** END PHP TOKENIZING ***".PHP_EOL; Chris@0: } Chris@0: Chris@0: return $finalTokens; Chris@0: Chris@0: }//end tokenizeString() Chris@0: Chris@0: Chris@0: /** Chris@0: * Performs additional processing after main tokenizing. Chris@0: * Chris@0: * This additional processing checks for CASE statements that are using curly Chris@0: * braces for scope openers and closers. It also turns some T_FUNCTION tokens Chris@0: * into T_CLOSURE when they are not standard function definitions. It also Chris@0: * detects short array syntax and converts those square brackets into new tokens. Chris@0: * It also corrects some usage of the static and class keywords. It also Chris@0: * assigns tokens to function return types. Chris@0: * Chris@0: * @param array $tokens The array of tokens to process. Chris@0: * @param string $eolChar The EOL character to use for splitting strings. Chris@0: * Chris@0: * @return void Chris@0: */ Chris@0: public function processAdditional(&$tokens, $eolChar) Chris@0: { Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: echo "\t*** START ADDITIONAL PHP PROCESSING ***".PHP_EOL; Chris@0: } Chris@0: Chris@0: $numTokens = count($tokens); Chris@0: for ($i = ($numTokens - 1); $i >= 0; $i--) { Chris@0: // Check for any unset scope conditions due to alternate IF/ENDIF syntax. Chris@0: if (isset($tokens[$i]['scope_opener']) === true Chris@0: && isset($tokens[$i]['scope_condition']) === false Chris@0: ) { Chris@0: $tokens[$i]['scope_condition'] = $tokens[$tokens[$i]['scope_opener']]['scope_condition']; Chris@0: } Chris@0: Chris@0: if ($tokens[$i]['code'] === T_FUNCTION) { Chris@0: /* Chris@0: Detect functions that are actually closures and Chris@0: assign them a different token. Chris@0: */ Chris@0: Chris@0: if (isset($tokens[$i]['scope_opener']) === true) { Chris@0: for ($x = ($i + 1); $x < $numTokens; $x++) { Chris@0: if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false Chris@0: && $tokens[$x]['code'] !== T_BITWISE_AND Chris@0: ) { Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($tokens[$x]['code'] === T_OPEN_PARENTHESIS) { Chris@0: $tokens[$i]['code'] = T_CLOSURE; Chris@0: $tokens[$i]['type'] = 'T_CLOSURE'; Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $line = $tokens[$i]['line']; Chris@0: echo "\t* token $i on line $line changed from T_FUNCTION to T_CLOSURE".PHP_EOL; Chris@0: } Chris@0: Chris@0: for ($x = ($tokens[$i]['scope_opener'] + 1); $x < $tokens[$i]['scope_closer']; $x++) { Chris@0: if (isset($tokens[$x]['conditions'][$i]) === false) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: $tokens[$x]['conditions'][$i] = T_CLOSURE; Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $type = $tokens[$x]['type']; Chris@0: echo "\t\t* cleaned $x ($type) *".PHP_EOL; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: $tokenAfterReturnTypeHint = $tokens[$i]['scope_opener']; Chris@0: } else if (isset($tokens[$i]['parenthesis_closer']) === true) { Chris@0: $tokenAfterReturnTypeHint = null; Chris@0: for ($x = ($tokens[$i]['parenthesis_closer'] + 1); $x < $numTokens; $x++) { Chris@0: if ($tokens[$x]['code'] === T_SEMICOLON) { Chris@0: $tokenAfterReturnTypeHint = $x; Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($tokenAfterReturnTypeHint === null) { Chris@0: // Probably a syntax error. Chris@0: continue; Chris@0: } Chris@0: } else { Chris@0: // Probably a syntax error. Chris@0: continue; Chris@0: }//end if Chris@0: Chris@0: /* Chris@0: Detect function return values and assign them Chris@0: a special token, because PHP doesn't. Chris@0: */ Chris@0: Chris@0: for ($x = ($tokenAfterReturnTypeHint - 1); $x > $i; $x--) { Chris@0: if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) { Chris@0: if (in_array($tokens[$x]['code'], array(T_STRING, T_ARRAY, T_ARRAY_HINT, T_CALLABLE, T_SELF, T_PARENT), true) === true) { Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $line = $tokens[$x]['line']; Chris@0: $type = $tokens[$x]['type']; Chris@0: echo "\t* token $x on line $line changed from $type to T_RETURN_TYPE".PHP_EOL; Chris@0: } Chris@0: Chris@0: $tokens[$x]['code'] = T_RETURN_TYPE; Chris@0: $tokens[$x]['type'] = 'T_RETURN_TYPE'; Chris@0: } Chris@0: Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: continue; Chris@0: } else if ($tokens[$i]['code'] === T_CLASS && isset($tokens[$i]['scope_opener']) === true) { Chris@0: /* Chris@0: Detect anonymous classes and assign them a different token. Chris@0: */ Chris@0: Chris@0: for ($x = ($i + 1); $x < $numTokens; $x++) { Chris@0: if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) { Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($tokens[$x]['code'] === T_OPEN_PARENTHESIS Chris@0: || $tokens[$x]['code'] === T_OPEN_CURLY_BRACKET Chris@0: || $tokens[$x]['code'] === T_EXTENDS Chris@0: || $tokens[$x]['code'] === T_IMPLEMENTS Chris@0: ) { Chris@0: $tokens[$i]['code'] = T_ANON_CLASS; Chris@0: $tokens[$i]['type'] = 'T_ANON_CLASS'; Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $line = $tokens[$i]['line']; Chris@0: echo "\t* token $i on line $line changed from T_CLASS to T_ANON_CLASS".PHP_EOL; Chris@0: } Chris@0: Chris@0: for ($x = ($tokens[$i]['scope_opener'] + 1); $x < $tokens[$i]['scope_closer']; $x++) { Chris@0: if (isset($tokens[$x]['conditions'][$i]) === false) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: $tokens[$x]['conditions'][$i] = T_ANON_CLASS; Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $type = $tokens[$x]['type']; Chris@0: echo "\t\t* cleaned $x ($type) *".PHP_EOL; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: continue; Chris@0: } else if ($tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET) { Chris@0: if (isset($tokens[$i]['bracket_closer']) === false) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: // Unless there is a variable or a bracket before this token, Chris@0: // it is the start of an array being defined using the short syntax. Chris@0: $isShortArray = false; Chris@0: $allowed = array( Chris@0: T_CLOSE_SQUARE_BRACKET => T_CLOSE_SQUARE_BRACKET, Chris@0: T_CLOSE_PARENTHESIS => T_CLOSE_PARENTHESIS, Chris@0: T_VARIABLE => T_VARIABLE, Chris@0: T_OBJECT_OPERATOR => T_OBJECT_OPERATOR, Chris@0: T_STRING => T_STRING, Chris@0: ); Chris@0: Chris@0: for ($x = ($i - 1); $x > 0; $x--) { Chris@0: // If we hit a scope opener, the statement has ended Chris@0: // without finding anything, so it's probably an array Chris@0: // using PHP 7.1 short list syntax. Chris@0: if (isset($tokens[$x]['scope_opener']) === true) { Chris@0: $isShortArray = true; Chris@0: break; Chris@0: } Chris@0: Chris@0: if (isset($tokens[$x]['bracket_opener']) === true Chris@0: && $x > $tokens[$x]['bracket_opener'] Chris@0: ) { Chris@0: $x = $tokens[$x]['bracket_opener']; Chris@0: continue; Chris@0: } Chris@0: Chris@0: if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) { Chris@0: if (isset($allowed[$tokens[$x]['code']]) === false) { Chris@0: $isShortArray = true; Chris@0: } Chris@0: Chris@0: break; Chris@0: } Chris@0: }//end for Chris@0: Chris@0: if ($isShortArray === true) { Chris@0: $tokens[$i]['code'] = T_OPEN_SHORT_ARRAY; Chris@0: $tokens[$i]['type'] = 'T_OPEN_SHORT_ARRAY'; Chris@0: Chris@0: $closer = $tokens[$i]['bracket_closer']; Chris@0: $tokens[$closer]['code'] = T_CLOSE_SHORT_ARRAY; Chris@0: $tokens[$closer]['type'] = 'T_CLOSE_SHORT_ARRAY'; Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $line = $tokens[$i]['line']; Chris@0: echo "\t* token $i on line $line changed from T_OPEN_SQUARE_BRACKET to T_OPEN_SHORT_ARRAY".PHP_EOL; Chris@0: $line = $tokens[$closer]['line']; Chris@0: echo "\t* token $closer on line $line changed from T_CLOSE_SQUARE_BRACKET to T_CLOSE_SHORT_ARRAY".PHP_EOL; Chris@0: } Chris@0: } Chris@0: Chris@0: continue; Chris@0: } else if ($tokens[$i]['code'] === T_STATIC) { Chris@0: for ($x = ($i - 1); $x > 0; $x--) { Chris@0: if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) { Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($tokens[$x]['code'] === T_INSTANCEOF) { Chris@0: $tokens[$i]['code'] = T_STRING; Chris@0: $tokens[$i]['type'] = 'T_STRING'; Chris@0: Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $line = $tokens[$i]['line']; Chris@0: echo "\t* token $i on line $line changed from T_STATIC to T_STRING".PHP_EOL; Chris@0: } Chris@0: } Chris@0: Chris@0: continue; Chris@0: } else if ($tokens[$i]['code'] === T_ECHO && $tokens[$i]['content'] === ' 1) { Chris@0: $line = $tokens[$i]['line']; Chris@0: echo "\t* token $i on line $line changed from T_ECHO to T_OPEN_TAG_WITH_ECHO".PHP_EOL; Chris@0: } Chris@0: } else if ($tokens[$i]['code'] === T_TRUE Chris@0: || $tokens[$i]['code'] === T_FALSE Chris@0: || $tokens[$i]['code'] === T_NULL Chris@0: ) { Chris@0: for ($x = ($i + 1); $i < $numTokens; $x++) { Chris@0: if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) { Chris@0: // Non-whitespace content. Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: $context = array( Chris@0: T_OBJECT_OPERATOR => true, Chris@0: T_NS_SEPARATOR => true, Chris@0: T_PAAMAYIM_NEKUDOTAYIM => true, Chris@0: ); Chris@0: if (isset($context[$tokens[$x]['code']]) === true) { Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $line = $tokens[$i]['line']; Chris@0: $type = $tokens[$i]['type']; Chris@0: echo "\t* token $i on line $line changed from $type to T_STRING".PHP_EOL; Chris@0: } Chris@0: Chris@0: $tokens[$i]['code'] = T_STRING; Chris@0: $tokens[$i]['type'] = 'T_STRING'; Chris@0: } Chris@0: } else if ($tokens[$i]['code'] === T_CONST) { Chris@0: // Context sensitive keywords support. Chris@0: for ($x = ($i + 1); $i < $numTokens; $x++) { Chris@0: if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) { Chris@0: // Non-whitespace content. Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($tokens[$x]['code'] !== T_STRING) { Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $line = $tokens[$x]['line']; Chris@0: $type = $tokens[$x]['type']; Chris@0: echo "\t* token $x on line $line changed from $type to T_STRING".PHP_EOL; Chris@0: } Chris@0: Chris@0: $tokens[$x]['code'] = T_STRING; Chris@0: $tokens[$x]['type'] = 'T_STRING'; Chris@0: } Chris@0: }//end if Chris@0: Chris@0: if (($tokens[$i]['code'] !== T_CASE Chris@0: && $tokens[$i]['code'] !== T_DEFAULT) Chris@0: || isset($tokens[$i]['scope_opener']) === false Chris@0: ) { Chris@0: // Only interested in CASE and DEFAULT statements from here on in. Chris@0: continue; Chris@0: } Chris@0: Chris@0: $scopeOpener = $tokens[$i]['scope_opener']; Chris@0: $scopeCloser = $tokens[$i]['scope_closer']; Chris@0: Chris@0: // If the first char after the opener is a curly brace Chris@0: // and that brace has been ignored, it is actually Chris@0: // opening this case statement and the opener and closer are Chris@0: // probably set incorrectly. Chris@0: for ($x = ($scopeOpener + 1); $x < $numTokens; $x++) { Chris@0: if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) { Chris@0: // Non-whitespace content. Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($tokens[$x]['code'] === T_CASE || $tokens[$x]['code'] === T_DEFAULT) { Chris@0: // Special case for multiple CASE statements that share the same Chris@0: // closer. Because we are going backwards through the file, this next Chris@0: // CASE/DEFAULT statement is already fixed, so just use its closer Chris@0: // and don't worry about fixing anything. Chris@0: $newCloser = $tokens[$x]['scope_closer']; Chris@0: $tokens[$i]['scope_closer'] = $newCloser; Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $oldType = $tokens[$scopeCloser]['type']; Chris@0: $newType = $tokens[$newCloser]['type']; Chris@0: $line = $tokens[$i]['line']; Chris@0: echo "\t* token $i (T_CASE) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL; Chris@0: } Chris@0: Chris@0: continue; Chris@0: } Chris@0: Chris@0: if ($tokens[$x]['code'] !== T_OPEN_CURLY_BRACKET Chris@0: || isset($tokens[$x]['scope_condition']) === true Chris@0: ) { Chris@0: // Not a CASE/DEFAULT with a curly brace opener. Chris@0: continue; Chris@0: } Chris@0: Chris@0: // The closer for this CASE/DEFAULT should be the closing curly brace and Chris@0: // not whatever it already is. The opener needs to be the opening curly Chris@0: // brace so everything matches up. Chris@0: $newCloser = $tokens[$x]['bracket_closer']; Chris@0: foreach (array($i, $x, $newCloser) as $index) { Chris@0: $tokens[$index]['scope_condition'] = $i; Chris@0: $tokens[$index]['scope_opener'] = $x; Chris@0: $tokens[$index]['scope_closer'] = $newCloser; Chris@0: } Chris@0: Chris@0: unset($tokens[$scopeOpener]['scope_condition']); Chris@0: unset($tokens[$scopeOpener]['scope_opener']); Chris@0: unset($tokens[$scopeOpener]['scope_closer']); Chris@0: unset($tokens[$scopeCloser]['scope_condition']); Chris@0: unset($tokens[$scopeCloser]['scope_opener']); Chris@0: unset($tokens[$scopeCloser]['scope_closer']); Chris@0: unset($tokens[$x]['bracket_opener']); Chris@0: unset($tokens[$x]['bracket_closer']); Chris@0: unset($tokens[$newCloser]['bracket_opener']); Chris@0: unset($tokens[$newCloser]['bracket_closer']); Chris@0: $tokens[$scopeCloser]['conditions'][] = $i; Chris@0: Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $line = $tokens[$i]['line']; Chris@0: $tokenType = $tokens[$i]['type']; Chris@0: Chris@0: $oldType = $tokens[$scopeOpener]['type']; Chris@0: $newType = $tokens[$x]['type']; Chris@0: echo "\t* token $i ($tokenType) on line $line opener changed from $scopeOpener ($oldType) to $x ($newType)".PHP_EOL; Chris@0: Chris@0: $oldType = $tokens[$scopeCloser]['type']; Chris@0: $newType = $tokens[$newCloser]['type']; Chris@0: echo "\t* token $i ($tokenType) on line $line closer changed from $scopeCloser ($oldType) to $newCloser ($newType)".PHP_EOL; Chris@0: } Chris@0: Chris@0: // Now fix up all the tokens that think they are Chris@0: // inside the CASE/DEFAULT statement when they are really outside. Chris@0: for ($x = $newCloser; $x < $scopeCloser; $x++) { Chris@0: foreach ($tokens[$x]['conditions'] as $num => $oldCond) { Chris@0: if ($oldCond === $tokens[$i]['code']) { Chris@0: $oldConditions = $tokens[$x]['conditions']; Chris@0: unset($tokens[$x]['conditions'][$num]); Chris@0: Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: $type = $tokens[$x]['type']; Chris@0: $oldConds = ''; Chris@0: foreach ($oldConditions as $condition) { Chris@0: $oldConds .= token_name($condition).','; Chris@0: } Chris@0: Chris@0: $oldConds = rtrim($oldConds, ','); Chris@0: Chris@0: $newConds = ''; Chris@0: foreach ($tokens[$x]['conditions'] as $condition) { Chris@0: $newConds .= token_name($condition).','; Chris@0: } Chris@0: Chris@0: $newConds = rtrim($newConds, ','); Chris@0: Chris@0: echo "\t\t* cleaned $x ($type) *".PHP_EOL; Chris@0: echo "\t\t\t=> conditions changed from $oldConds to $newConds".PHP_EOL; Chris@0: } Chris@0: Chris@0: break; Chris@0: }//end if Chris@0: }//end foreach Chris@0: }//end for Chris@0: }//end for Chris@0: Chris@0: if (PHP_CODESNIFFER_VERBOSITY > 1) { Chris@0: echo "\t*** END ADDITIONAL PHP PROCESSING ***".PHP_EOL; Chris@0: } Chris@0: Chris@0: }//end processAdditional() Chris@0: Chris@0: Chris@0: /** Chris@0: * Takes a token produced from token_get_all() and produces a Chris@0: * more uniform token. Chris@0: * Chris@0: * @param string|array $token The token to convert. Chris@0: * Chris@0: * @return array The new token. Chris@0: */ Chris@0: public static function standardiseToken($token) Chris@0: { Chris@0: if (isset($token[1]) === false) { Chris@0: if (isset(self::$_resolveTokenCache[$token[0]]) === true) { Chris@0: return self::$_resolveTokenCache[$token[0]]; Chris@0: } Chris@0: } else { Chris@0: $cacheKey = null; Chris@0: if ($token[0] === T_STRING) { Chris@0: $cacheKey = strtolower($token[1]); Chris@0: } else if ($token[0] !== T_CURLY_OPEN) { Chris@0: $cacheKey = $token[0]; Chris@0: } Chris@0: Chris@0: if ($cacheKey !== null && isset(self::$_resolveTokenCache[$cacheKey]) === true) { Chris@0: $newToken = self::$_resolveTokenCache[$cacheKey]; Chris@0: $newToken['content'] = $token[1]; Chris@0: return $newToken; Chris@0: } Chris@0: } Chris@0: Chris@0: if (isset($token[1]) === false) { Chris@0: return self::resolveSimpleToken($token[0]); Chris@0: } Chris@0: Chris@0: if ($token[0] === T_STRING) { Chris@0: switch ($cacheKey) { Chris@0: case 'false': Chris@0: $newToken['type'] = 'T_FALSE'; Chris@0: break; Chris@0: case 'true': Chris@0: $newToken['type'] = 'T_TRUE'; Chris@0: break; Chris@0: case 'null': Chris@0: $newToken['type'] = 'T_NULL'; Chris@0: break; Chris@0: case 'self': Chris@0: $newToken['type'] = 'T_SELF'; Chris@0: break; Chris@0: case 'parent': Chris@0: $newToken['type'] = 'T_PARENT'; Chris@0: break; Chris@0: default: Chris@0: $newToken['type'] = 'T_STRING'; Chris@0: break; Chris@0: } Chris@0: Chris@0: $newToken['code'] = constant($newToken['type']); Chris@0: Chris@0: self::$_resolveTokenCache[$cacheKey] = $newToken; Chris@0: } else if ($token[0] === T_CURLY_OPEN) { Chris@0: $newToken = array( Chris@0: 'code' => T_OPEN_CURLY_BRACKET, Chris@0: 'type' => 'T_OPEN_CURLY_BRACKET', Chris@0: ); Chris@0: } else { Chris@0: $newToken = array( Chris@0: 'code' => $token[0], Chris@0: 'type' => token_name($token[0]), Chris@0: ); Chris@0: Chris@0: self::$_resolveTokenCache[$token[0]] = $newToken; Chris@0: }//end if Chris@0: Chris@0: $newToken['content'] = $token[1]; Chris@0: return $newToken; Chris@0: Chris@0: }//end standardiseToken() Chris@0: Chris@0: Chris@0: /** Chris@0: * Converts simple tokens into a format that conforms to complex tokens Chris@0: * produced by token_get_all(). Chris@0: * Chris@0: * Simple tokens are tokens that are not in array form when produced from Chris@0: * token_get_all(). Chris@0: * Chris@0: * @param string $token The simple token to convert. Chris@0: * Chris@0: * @return array The new token in array format. Chris@0: */ Chris@0: public static function resolveSimpleToken($token) Chris@0: { Chris@0: $newToken = array(); Chris@0: Chris@0: switch ($token) { Chris@0: case '{': Chris@0: $newToken['type'] = 'T_OPEN_CURLY_BRACKET'; Chris@0: break; Chris@0: case '}': Chris@0: $newToken['type'] = 'T_CLOSE_CURLY_BRACKET'; Chris@0: break; Chris@0: case '[': Chris@0: $newToken['type'] = 'T_OPEN_SQUARE_BRACKET'; Chris@0: break; Chris@0: case ']': Chris@0: $newToken['type'] = 'T_CLOSE_SQUARE_BRACKET'; Chris@0: break; Chris@0: case '(': Chris@0: $newToken['type'] = 'T_OPEN_PARENTHESIS'; Chris@0: break; Chris@0: case ')': Chris@0: $newToken['type'] = 'T_CLOSE_PARENTHESIS'; Chris@0: break; Chris@0: case ':': Chris@0: $newToken['type'] = 'T_COLON'; Chris@0: break; Chris@0: case '.': Chris@0: $newToken['type'] = 'T_STRING_CONCAT'; Chris@0: break; Chris@0: case ';': Chris@0: $newToken['type'] = 'T_SEMICOLON'; Chris@0: break; Chris@0: case '=': Chris@0: $newToken['type'] = 'T_EQUAL'; Chris@0: break; Chris@0: case '*': Chris@0: $newToken['type'] = 'T_MULTIPLY'; Chris@0: break; Chris@0: case '/': Chris@0: $newToken['type'] = 'T_DIVIDE'; Chris@0: break; Chris@0: case '+': Chris@0: $newToken['type'] = 'T_PLUS'; Chris@0: break; Chris@0: case '-': Chris@0: $newToken['type'] = 'T_MINUS'; Chris@0: break; Chris@0: case '%': Chris@0: $newToken['type'] = 'T_MODULUS'; Chris@0: break; Chris@0: case '^': Chris@0: $newToken['type'] = 'T_BITWISE_XOR'; Chris@0: break; Chris@0: case '&': Chris@0: $newToken['type'] = 'T_BITWISE_AND'; Chris@0: break; Chris@0: case '|': Chris@0: $newToken['type'] = 'T_BITWISE_OR'; Chris@0: break; Chris@0: case '<': Chris@0: $newToken['type'] = 'T_LESS_THAN'; Chris@0: break; Chris@0: case '>': Chris@0: $newToken['type'] = 'T_GREATER_THAN'; Chris@0: break; Chris@0: case '!': Chris@0: $newToken['type'] = 'T_BOOLEAN_NOT'; Chris@0: break; Chris@0: case ',': Chris@0: $newToken['type'] = 'T_COMMA'; Chris@0: break; Chris@0: case '@': Chris@0: $newToken['type'] = 'T_ASPERAND'; Chris@0: break; Chris@0: case '$': Chris@0: $newToken['type'] = 'T_DOLLAR'; Chris@0: break; Chris@0: case '`': Chris@0: $newToken['type'] = 'T_BACKTICK'; Chris@0: break; Chris@0: default: Chris@0: $newToken['type'] = 'T_NONE'; Chris@0: break; Chris@0: }//end switch Chris@0: Chris@0: $newToken['code'] = constant($newToken['type']); Chris@0: $newToken['content'] = $token; Chris@0: Chris@0: self::$_resolveTokenCache[$token] = $newToken; Chris@0: return $newToken; Chris@0: Chris@0: }//end resolveSimpleToken() Chris@0: Chris@0: Chris@0: }//end class