From 9b3fda43424bb9044afcbf8490e38906c46851a3 Mon Sep 17 00:00:00 2001
From: Nikita Popov <nikita.ppv@gmail.com>
Date: Fri, 4 Jan 2019 11:16:59 +0100
Subject: [PATCH] Don't replace self::class with __CLASS__ during const eval

Instead delay this until const expression compilation. Otherwise
self::class and [self::class] have different behavior...
---
 .../tests/class_name_as_scalar_error_007.phpt | 19 +++++++----
 Zend/zend_compile.c                           | 32 ++++++++-----------
 2 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/Zend/tests/class_name_as_scalar_error_007.phpt b/Zend/tests/class_name_as_scalar_error_007.phpt
index 22099f6104..cbaf518ea8 100644
--- a/Zend/tests/class_name_as_scalar_error_007.phpt
+++ b/Zend/tests/class_name_as_scalar_error_007.phpt
@@ -3,11 +3,18 @@ Cannot access self::class when no class scope is active
 --FILE--
 <?php
 
-var_dump(self::class);
+try {
+    var_dump(self::class);
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+try {
+    var_dump([self::class]);
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
 
 ?>
---EXPECTF--
-Fatal error: Uncaught Error: Cannot use "self" when no class scope is active in %s:3
-Stack trace:
-#0 {main}
-  thrown in %s on line 3
+--EXPECT--
+Cannot use "self" when no class scope is active
+Cannot use "self" when no class scope is active
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index a12de01990..f3f12ddbf1 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -1366,7 +1366,7 @@ 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_bool constant) /* {{{ */
+static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast) /* {{{ */
 {
 	uint32_t fetch_type;
 
@@ -1386,12 +1386,6 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a
 			return 0;
 		case ZEND_FETCH_CLASS_STATIC:
 		case ZEND_FETCH_CLASS_PARENT:
-			if (constant) {
-				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"
-				);
-			}
 			return 0;
 		case ZEND_FETCH_CLASS_DEFAULT:
 			ZVAL_STR(zv, zend_resolve_class_name_ast(class_ast));
@@ -7712,7 +7706,7 @@ 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)) {
+	if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast)) {
 		result->op_type = IS_CONST;
 		return;
 	}
@@ -7962,8 +7956,14 @@ 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. */
+	/* TODO We should not use AST_CONSTANT_CLASS for this, because the semantics are slightly
+	 * different. */
+	if (fetch_type == ZEND_FETCH_CLASS_SELF) {
+		zend_ast_destroy(*ast_ptr);
+		*ast_ptr = zend_ast_create(ZEND_AST_CONSTANT_CLASS);
+		return;
+	}
+
 	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"
@@ -8635,16 +8635,10 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
 		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);
+			if (!zend_try_compile_const_expr_resolve_class_name(&result, class_ast)) {
+				return;
 			}
-			return;
+			break;
 		}
 		default:
 			return;
-- 
2.40.0