1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 08:12:21 +01:00

Treat namespaced names as single token

Namespace names are now lexed as single tokens of type
T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED or T_NAME_RELATIVE.

RFC: https://wiki.php.net/rfc/namespaced_names_as_token

Closes GH-5827.
This commit is contained in:
Nikita Popov
2020-06-15 11:50:55 +02:00
parent acbf7802af
commit 7a3dcc3e33
20 changed files with 182 additions and 41 deletions

View File

@@ -204,6 +204,10 @@ PHP 8.0 UPGRADE NOTES
numbers and numeric strings continue to work as before. Notably, this means
that `0 == "not-a-number"` is considered false now.
RFC: https://wiki.php.net/rfc/string_to_number_comparison
. Namespaced names can no longer contain whitespace: While `Foo\Bar` will be
recognized as a namespaced name, `Foo \ Bar` will not. Conversely, reserved
keywords are now permitted as namespace segments.
RFC: https://wiki.php.net/rfc/namespaced_names_as_token
- COM:
. Removed the ability to import case-insensitive constants from type
@@ -509,6 +513,11 @@ PHP 8.0 UPGRADE NOTES
instead be part of a following T_WHITESPACE token. It should be noted that
T_COMMENT is not always followed by whitespace, it may also be followed by
T_CLOSE_TAG or end-of-file.
. Namespaced names are now represented using the T_NAME_QUALIFIED (Foo\Bar),
T_NAME_FULLY_QUALIFIED (\Foo\Bar) and T_NAME_RELATIVE (namespace\Foo\Bar)
tokens. T_NS_SEPARATOR is only used for standalone namespace separators,
and only syntactially valid in conjunction with group use declarations.
RFC: https://wiki.php.net/rfc/namespaced_names_as_token
- XML:
. xml_parser_create(_ns) will now return an XmlParser object rather than a

View File

@@ -8,4 +8,4 @@ class A {}
?>
--EXPECTF--
Parse error: syntax error, unexpected variable "$x", expecting identifier or "static" or "namespace" or "\" in %s on line %d
Parse error: syntax error, unexpected variable "$x" in %s on line %d

View File

@@ -8,4 +8,4 @@ $foo = 'bar';
var_dump(new namespace::$foo);
?>
--EXPECTF--
Parse error: %s error%sexpecting%s"\"%sin %sbug43343.php on line 5
Parse error: syntax error, unexpected token "namespace" in %s on line %d

View File

@@ -26,7 +26,7 @@ namespace N2 {
echo $a->hello(), PHP_EOL;
echo $a->foo(), PHP_EOL;
try {
} catch(namespace \Foo $e)
} catch (namespace\Foo $e)
{
}
}

View File

@@ -5,8 +5,11 @@ Test to check regressions on T_IMPLEMENTS followed by a T_NS_SEPARATOR
interface A{}
// No longer considered legal in PHP 8.
class B implements\A {}
echo "Done", PHP_EOL;
--EXPECT--
Done
?>
--EXPECTF--
Parse error: syntax error, unexpected namespaced name "implements\A", expecting "{" in %s on line %d

View File

@@ -0,0 +1,10 @@
--TEST--
Cannot use "namespace" as namespace name, due to conflict with ns-relative names
--FILE--
<?php
namespace NAMEspace;
?>
--EXPECTF--
Fatal error: Cannot use 'NAMEspace' as namespace name in %s on line %d

View File

@@ -0,0 +1,10 @@
--TEST--
Cannot use "namespace\xyz" as namespace name, due to conflict with ns-relative names
--FILE--
<?php
namespace NAMEspace\xyz;
?>
--EXPECTF--
Parse error: syntax error, unexpected namespace-relative name "NAMEspace\xyz", expecting "{" in %s on line %d

View File

@@ -0,0 +1,37 @@
--TEST--
Reserved keywords in namespace name
--FILE--
<?php
namespace iter\fn {
function test() {
echo __FUNCTION__, "\n";
}
}
namespace fn {
function test() {
echo __FUNCTION__, "\n";
}
}
namespace self {
function test() {
echo __FUNCTION__, "\n";
}
}
namespace {
use iter\fn;
use function fn\test as test2;
use function self\test as test3;
fn\test();
test2();
test3();
}
?>
--EXPECT--
iter\fn\test
fn\test
self\test

View File

@@ -0,0 +1,10 @@
--TEST--
Whitespace between namespace separators is no longer allowed
--FILE--
<?php
Foo \ Bar \ Baz;
?>
--EXPECTF--
Parse error: syntax error, unexpected token "\" in %s on line %d

View File

@@ -7,4 +7,4 @@ use Foo\Bar\{\Baz};
?>
--EXPECTF--
Parse error: syntax error, unexpected token "\", expecting identifier or "function" or "const" in %s on line %d
Parse error: syntax error, unexpected fully qualified name "\Baz", expecting identifier or namespaced name or "function" or "const" in %s on line %d

View File

@@ -5,4 +5,4 @@ Group use declarations mustn't be empty
use Baz\{};
?>
--EXPECTF--
Parse error: syntax error, unexpected token "}", expecting identifier or "function" or "const" in %s on line %d
Parse error: syntax error, unexpected token "}", expecting identifier or namespaced name or "function" or "const" in %s on line %d

View File

@@ -5,4 +5,4 @@ Group use declarations mustn't contain just a comma
use Baz\{,};
?>
--EXPECTF--
Parse error: syntax error, unexpected token ",", expecting identifier or "function" or "const" in %s on line %d
Parse error: syntax error, unexpected token ",", expecting identifier or namespaced name or "function" or "const" in %s on line %d

View File

@@ -5,4 +5,4 @@ Group use declarations mustn't begin with a comma
use Baz\{,Foo};
?>
--EXPECTF--
Parse error: syntax error, unexpected token ",", expecting identifier or "function" or "const" in %s on line %d
Parse error: syntax error, unexpected token ",", expecting identifier or namespaced name or "function" or "const" in %s on line %d

View File

@@ -5,4 +5,4 @@ Unmixed group use declarations mustn't begin with a comma
use function Baz\{,Foo};
?>
--EXPECTF--
Parse error: syntax error, unexpected token ",", expecting identifier in %s on line %d
Parse error: syntax error, unexpected token ",", expecting identifier or namespaced name in %s on line %d

View File

@@ -1,10 +0,0 @@
--TEST--
Cannot use special class name as namespace
--FILE--
<?php
namespace self;
?>
--EXPECTF--
Fatal error: Cannot use 'self' as namespace name in %s on line %d

View File

@@ -7432,7 +7432,7 @@ void zend_compile_namespace(zend_ast *ast) /* {{{ */
if (name_ast) {
name = zend_ast_get_str(name_ast);
if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name)) {
if (zend_string_equals_literal_ci(name, "namespace")) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as namespace name", ZSTR_VAL(name));
}

View File

@@ -89,6 +89,9 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%token <ast> T_LNUMBER "integer"
%token <ast> T_DNUMBER "floating-point number"
%token <ast> T_STRING "identifier"
%token <ast> T_NAME_FULLY_QUALIFIED "fully qualified name"
%token <ast> T_NAME_RELATIVE "namespace-relative name"
%token <ast> T_NAME_QUALIFIED "namespaced name"
%token <ast> T_VARIABLE "variable"
%token <ast> T_INLINE_HTML
%token <ast> T_ENCAPSED_AND_WHITESPACE "string content"
@@ -231,7 +234,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%token T_ERROR
%type <ast> top_statement namespace_name name statement function_declaration_statement
%type <ast> class_declaration_statement trait_declaration_statement
%type <ast> class_declaration_statement trait_declaration_statement legacy_namespace_name
%type <ast> interface_declaration_statement interface_extends_list
%type <ast> group_use_declaration inline_use_declarations inline_use_declaration
%type <ast> mixed_group_use_declaration use_declaration unprefixed_use_declaration
@@ -261,7 +264,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> identifier type_expr_without_static union_type_without_static
%type <ast> inline_function union_type
%type <ast> attributed_statement attributed_class_statement attributed_parameter
%type <ast> attribute_decl attribute attributes
%type <ast> attribute_decl attribute attributes namespace_declaration_name
%type <ast> match match_arm_list non_empty_match_arm_list match_arm match_arm_cond_list
%type <num> returns_ref function fn is_reference is_variadic variable_modifiers
@@ -308,15 +311,29 @@ top_statement_list:
| %empty { $$ = zend_ast_create_list(0, ZEND_AST_STMT_LIST); }
;
/* Name usable in a namespace declaration. */
namespace_declaration_name:
identifier { $$ = $1; }
| T_NAME_QUALIFIED { $$ = $1; }
;
/* Name usable in "use" declarations (loading separator forbidden). */
namespace_name:
T_STRING { $$ = $1; }
| namespace_name T_NS_SEPARATOR T_STRING { $$ = zend_ast_append_str($1, $3); }
| T_NAME_QUALIFIED { $$ = $1; }
;
/* Name usable in "use" declarations (leading separator allowed). */
legacy_namespace_name:
namespace_name { $$ = $1; }
| T_NAME_FULLY_QUALIFIED { $$ = $1; }
;
name:
namespace_name { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; }
| T_NAMESPACE T_NS_SEPARATOR namespace_name { $$ = $3; $$->attr = ZEND_NAME_RELATIVE; }
| T_NS_SEPARATOR namespace_name { $$ = $2; $$->attr = ZEND_NAME_FQ; }
T_STRING { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; }
| T_NAME_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; }
| T_NAME_FULLY_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_FQ; }
| T_NAME_RELATIVE { $$ = $1; $$->attr = ZEND_NAME_RELATIVE; }
;
attribute_decl:
@@ -350,10 +367,10 @@ top_statement:
{ $$ = zend_ast_create(ZEND_AST_HALT_COMPILER,
zend_ast_create_zval_from_long(zend_get_scanned_file_offset()));
zend_stop_lexing(); }
| T_NAMESPACE namespace_name ';'
| T_NAMESPACE namespace_declaration_name ';'
{ $$ = zend_ast_create(ZEND_AST_NAMESPACE, $2, NULL);
RESET_DOC_COMMENT(); }
| T_NAMESPACE namespace_name { RESET_DOC_COMMENT(); }
| T_NAMESPACE namespace_declaration_name { RESET_DOC_COMMENT(); }
'{' top_statement_list '}'
{ $$ = zend_ast_create(ZEND_AST_NAMESPACE, $2, $5); }
| T_NAMESPACE { RESET_DOC_COMMENT(); }
@@ -372,17 +389,13 @@ use_type:
;
group_use_declaration:
namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations possible_comma '}'
legacy_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations possible_comma '}'
{ $$ = zend_ast_create(ZEND_AST_GROUP_USE, $1, $4); }
| T_NS_SEPARATOR namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations possible_comma '}'
{ $$ = zend_ast_create(ZEND_AST_GROUP_USE, $2, $5); }
;
mixed_group_use_declaration:
namespace_name T_NS_SEPARATOR '{' inline_use_declarations possible_comma '}'
legacy_namespace_name T_NS_SEPARATOR '{' inline_use_declarations possible_comma '}'
{ $$ = zend_ast_create(ZEND_AST_GROUP_USE, $1, $4);}
| T_NS_SEPARATOR namespace_name T_NS_SEPARATOR '{' inline_use_declarations possible_comma '}'
{ $$ = zend_ast_create(ZEND_AST_GROUP_USE, $2, $5); }
;
possible_comma:
@@ -424,8 +437,10 @@ unprefixed_use_declaration:
;
use_declaration:
unprefixed_use_declaration { $$ = $1; }
| T_NS_SEPARATOR unprefixed_use_declaration { $$ = $2; }
legacy_namespace_name
{ $$ = zend_ast_create(ZEND_AST_USE_ELEM, $1, NULL); }
| legacy_namespace_name T_AS T_STRING
{ $$ = zend_ast_create(ZEND_AST_USE_ELEM, $1, $3); }
;
const_list:

View File

@@ -1596,10 +1596,6 @@ NEWLINE ("\r"|"\n"|"\r\n")
RETURN_TOKEN(T_PAAMAYIM_NEKUDOTAYIM);
}
<ST_IN_SCRIPTING>"\\" {
RETURN_TOKEN(T_NS_SEPARATOR);
}
<ST_IN_SCRIPTING>"..." {
RETURN_TOKEN(T_ELLIPSIS);
}
@@ -2288,6 +2284,22 @@ inline_char_handler:
RETURN_TOKEN_WITH_VAL(T_ENCAPSED_AND_WHITESPACE);
}
<ST_IN_SCRIPTING>"namespace"("\\"{LABEL})+ {
RETURN_TOKEN_WITH_STR(T_NAME_RELATIVE, sizeof("namespace\\") - 1);
}
<ST_IN_SCRIPTING>{LABEL}("\\"{LABEL})+ {
RETURN_TOKEN_WITH_STR(T_NAME_QUALIFIED, 0);
}
<ST_IN_SCRIPTING>"\\"{LABEL}("\\"{LABEL})* {
RETURN_TOKEN_WITH_STR(T_NAME_FULLY_QUALIFIED, 1);
}
<ST_IN_SCRIPTING>"\\" {
RETURN_TOKEN(T_NS_SEPARATOR);
}
<ST_IN_SCRIPTING,ST_VAR_OFFSET>{LABEL} {
RETURN_TOKEN_WITH_STR(T_STRING, 0);
}

View File

@@ -0,0 +1,39 @@
--TEST--
Tokenization of namespaced names
--FILE--
<?php
$code = <<<'CODE'
<?php
Foo
Foo\Bar
\Foo\Bar
namespace\Foo
Foo \ Bar
CODE;
foreach (PhpToken::getAll($code) as $token) {
echo "{$token->getTokenName()}: \"$token->text\"\n";
}
?>
--EXPECT--
T_OPEN_TAG: "<?php
"
T_STRING: "Foo"
T_WHITESPACE: "
"
T_NAME_QUALIFIED: "Foo\Bar"
T_WHITESPACE: "
"
T_NAME_FULLY_QUALIFIED: "\Foo\Bar"
T_WHITESPACE: "
"
T_NAME_RELATIVE: "namespace\Foo"
T_WHITESPACE: "
"
T_STRING: "Foo"
T_WHITESPACE: " "
T_NS_SEPARATOR: "\"
T_WHITESPACE: " "
T_STRING: "Bar"

View File

@@ -77,6 +77,9 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) {
REGISTER_LONG_CONSTANT("T_LNUMBER", T_LNUMBER, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_DNUMBER", T_DNUMBER, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_STRING", T_STRING, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_NAME_FULLY_QUALIFIED", T_NAME_FULLY_QUALIFIED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_NAME_RELATIVE", T_NAME_RELATIVE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_NAME_QUALIFIED", T_NAME_QUALIFIED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_VARIABLE", T_VARIABLE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_INLINE_HTML", T_INLINE_HTML, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_ENCAPSED_AND_WHITESPACE", T_ENCAPSED_AND_WHITESPACE, CONST_CS | CONST_PERSISTENT);
@@ -221,6 +224,9 @@ char *get_token_type_name(int token_type)
case T_LNUMBER: return "T_LNUMBER";
case T_DNUMBER: return "T_DNUMBER";
case T_STRING: return "T_STRING";
case T_NAME_FULLY_QUALIFIED: return "T_NAME_FULLY_QUALIFIED";
case T_NAME_RELATIVE: return "T_NAME_RELATIVE";
case T_NAME_QUALIFIED: return "T_NAME_QUALIFIED";
case T_VARIABLE: return "T_VARIABLE";
case T_INLINE_HTML: return "T_INLINE_HTML";
case T_ENCAPSED_AND_WHITESPACE: return "T_ENCAPSED_AND_WHITESPACE";