1
0
mirror of https://github.com/danog/PHP-Parser.git synced 2024-11-30 04:19:30 +01:00

Support keywords in namespaced names

This commit is contained in:
Nikita Popov 2020-08-09 21:11:49 +02:00
parent a98350581c
commit 3aadc15e2e
3 changed files with 76 additions and 3 deletions

View File

@ -15,6 +15,7 @@ class Lexer
protected $tokenMap;
protected $dropTokens;
protected $identifierTokens;
private $attributeStartLineUsed;
private $attributeEndLineUsed;
@ -37,6 +38,7 @@ class Lexer
// Create Map from internal tokens to PhpParser tokens.
$this->defineCompatibilityTokens();
$this->tokenMap = $this->createTokenMap();
$this->identifierTokens = $this->createIdentifierTokenMap();
// map of tokens to drop while lexing (the map is only used for isset lookup,
// that's why the value is simply set to 1; the value is never actually used.)
@ -170,14 +172,13 @@ class Lexer
// Emulate PHP 8 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and T_STRING
// into a single token.
// TODO: Also handle reserved keywords in namespaced names.
if (\is_array($token)
&& ($token[0] === \T_NS_SEPARATOR || $token[0] === \T_STRING || $token[0] === \T_NAMESPACE)) {
&& ($token[0] === \T_NS_SEPARATOR || isset($this->identifierTokens[$token[0]]))) {
$lastWasSeparator = $token[0] === \T_NS_SEPARATOR;
$text = $token[1];
for ($j = $i + 1; isset($this->tokens[$j]); $j++) {
if ($lastWasSeparator) {
if ($this->tokens[$j][0] !== \T_STRING) {
if (!isset($this->identifierTokens[$this->tokens[$j][0]])) {
break;
}
$lastWasSeparator = false;
@ -488,4 +489,19 @@ class Lexer
return $tokenMap;
}
private function createIdentifierTokenMap(): array {
// Based on semi_reserved production.
return array_fill_keys([
\T_STRING,
\T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND,
\T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE,
\T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH,
\T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO,
\T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT,
\T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS,
\T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN,
\T_MATCH,
], true);
}
}

View File

@ -227,6 +227,18 @@ class LexerTest extends \PHPUnit\Framework\TestCase
[Tokens::T_NS_SEPARATOR, '\\', [], []],
]
],
// tests PHP 8 T_NAME_* emulation with reserved keywords
[
'<?php fn\use \fn\use namespace\fn\use fn\use\\',
['usedAttributes' => []],
[
[Tokens::T_NAME_QUALIFIED, 'fn\use', [], []],
[Tokens::T_NAME_FULLY_QUALIFIED, '\fn\use', [], []],
[Tokens::T_NAME_RELATIVE, 'namespace\fn\use', [], []],
[Tokens::T_NAME_QUALIFIED, 'fn\use', [], []],
[Tokens::T_NS_SEPARATOR, '\\', [], []],
]
],
];
}

View File

@ -0,0 +1,45 @@
Keywords in namespaced name
-----
<?php
fn\use();
\fn\use();
namespace\fn\use();
-----
array(
0: Stmt_Expression(
expr: Expr_FuncCall(
name: Name(
parts: array(
0: fn
1: use
)
)
args: array(
)
)
)
1: Stmt_Expression(
expr: Expr_FuncCall(
name: Name_FullyQualified(
parts: array(
0: fn
1: use
)
)
args: array(
)
)
)
2: Stmt_Expression(
expr: Expr_FuncCall(
name: Name_Relative(
parts: array(
0: fn
1: use
)
)
args: array(
)
)
)
)