Skip to content

Commit 290adc4

Browse files
committed
Introduce separate ZEND_AST_CLASS_NAME AST node
Instead of representing this as a ZEND_AST_CLASS_CONST with a "class" constant name. Class constants and ::class are unrelated features that happen to share syntax, so represent and handle them separately.
1 parent 03094c7 commit 290adc4

File tree

5 files changed

+85
-58
lines changed

5 files changed

+85
-58
lines changed

Zend/tests/class_name_of_var.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
$var::class is not supported
3+
--FILE--
4+
<?php
5+
6+
$obj = new stdClass;
7+
var_dump($obj::class);
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Cannot use ::class with dynamic class name in %s on line %d

Zend/zend_ast.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, ze
102102
return (zend_ast *) ast;
103103
}
104104

105+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class, zend_ast *name) {
106+
zend_string *name_str = zend_ast_get_str(name);
107+
if (zend_string_equals_literal_ci(name_str, "class")) {
108+
zend_string_release(name_str);
109+
return zend_ast_create(ZEND_AST_CLASS_NAME, class);
110+
} else {
111+
return zend_ast_create(ZEND_AST_CLASS_CONST, class, name);
112+
}
113+
}
114+
105115
ZEND_API zend_ast *zend_ast_create_decl(
106116
zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment,
107117
zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3
@@ -1606,6 +1616,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
16061616
smart_str_appends(str, "::");
16071617
zend_ast_export_name(str, ast->child[1], 0, indent);
16081618
break;
1619+
case ZEND_AST_CLASS_NAME:
1620+
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
1621+
smart_str_appends(str, "::class");
1622+
break;
16091623
case ZEND_AST_ASSIGN: BINARY_OP(" = ", 90, 91, 90);
16101624
case ZEND_AST_ASSIGN_REF: BINARY_OP(" =& ", 90, 91, 90);
16111625
case ZEND_AST_ASSIGN_OP:

Zend/zend_ast.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ enum _zend_ast_kind {
8787
ZEND_AST_POST_INC,
8888
ZEND_AST_POST_DEC,
8989
ZEND_AST_YIELD_FROM,
90+
ZEND_AST_CLASS_NAME,
9091

9192
ZEND_AST_GLOBAL,
9293
ZEND_AST_UNSET,
@@ -200,6 +201,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_str(zend_string *str
200201
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_long(zend_long lval);
201202

202203
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, zend_ast_attr attr);
204+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class, zend_ast *name);
203205

204206
#if ZEND_AST_SPEC
205207
# define ZEND_AST_SPEC_CALL(name, ...) \

Zend/zend_compile.c

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,21 +1366,12 @@ static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */
13661366
}
13671367
/* }}} */
13681368

1369-
static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast, zend_ast *name_ast, zend_bool constant) /* {{{ */
1369+
static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast, zend_bool constant) /* {{{ */
13701370
{
13711371
uint32_t fetch_type;
13721372

1373-
if (name_ast->kind != ZEND_AST_ZVAL) {
1374-
return 0;
1375-
}
1376-
1377-
if (!zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "class")) {
1378-
return 0;
1379-
}
1380-
13811373
if (class_ast->kind != ZEND_AST_ZVAL) {
1382-
zend_error_noreturn(E_COMPILE_ERROR,
1383-
"Dynamic class names are not allowed in compile-time ::class fetch");
1374+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use ::class with dynamic class name");
13841375
}
13851376

13861377
fetch_type = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
@@ -1390,21 +1381,18 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a
13901381
case ZEND_FETCH_CLASS_SELF:
13911382
if (CG(active_class_entry) && zend_is_scope_known()) {
13921383
ZVAL_STR_COPY(zv, CG(active_class_entry)->name);
1393-
} else {
1394-
ZVAL_NULL(zv);
1384+
return 1;
13951385
}
1396-
return 1;
1386+
return 0;
13971387
case ZEND_FETCH_CLASS_STATIC:
13981388
case ZEND_FETCH_CLASS_PARENT:
13991389
if (constant) {
14001390
zend_error_noreturn(E_COMPILE_ERROR,
14011391
"%s::class cannot be used for compile-time class name resolution",
14021392
fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent"
14031393
);
1404-
} else {
1405-
ZVAL_NULL(zv);
14061394
}
1407-
return 1;
1395+
return 0;
14081396
case ZEND_FETCH_CLASS_DEFAULT:
14091397
ZVAL_STR(zv, zend_resolve_class_name_ast(class_ast));
14101398
return 1;
@@ -7689,16 +7677,6 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
76897677
znode class_node, const_node;
76907678
zend_op *opline;
76917679

7692-
if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast, const_ast, 0)) {
7693-
if (Z_TYPE(result->u.constant) == IS_NULL) {
7694-
zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL);
7695-
opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
7696-
} else {
7697-
result->op_type = IS_CONST;
7698-
}
7699-
return;
7700-
}
7701-
77027680
zend_eval_const_expr(&ast->child[0]);
77037681
zend_eval_const_expr(&ast->child[1]);
77047682

@@ -7716,10 +7694,6 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
77167694
}
77177695
zend_string_release_ex(resolved_name, 0);
77187696
}
7719-
if (const_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(const_ast), "class")) {
7720-
zend_error_noreturn(E_COMPILE_ERROR,
7721-
"Dynamic class names are not allowed in compile-time ::class fetch");
7722-
}
77237697

77247698
zend_compile_class_ref(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
77257699

@@ -7733,6 +7707,21 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
77337707
}
77347708
/* }}} */
77357709

7710+
void zend_compile_class_name(znode *result, zend_ast *ast) /* {{{ */
7711+
{
7712+
zend_ast *class_ast = ast->child[0];
7713+
zend_op *opline;
7714+
7715+
if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast, 0)) {
7716+
result->op_type = IS_CONST;
7717+
return;
7718+
}
7719+
7720+
opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL);
7721+
opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
7722+
}
7723+
/* }}} */
7724+
77367725
void zend_compile_resolve_class_name(znode *result, zend_ast *ast) /* {{{ */
77377726
{
77387727
zend_ast *name_ast = ast->child[0];
@@ -7924,6 +7913,7 @@ zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
79247913
|| kind == ZEND_AST_CONDITIONAL || kind == ZEND_AST_DIM
79257914
|| kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM
79267915
|| kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST
7916+
|| kind == ZEND_AST_CLASS_NAME
79277917
|| kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE;
79287918
}
79297919
/* }}} */
@@ -7936,19 +7926,13 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */
79367926
zend_string *class_name;
79377927
zend_string *const_name = zend_ast_get_str(const_ast);
79387928
zend_string *name;
7939-
zval result;
79407929
int fetch_type;
79417930

79427931
if (class_ast->kind != ZEND_AST_ZVAL) {
79437932
zend_error_noreturn(E_COMPILE_ERROR,
79447933
"Dynamic class names are not allowed in compile-time class constant references");
79457934
}
79467935

7947-
if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, const_ast, 1)) {
7948-
*ast_ptr = zend_ast_create_zval(&result);
7949-
return;
7950-
}
7951-
79527936
class_name = zend_ast_get_str(class_ast);
79537937
fetch_type = zend_get_class_fetch_type(class_name);
79547938

@@ -7973,6 +7957,19 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */
79737957
}
79747958
/* }}} */
79757959

7960+
void zend_compile_const_expr_class_name(zend_ast **ast_ptr) /* {{{ */
7961+
{
7962+
zend_ast *class_ast = (*ast_ptr)->child[0];
7963+
uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
7964+
7965+
/* If we reach here, ::class should have either been constant evaluated or replaced
7966+
* by a AST_MAGIC_CONST, so only the error case is left here. */
7967+
zend_error_noreturn(E_COMPILE_ERROR,
7968+
"%s::class cannot be used for compile-time class name resolution",
7969+
fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent"
7970+
);
7971+
}
7972+
79767973
void zend_compile_const_expr_const(zend_ast **ast_ptr) /* {{{ */
79777974
{
79787975
zend_ast *ast = *ast_ptr;
@@ -8024,6 +8021,9 @@ void zend_compile_const_expr(zend_ast **ast_ptr) /* {{{ */
80248021
case ZEND_AST_CLASS_CONST:
80258022
zend_compile_const_expr_class_const(ast_ptr);
80268023
break;
8024+
case ZEND_AST_CLASS_NAME:
8025+
zend_compile_const_expr_class_name(ast_ptr);
8026+
break;
80278027
case ZEND_AST_CONST:
80288028
zend_compile_const_expr_const(ast_ptr);
80298029
break;
@@ -8312,6 +8312,9 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */
83128312
case ZEND_AST_CLASS_CONST:
83138313
zend_compile_class_const(result, ast);
83148314
return;
8315+
case ZEND_AST_CLASS_NAME:
8316+
zend_compile_class_name(result, ast);
8317+
return;
83158318
case ZEND_AST_ENCAPS_LIST:
83168319
zend_compile_encaps_list(result, ast);
83178320
return;
@@ -8605,32 +8608,16 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
86058608
}
86068609
case ZEND_AST_CLASS_CONST:
86078610
{
8608-
zend_ast *class_ast = ast->child[0];
8609-
zend_ast *name_ast = ast->child[1];
8611+
zend_ast *class_ast;
8612+
zend_ast *name_ast;
86108613
zend_string *resolved_name;
86118614

8612-
if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, name_ast, 0)) {
8613-
if (Z_TYPE(result) == IS_NULL) {
8614-
if (zend_get_class_fetch_type(zend_ast_get_str(class_ast)) == ZEND_FETCH_CLASS_SELF) {
8615-
zend_ast_destroy(ast);
8616-
*ast_ptr = zend_ast_create_ex(ZEND_AST_MAGIC_CONST, T_CLASS_C);
8617-
}
8618-
return;
8619-
}
8620-
break;
8621-
}
8622-
86238615
zend_eval_const_expr(&ast->child[0]);
86248616
zend_eval_const_expr(&ast->child[1]);
86258617

86268618
class_ast = ast->child[0];
86278619
name_ast = ast->child[1];
86288620

8629-
if (name_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "class")) {
8630-
zend_error_noreturn(E_COMPILE_ERROR,
8631-
"Dynamic class names are not allowed in compile-time ::class fetch");
8632-
}
8633-
86348621
if (class_ast->kind != ZEND_AST_ZVAL || name_ast->kind != ZEND_AST_ZVAL) {
86358622
return;
86368623
}
@@ -8645,7 +8632,20 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
86458632
zend_string_release_ex(resolved_name, 0);
86468633
break;
86478634
}
8648-
8635+
case ZEND_AST_CLASS_NAME:
8636+
{
8637+
zend_ast *class_ast = ast->child[0];
8638+
if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, 0)) {
8639+
break;
8640+
}
8641+
/* TODO We should not use AST_MAGIC_CONST for this, because the semantics are slightly
8642+
* different. */
8643+
if (zend_get_class_fetch_type(zend_ast_get_str(class_ast)) == ZEND_FETCH_CLASS_SELF) {
8644+
zend_ast_destroy(ast);
8645+
*ast_ptr = zend_ast_create_ex(ZEND_AST_MAGIC_CONST, T_CLASS_C);
8646+
}
8647+
return;
8648+
}
86498649
default:
86508650
return;
86518651
}

Zend/zend_language_parser.y

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,9 +1095,9 @@ scalar:
10951095
constant:
10961096
name { $$ = zend_ast_create(ZEND_AST_CONST, $1); }
10971097
| class_name T_PAAMAYIM_NEKUDOTAYIM identifier
1098-
{ $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $3); }
1098+
{ $$ = zend_ast_create_class_const_or_name($1, $3); }
10991099
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM identifier
1100-
{ $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $3); }
1100+
{ $$ = zend_ast_create_class_const_or_name($1, $3); }
11011101
;
11021102

11031103
optional_expr:

0 commit comments

Comments
 (0)