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