]> granicus.if.org Git - php/commitdiff
Introduce separate ZEND_AST_CLASS_NAME AST node
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 4 Jan 2019 09:49:23 +0000 (10:49 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Fri, 4 Jan 2019 09:49:23 +0000 (10:49 +0100)
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.

Zend/tests/class_name_of_var.phpt [new file with mode: 0644]
Zend/zend_ast.c
Zend/zend_ast.h
Zend/zend_compile.c
Zend/zend_language_parser.y

diff --git a/Zend/tests/class_name_of_var.phpt b/Zend/tests/class_name_of_var.phpt
new file mode 100644 (file)
index 0000000..29f19e9
--- /dev/null
@@ -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
index 834696c2ca9f4fe614c0708001b96cbc227a46e6..b691dce0e03c64fc2132496d5298fb98506dd43d 100644 (file)
@@ -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:
index db1f51b3786479ac1342571d8b8afdd9476435ad..78d879a3c6599426bcb1dca5b4fa1f2e9c751d27 100644 (file)
@@ -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, ...) \
index af6a3f13324d6e65529e5b927c4f2cd02933c17d..a12de019906eed1b40eba10efb28afd228b88cbb 100644 (file)
@@ -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;
        }
index cf6f0662601e4a56ec219490fc2285908615b412..ec9ba82193346ce0bb144b57e7848832ad0a77a6 100644 (file)
@@ -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: