mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
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.
This commit is contained in:
parent
03094c7af3
commit
290adc4132
11
Zend/tests/class_name_of_var.phpt
Normal file
11
Zend/tests/class_name_of_var.phpt
Normal file
@ -0,0 +1,11 @@
|
||||
--TEST--
|
||||
$var::class is not supported
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$obj = new stdClass;
|
||||
var_dump($obj::class);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Cannot use ::class with dynamic class name in %s on line %d
|
@ -102,6 +102,16 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, ze
|
||||
return (zend_ast *) ast;
|
||||
}
|
||||
|
||||
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class, zend_ast *name) {
|
||||
zend_string *name_str = zend_ast_get_str(name);
|
||||
if (zend_string_equals_literal_ci(name_str, "class")) {
|
||||
zend_string_release(name_str);
|
||||
return zend_ast_create(ZEND_AST_CLASS_NAME, class);
|
||||
} else {
|
||||
return zend_ast_create(ZEND_AST_CLASS_CONST, class, name);
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API zend_ast *zend_ast_create_decl(
|
||||
zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment,
|
||||
zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3
|
||||
@ -1606,6 +1616,10 @@ simple_list:
|
||||
smart_str_appends(str, "::");
|
||||
zend_ast_export_name(str, ast->child[1], 0, indent);
|
||||
break;
|
||||
case ZEND_AST_CLASS_NAME:
|
||||
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
|
||||
smart_str_appends(str, "::class");
|
||||
break;
|
||||
case ZEND_AST_ASSIGN: BINARY_OP(" = ", 90, 91, 90);
|
||||
case ZEND_AST_ASSIGN_REF: BINARY_OP(" =& ", 90, 91, 90);
|
||||
case ZEND_AST_ASSIGN_OP:
|
||||
|
@ -87,6 +87,7 @@ enum _zend_ast_kind {
|
||||
ZEND_AST_POST_INC,
|
||||
ZEND_AST_POST_DEC,
|
||||
ZEND_AST_YIELD_FROM,
|
||||
ZEND_AST_CLASS_NAME,
|
||||
|
||||
ZEND_AST_GLOBAL,
|
||||
ZEND_AST_UNSET,
|
||||
@ -200,6 +201,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_str(zend_string *str
|
||||
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_long(zend_long lval);
|
||||
|
||||
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, zend_ast_attr attr);
|
||||
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class, zend_ast *name);
|
||||
|
||||
#if ZEND_AST_SPEC
|
||||
# define ZEND_AST_SPEC_CALL(name, ...) \
|
||||
|
@ -1366,21 +1366,12 @@ static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast, zend_ast *name_ast, zend_bool constant) /* {{{ */
|
||||
static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast, zend_bool constant) /* {{{ */
|
||||
{
|
||||
uint32_t fetch_type;
|
||||
|
||||
if (name_ast->kind != ZEND_AST_ZVAL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "class")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (class_ast->kind != ZEND_AST_ZVAL) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Dynamic class names are not allowed in compile-time ::class fetch");
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use ::class with dynamic class name");
|
||||
}
|
||||
|
||||
fetch_type = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
|
||||
@ -1390,10 +1381,9 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a
|
||||
case ZEND_FETCH_CLASS_SELF:
|
||||
if (CG(active_class_entry) && zend_is_scope_known()) {
|
||||
ZVAL_STR_COPY(zv, CG(active_class_entry)->name);
|
||||
} else {
|
||||
ZVAL_NULL(zv);
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
return 0;
|
||||
case ZEND_FETCH_CLASS_STATIC:
|
||||
case ZEND_FETCH_CLASS_PARENT:
|
||||
if (constant) {
|
||||
@ -1401,10 +1391,8 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a
|
||||
"%s::class cannot be used for compile-time class name resolution",
|
||||
fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent"
|
||||
);
|
||||
} else {
|
||||
ZVAL_NULL(zv);
|
||||
}
|
||||
return 1;
|
||||
return 0;
|
||||
case ZEND_FETCH_CLASS_DEFAULT:
|
||||
ZVAL_STR(zv, zend_resolve_class_name_ast(class_ast));
|
||||
return 1;
|
||||
@ -7689,16 +7677,6 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
|
||||
znode class_node, const_node;
|
||||
zend_op *opline;
|
||||
|
||||
if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast, const_ast, 0)) {
|
||||
if (Z_TYPE(result->u.constant) == IS_NULL) {
|
||||
zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL);
|
||||
opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
|
||||
} else {
|
||||
result->op_type = IS_CONST;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
zend_eval_const_expr(&ast->child[0]);
|
||||
zend_eval_const_expr(&ast->child[1]);
|
||||
|
||||
@ -7716,10 +7694,6 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
|
||||
}
|
||||
zend_string_release_ex(resolved_name, 0);
|
||||
}
|
||||
if (const_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(const_ast), "class")) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Dynamic class names are not allowed in compile-time ::class fetch");
|
||||
}
|
||||
|
||||
zend_compile_class_ref(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
|
||||
|
||||
@ -7733,6 +7707,21 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_compile_class_name(znode *result, zend_ast *ast) /* {{{ */
|
||||
{
|
||||
zend_ast *class_ast = ast->child[0];
|
||||
zend_op *opline;
|
||||
|
||||
if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast, 0)) {
|
||||
result->op_type = IS_CONST;
|
||||
return;
|
||||
}
|
||||
|
||||
opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL);
|
||||
opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_compile_resolve_class_name(znode *result, zend_ast *ast) /* {{{ */
|
||||
{
|
||||
zend_ast *name_ast = ast->child[0];
|
||||
@ -7924,6 +7913,7 @@ zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
|
||||
|| kind == ZEND_AST_CONDITIONAL || kind == ZEND_AST_DIM
|
||||
|| kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM
|
||||
|| kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST
|
||||
|| kind == ZEND_AST_CLASS_NAME
|
||||
|| kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE;
|
||||
}
|
||||
/* }}} */
|
||||
@ -7936,7 +7926,6 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */
|
||||
zend_string *class_name;
|
||||
zend_string *const_name = zend_ast_get_str(const_ast);
|
||||
zend_string *name;
|
||||
zval result;
|
||||
int fetch_type;
|
||||
|
||||
if (class_ast->kind != ZEND_AST_ZVAL) {
|
||||
@ -7944,11 +7933,6 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */
|
||||
"Dynamic class names are not allowed in compile-time class constant references");
|
||||
}
|
||||
|
||||
if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, const_ast, 1)) {
|
||||
*ast_ptr = zend_ast_create_zval(&result);
|
||||
return;
|
||||
}
|
||||
|
||||
class_name = zend_ast_get_str(class_ast);
|
||||
fetch_type = zend_get_class_fetch_type(class_name);
|
||||
|
||||
@ -7973,6 +7957,19 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_compile_const_expr_class_name(zend_ast **ast_ptr) /* {{{ */
|
||||
{
|
||||
zend_ast *class_ast = (*ast_ptr)->child[0];
|
||||
uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
|
||||
|
||||
/* If we reach here, ::class should have either been constant evaluated or replaced
|
||||
* by a AST_MAGIC_CONST, so only the error case is left here. */
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"%s::class cannot be used for compile-time class name resolution",
|
||||
fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent"
|
||||
);
|
||||
}
|
||||
|
||||
void zend_compile_const_expr_const(zend_ast **ast_ptr) /* {{{ */
|
||||
{
|
||||
zend_ast *ast = *ast_ptr;
|
||||
@ -8024,6 +8021,9 @@ void zend_compile_const_expr(zend_ast **ast_ptr) /* {{{ */
|
||||
case ZEND_AST_CLASS_CONST:
|
||||
zend_compile_const_expr_class_const(ast_ptr);
|
||||
break;
|
||||
case ZEND_AST_CLASS_NAME:
|
||||
zend_compile_const_expr_class_name(ast_ptr);
|
||||
break;
|
||||
case ZEND_AST_CONST:
|
||||
zend_compile_const_expr_const(ast_ptr);
|
||||
break;
|
||||
@ -8312,6 +8312,9 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */
|
||||
case ZEND_AST_CLASS_CONST:
|
||||
zend_compile_class_const(result, ast);
|
||||
return;
|
||||
case ZEND_AST_CLASS_NAME:
|
||||
zend_compile_class_name(result, ast);
|
||||
return;
|
||||
case ZEND_AST_ENCAPS_LIST:
|
||||
zend_compile_encaps_list(result, ast);
|
||||
return;
|
||||
@ -8605,32 +8608,16 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
|
||||
}
|
||||
case ZEND_AST_CLASS_CONST:
|
||||
{
|
||||
zend_ast *class_ast = ast->child[0];
|
||||
zend_ast *name_ast = ast->child[1];
|
||||
zend_ast *class_ast;
|
||||
zend_ast *name_ast;
|
||||
zend_string *resolved_name;
|
||||
|
||||
if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, name_ast, 0)) {
|
||||
if (Z_TYPE(result) == IS_NULL) {
|
||||
if (zend_get_class_fetch_type(zend_ast_get_str(class_ast)) == ZEND_FETCH_CLASS_SELF) {
|
||||
zend_ast_destroy(ast);
|
||||
*ast_ptr = zend_ast_create_ex(ZEND_AST_MAGIC_CONST, T_CLASS_C);
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
zend_eval_const_expr(&ast->child[0]);
|
||||
zend_eval_const_expr(&ast->child[1]);
|
||||
|
||||
class_ast = ast->child[0];
|
||||
name_ast = ast->child[1];
|
||||
|
||||
if (name_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "class")) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Dynamic class names are not allowed in compile-time ::class fetch");
|
||||
}
|
||||
|
||||
if (class_ast->kind != ZEND_AST_ZVAL || name_ast->kind != ZEND_AST_ZVAL) {
|
||||
return;
|
||||
}
|
||||
@ -8645,7 +8632,20 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
|
||||
zend_string_release_ex(resolved_name, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case ZEND_AST_CLASS_NAME:
|
||||
{
|
||||
zend_ast *class_ast = ast->child[0];
|
||||
if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, 0)) {
|
||||
break;
|
||||
}
|
||||
/* TODO We should not use AST_MAGIC_CONST for this, because the semantics are slightly
|
||||
* different. */
|
||||
if (zend_get_class_fetch_type(zend_ast_get_str(class_ast)) == ZEND_FETCH_CLASS_SELF) {
|
||||
zend_ast_destroy(ast);
|
||||
*ast_ptr = zend_ast_create_ex(ZEND_AST_MAGIC_CONST, T_CLASS_C);
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
@ -1095,9 +1095,9 @@ scalar:
|
||||
constant:
|
||||
name { $$ = zend_ast_create(ZEND_AST_CONST, $1); }
|
||||
| class_name T_PAAMAYIM_NEKUDOTAYIM identifier
|
||||
{ $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $3); }
|
||||
{ $$ = zend_ast_create_class_const_or_name($1, $3); }
|
||||
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM identifier
|
||||
{ $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $3); }
|
||||
{ $$ = zend_ast_create_class_const_or_name($1, $3); }
|
||||
;
|
||||
|
||||
optional_expr:
|
||||
|
Loading…
Reference in New Issue
Block a user