Chris@0: newKeywords = array(); Chris@0: foreach ($newKeywordsPerVersion as $version => $newKeywords) { Chris@0: if (version_compare(PHP_VERSION, $version, '>=')) { Chris@0: break; Chris@0: } Chris@0: Chris@0: $this->newKeywords += $newKeywords; Chris@0: } Chris@0: Chris@0: if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) { Chris@0: return; Chris@0: } Chris@0: $this->tokenMap[self::T_COALESCE] = Tokens::T_COALESCE; Chris@0: $this->tokenMap[self::T_SPACESHIP] = Tokens::T_SPACESHIP; Chris@0: $this->tokenMap[self::T_YIELD_FROM] = Tokens::T_YIELD_FROM; Chris@0: Chris@0: if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) { Chris@0: return; Chris@0: } Chris@0: $this->tokenMap[self::T_ELLIPSIS] = Tokens::T_ELLIPSIS; Chris@0: $this->tokenMap[self::T_POW] = Tokens::T_POW; Chris@0: $this->tokenMap[self::T_POW_EQUAL] = Tokens::T_POW_EQUAL; Chris@0: } Chris@0: Chris@0: public function startLexing($code, ErrorHandler $errorHandler = null) { Chris@0: $this->inObjectAccess = false; Chris@0: Chris@0: parent::startLexing($code, $errorHandler); Chris@0: if ($this->requiresEmulation($code)) { Chris@0: $this->emulateTokens(); Chris@0: } Chris@0: } Chris@0: Chris@0: /* Chris@0: * Checks if the code is potentially using features that require emulation. Chris@0: */ Chris@0: protected function requiresEmulation($code) { Chris@0: if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) { Chris@0: return false; Chris@0: } Chris@0: Chris@0: if (preg_match('(\?\?|<=>|yield[ \n\r\t]+from)', $code)) { Chris@0: return true; Chris@0: } Chris@0: Chris@0: if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) { Chris@0: return false; Chris@0: } Chris@0: Chris@0: return preg_match('(\.\.\.|(?tokens); $i < $c; ++$i) { Chris@0: $replace = null; Chris@0: if (isset($this->tokens[$i + 1])) { Chris@0: if ($this->tokens[$i] === '?' && $this->tokens[$i + 1] === '?') { Chris@0: array_splice($this->tokens, $i, 2, array( Chris@0: array(self::T_COALESCE, '??', $line) Chris@0: )); Chris@0: $c--; Chris@0: continue; Chris@0: } Chris@0: if ($this->tokens[$i][0] === T_IS_SMALLER_OR_EQUAL Chris@0: && $this->tokens[$i + 1] === '>' Chris@0: ) { Chris@0: array_splice($this->tokens, $i, 2, array( Chris@0: array(self::T_SPACESHIP, '<=>', $line) Chris@0: )); Chris@0: $c--; Chris@0: continue; Chris@0: } Chris@0: if ($this->tokens[$i] === '*' && $this->tokens[$i + 1] === '*') { Chris@0: array_splice($this->tokens, $i, 2, array( Chris@0: array(self::T_POW, '**', $line) Chris@0: )); Chris@0: $c--; Chris@0: continue; Chris@0: } Chris@0: if ($this->tokens[$i] === '*' && $this->tokens[$i + 1][0] === T_MUL_EQUAL) { Chris@0: array_splice($this->tokens, $i, 2, array( Chris@0: array(self::T_POW_EQUAL, '**=', $line) Chris@0: )); Chris@0: $c--; Chris@0: continue; Chris@0: } Chris@0: } Chris@0: Chris@0: if (isset($this->tokens[$i + 2])) { Chris@0: if ($this->tokens[$i][0] === T_YIELD && $this->tokens[$i + 1][0] === T_WHITESPACE Chris@0: && $this->tokens[$i + 2][0] === T_STRING Chris@0: && !strcasecmp($this->tokens[$i + 2][1], 'from') Chris@0: ) { Chris@0: array_splice($this->tokens, $i, 3, array( Chris@0: array( Chris@0: self::T_YIELD_FROM, Chris@0: $this->tokens[$i][1] . $this->tokens[$i + 1][1] . $this->tokens[$i + 2][1], Chris@0: $line Chris@0: ) Chris@0: )); Chris@0: $c -= 2; Chris@0: $line += substr_count($this->tokens[$i][1], "\n"); Chris@0: continue; Chris@0: } Chris@0: if ($this->tokens[$i] === '.' && $this->tokens[$i + 1] === '.' Chris@0: && $this->tokens[$i + 2] === '.' Chris@0: ) { Chris@0: array_splice($this->tokens, $i, 3, array( Chris@0: array(self::T_ELLIPSIS, '...', $line) Chris@0: )); Chris@0: $c -= 2; Chris@0: continue; Chris@0: } Chris@0: } Chris@0: Chris@0: if (\is_array($this->tokens[$i])) { Chris@0: $line += substr_count($this->tokens[$i][1], "\n"); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) { Chris@0: $token = parent::getNextToken($value, $startAttributes, $endAttributes); Chris@0: Chris@0: // replace new keywords by their respective tokens. This is not done Chris@0: // if we currently are in an object access (e.g. in $obj->namespace Chris@0: // "namespace" stays a T_STRING tokens and isn't converted to T_NAMESPACE) Chris@0: if (Tokens::T_STRING === $token && !$this->inObjectAccess) { Chris@0: if (isset($this->newKeywords[strtolower($value)])) { Chris@0: return $this->newKeywords[strtolower($value)]; Chris@0: } Chris@0: } else { Chris@0: // keep track of whether we currently are in an object access (after ->) Chris@0: $this->inObjectAccess = Tokens::T_OBJECT_OPERATOR === $token; Chris@0: } Chris@0: Chris@0: return $token; Chris@0: } Chris@0: }