[PHP-CVS] [php-src] master: Allow direct mutation of objects stored in constants or class constants via OBJ->prop = $val

[email protected] (Khaled Alam via Ilija Tovilo)
Newsgroups php.cvs
Message-ID <[email protected]>
Author: Khaled Alam (khaledalam)
Committer: Ilija Tovilo (iluuu1994)
Date: 2026-08-13T12:40:19+02:00

Commit: https://github.com/php/php-src/commit/b16cab7da8e5748b173542da42fc8b602ae59dea
Raw diff: https://github.com/php/php-src/commit/b16cab7da8e5748b173542da42fc8b602ae59dea.diff

Allow direct mutation of objects stored in constants or class constants via OBJ->prop = $val

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

Fixes GH-10497
Closes GH-20903

Co-authored-by: Ilija Tovilo <[email protected]>

Changed paths:
  A  Zend/tests/gh10497.phpt
  A  Zend/tests/gh10497_class_const.phpt
  A  Zend/tests/gh10497_dim_arr.phpt
  A  Zend/tests/gh10497_dim_obj.phpt
  A  Zend/tests/gh10497_func_arg.phpt
  A  Zend/tests/gh10497_non_object.phpt
  A  Zend/tests/gh10497_write_targets.phpt
  A  Zend/tests/gh10497_write_targets_guardrail.phpt
  M  NEWS
  M  UPGRADING
  M  Zend/Optimizer/zend_optimizer.c
  M  Zend/tests/enum/no-unsed-value.phpt
  M  Zend/zend_compile.c


Diff:

diff --git a/NEWS b/NEWS
index 3a5f08491611..930f40fc25e3 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,8 @@ PHP                                                                        NEWS
 - Core:
   . Fixed bug GH-15375 (Nested "yield from" skips items after a valid() or
     next() call on the inner generator). (iliaal)
+  . Fixed GH-10497 (Allow direct mutation of objects stored in constants or
+    class constants via OBJ->prop = $val). (Khaled Alam)
 
 - PDO_PGSQL:
   . Fixed several lazy fetch (PDO::ATTR_PREFETCH => 0) defects: an infinite
diff --git a/UPGRADING b/UPGRADING
index a4cc44f9a004..271238bf404d 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -309,6 +309,9 @@ PHP 8.6 UPGRADE NOTES
     RFC: https://wiki.php.net/rfc/override_constants
   . Implemented partial function application
     RFC: https://wiki.php.net/rfc/partial_function_application_v2
+  . Allow direct mutation of objects stored in constants or class constants via
+    OBJ->prop = $val.
+    RFC: https://wiki.php.net/rfc/const_object_property_write
 
 - Curl:
   . curl_getinfo() return array now includes a new size_delivered key, which
diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c
index 862236c923b2..d437a3fbe53e 100644
--- a/Zend/Optimizer/zend_optimizer.c
+++ b/Zend/Optimizer/zend_optimizer.c
@@ -280,6 +280,19 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
 		case ZEND_SEPARATE:
 		case ZEND_SEND_VAR_NO_REF:
 		case ZEND_SEND_VAR_NO_REF_EX:
+		case ZEND_ASSIGN_OP:
+		case ZEND_ASSIGN_DIM_OP:
+		case ZEND_ASSIGN_OBJ:
+		case ZEND_ASSIGN_OBJ_OP:
+		case ZEND_ASSIGN_OBJ_REF:
+		case ZEND_UNSET_OBJ:
+		case ZEND_FETCH_OBJ_W:
+		case ZEND_FETCH_OBJ_RW:
+		case ZEND_FETCH_OBJ_UNSET:
+		case ZEND_PRE_INC_OBJ:
+		case ZEND_PRE_DEC_OBJ:
+		case ZEND_POST_INC_OBJ:
+		case ZEND_POST_DEC_OBJ:
 			return false;
 		case ZEND_CATCH:
 			REQUIRES_STRING(val);
@@ -320,10 +333,6 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
 			}
 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
 			break;
-		case ZEND_ASSIGN_OP:
-		case ZEND_ASSIGN_DIM_OP:
-		case ZEND_ASSIGN_OBJ_OP:
-			break;
 		case ZEND_ASSIGN_STATIC_PROP_OP:
 		case ZEND_ASSIGN_STATIC_PROP:
 		case ZEND_ASSIGN_STATIC_PROP_REF:
diff --git a/Zend/tests/enum/no-unsed-value.phpt b/Zend/tests/enum/no-unsed-value.phpt
index a1cbdd43cad4..eb058cb1b26f 100644
--- a/Zend/tests/enum/no-unsed-value.phpt
+++ b/Zend/tests/enum/no-unsed-value.phpt
@@ -11,4 +11,7 @@ unset(Foo::Bar->value);
 
 ?>
 --EXPECTF--
-Fatal error: Cannot use temporary expression in write context in %s on line %d
+Fatal error: Uncaught Error: Cannot unset readonly property Foo::$value in %s:%d
+Stack trace:
+#0 {main}
+  thrown in %s on line %d
diff --git a/Zend/tests/gh10497.phpt b/Zend/tests/gh10497.phpt
new file mode 100644
index 000000000000..bcefffe3a8b9
--- /dev/null
+++ b/Zend/tests/gh10497.phpt
@@ -0,0 +1,84 @@
+--TEST--
+GH-10497: Allow direct modification of object properties on constants
+--FILE--
+<?php
+
+const OBJ = new stdClass;
+OBJ->prop = 123;
+var_dump(OBJ->prop);
+
+OBJ->foo = 'bar';
+OBJ->baz = 456;
+var_dump(OBJ->foo, OBJ->baz);
+
+OBJ->prop = 'overwritten';
+var_dump(OBJ->prop);
+
+OBJ->inner = new stdClass;
+OBJ->inner->value = 999;
+var_dump(OBJ->inner->value);
+
+OBJ->counter = 0;
+OBJ->counter++;
+OBJ->counter++;
+OBJ->counter--;
+var_dump(OBJ->counter);
+
+OBJ->str = 'hello';
+OBJ->str .= ' world';
+var_dump(OBJ->str);
+
+OBJ->temp = 'remove me';
+var_dump(isset(OBJ->temp));
+unset(OBJ->temp);
+var_dump(isset(OBJ->temp));
+
+var_dump(isset(OBJ->foo));
+var_dump(empty(OBJ->foo));
+var_dump(isset(OBJ->nonexistent));
+var_dump(empty(OBJ->nonexistent));
+
+function incr(&$v) { $v++; }
+OBJ->reftest = 10;
+incr(OBJ->reftest);
+var_dump(OBJ->reftest);
+
+OBJ->arr = [];
+OBJ->arr[0] = 42;
+OBJ->arr[] = 43;
+var_dump(OBJ->arr);
+
+OBJ->coalesce ??= 42;
+var_dump(OBJ->coalesce);
+OBJ->coalesce ??= 43;
+var_dump(OBJ->coalesce);
+
+const OBJS = [new stdClass];
+OBJS[0]->prop = 42;
+var_dump(OBJS[0]->prop);
+
+?>
+--EXPECT--
+int(123)
+string(3) "bar"
+int(456)
+string(11) "overwritten"
+int(999)
+int(1)
+string(11) "hello world"
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(false)
+bool(true)
+int(11)
+array(2) {
+  [0]=>
+  int(42)
+  [1]=>
+  int(43)
+}
+int(42)
+int(42)
+int(42)
diff --git a/Zend/tests/gh10497_class_const.phpt b/Zend/tests/gh10497_class_const.phpt
new file mode 100644
index 000000000000..0b24a8573035
--- /dev/null
+++ b/Zend/tests/gh10497_class_const.phpt
@@ -0,0 +1,73 @@
+--TEST--
+GH-10497: Allow direct modification of object properties on class constants
+--FILE--
+<?php
+
+const BACKING = new stdClass;
+
+class C {
+    const O = BACKING;
+}
+
+// Access via class name.
+C::O->prop = 123;
+var_dump(C::O->prop);
+
+// Access via instance.
+$c = new C;
+$c::O->prop = 'overwritten';
+var_dump(C::O->prop);
+
+// Compound assignment, increment/decrement and concatenation.
+C::O->counter = 0;
+C::O->counter++;
+C::O->counter++;
+C::O->counter--;
+var_dump(C::O->counter);
+
+C::O->str = 'hello';
+C::O->str .= ' world';
+var_dump(C::O->str);
+
+// Nested property chains.
+C::O->inner = new stdClass;
+C::O->inner->value = 999;
+var_dump(C::O->inner->value);
+
+// isset()/unset().
+C::O->temp = 'remove me';
+var_dump(isset(C::O->temp));
+unset(C::O->temp);
+var_dump(isset(C::O->temp));
+
+// Passing by reference.
+function incr(&$v) { $v++; }
+C::O->reftest = 10;
+incr(C::O->reftest);
+var_dump(C::O->reftest);
+
+// self:: from within a method.
+class D {
+    const O = BACKING;
+
+    public static function set(): void {
+        self::O->fromSelf = 'yes';
+    }
+}
+
+D::set();
+var_dump(D::O->fromSelf);
+var_dump(BACKING->fromSelf);
+
+?>
+--EXPECT--
+int(123)
+string(11) "overwritten"
+int(1)
+string(11) "hello world"
+int(999)
+bool(true)
+bool(false)
+int(11)
+string(3) "yes"
+string(3) "yes"
diff --git a/Zend/tests/gh10497_dim_arr.phpt b/Zend/tests/gh10497_dim_arr.phpt
new file mode 100644
index 000000000000..5c1562616c39
--- /dev/null
+++ b/Zend/tests/gh10497_dim_arr.phpt
@@ -0,0 +1,9 @@
+--TEST--
+GH-10497: Array dim write on constant fails
+--FILE--
+<?php
+const ARR = [1, 2, 3];
+ARR[0] = 9;
+?>
+--EXPECTF--
+Fatal error: Cannot use temporary expression in write context in %s on line %d
diff --git a/Zend/tests/gh10497_dim_obj.phpt b/Zend/tests/gh10497_dim_obj.phpt
new file mode 100644
index 000000000000..73552af828c5
--- /dev/null
+++ b/Zend/tests/gh10497_dim_obj.phpt
@@ -0,0 +1,9 @@
+--TEST--
+GH-10497: Dim write on constant object fails
+--FILE--
+<?php
+const OBJ = new stdClass;
+OBJ["x"] = 1;
+?>
+--EXPECTF--
+Fatal error: Cannot use temporary expression in write context in %s on line %d
diff --git a/Zend/tests/gh10497_func_arg.phpt b/Zend/tests/gh10497_func_arg.phpt
new file mode 100644
index 000000000000..047cf4cfe45f
--- /dev/null
+++ b/Zend/tests/gh10497_func_arg.phpt
@@ -0,0 +1,19 @@
+--TEST--
+GH-10497: Passing constant object property by reference via FUNC_ARG
+--FILE--
+<?php
+
+// Forward-reference: function declared after call site, so the compiler
+// uses BP_VAR_FUNC_ARG rather than BP_VAR_W for the property fetch.
+const OBJ = new stdClass;
+OBJ->val = 10;
+modify(OBJ->val);
+var_dump(OBJ->val);
+
+function modify(&$v) {
+    $v = 42;
+}
+
+?>
+--EXPECT--
+int(42)
diff --git a/Zend/tests/gh10497_non_object.phpt b/Zend/tests/gh10497_non_object.phpt
new file mode 100644
index 000000000000..546e9718443d
--- /dev/null
+++ b/Zend/tests/gh10497_non_object.phpt
@@ -0,0 +1,111 @@
+--TEST--
+GH-10497: Writing to a property of a non-object constant reports a runtime error
+--FILE--
+<?php
+
+try {
+    TRUE->prop = 1;
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+try {
+    NULL->prop = 1;
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+try {
+    PHP_INT_MAX->prop = 1;
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+class C {
+    const INT = 5;
+    const STR = 'str';
+    const ARR = [1, 2];
+
+    public static function fromSelf(): void {
+        self::INT->prop = 1;
+    }
+}
+
+try {
+    C::INT->prop = 1;
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+try {
+    C::STR->prop = 1;
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+try {
+    C::ARR->prop = 1;
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+try {
+    C::fromSelf();
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+// Other write contexts: compound assignment, unset() and by-reference arguments.
+try {
+    C::INT->prop++;
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+try {
+    C::INT->prop .= 'x';
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+function byRef(&$v) {}
+
+try {
+    byRef(C::INT->prop);
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+// As for plain variables, unsetting a property of a non-object is a silent no-op.
+unset(C::INT->prop);
+echo "unset() did not error\n";
+
+var_dump(TRUE, C::INT, C::STR);
+var_dump(isset(C::INT->prop));
+
+try {
+    __COMPILER_HALT_OFFSET__->prop = 1;
+} catch (Error $e) {
+    echo $e::class, $e->getMessage(), "\n";
+}
+
+__halt_compiler();
+
+?>
+--EXPECT--
+Attempt to assign property "prop" on true
+Attempt to assign property "prop" on null
+Attempt to assign property "prop" on int
+Attempt to assign property "prop" on int
+Attempt to assign property "prop" on string
+Attempt to assign property "prop" on array
+Attempt to assign property "prop" on int
+Attempt to increment/decrement property "prop" on int
+Attempt to assign property "prop" on int
+Attempt to modify property "prop" on int
+unset() did not error
+bool(true)
+int(5)
+string(3) "str"
+bool(false)
+ErrorAttempt to assign property "prop" on int
diff --git a/Zend/tests/gh10497_write_targets.phpt b/Zend/tests/gh10497_write_targets.phpt
new file mode 100644
index 000000000000..9b544035f644
--- /dev/null
+++ b/Zend/tests/gh10497_write_targets.phpt
@@ -0,0 +1,97 @@
+--TEST--
+GH-10497: Destructuring and by-reference foreach targeting constant object properties
+--FILE--
+<?php
+
+const BACKING = new stdClass;
+
+class C {
+    const O = BACKING;
+}
+
+// By-reference foreach must write through to the referenced object, as it does
+// for a plain variable holding the same object.
+BACKING->arr = [1, 2, 3];
+foreach (BACKING->arr as &$v) {
+    $v *= 2;
+}
+unset($v);
+var_dump(BACKING->arr);
+
+C::O->arr = [1, 2, 3];
+foreach (C::O->arr as &$v) {
+    $v *= 2;
+}
+unset($v);
+var_dump(C::O->arr);
+
+// Nested chains and a dimension after the property fetch.
+BACKING->inner = new stdClass;
+BACKING->inner->arr = [1, 2, 3];
+foreach (BACKING->inner->arr as &$v) {
+    $v *= 2;
+}
+unset($v);
+var_dump(BACKING->inner->arr);
+
+BACKING->matrix = [[1, 2]];
+foreach (BACKING->matrix[0] as &$v) {
+    $v *= 2;
+}
+unset($v);
+var_dump(BACKING->matrix[0]);
+
+// Destructuring assignment.
+[BACKING->p, BACKING->q] = [1, 2];
+var_dump(BACKING->p, BACKING->q);
+
+['k' => BACKING->keyed] = ['k' => 9];
+var_dump(BACKING->keyed);
+
+BACKING->list = [];
+[BACKING->list[0]] = [7];
+var_dump(BACKING->list);
+
+[C::O->viaClassConst] = ['yes'];
+var_dump(BACKING->viaClassConst);
+
+?>
+--EXPECT--
+array(3) {
+  [0]=>
+  int(2)
+  [1]=>
+  int(4)
+  [2]=>
+  int(6)
+}
+array(3) {
+  [0]=>
+  int(2)
+  [1]=>
+  int(4)
+  [2]=>
+  int(6)
+}
+array(3) {
+  [0]=>
+  int(2)
+  [1]=>
+  int(4)
+  [2]=>
+  int(6)
+}
+array(2) {
+  [0]=>
+  int(2)
+  [1]=>
+  int(4)
+}
+int(1)
+int(2)
+int(9)
+array(1) {
+  [0]=>
+  int(7)
+}
+string(3) "yes"
diff --git a/Zend/tests/gh10497_write_targets_guardrail.phpt b/Zend/tests/gh10497_write_targets_guardrail.phpt
new file mode 100644
index 000000000000..f8763d7b4651
--- /dev/null
+++ b/Zend/tests/gh10497_write_targets_guardrail.phpt
@@ -0,0 +1,9 @@
+--TEST--
+GH-10497: Destructuring into a constant without property assignment
+--FILE--
+<?php
+const ARR = [1, 2, 3];
+[ARR[0]] = [9];
+?>
+--EXPECTF--
+Fatal error: Assignments can only happen to writable values in %s on line %d
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 91822e886684..c7ac93f8d34e 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -2594,6 +2594,20 @@ static void zend_short_circuiting_commit(uint32_t checkpoint, znode *result, con
 	}
 }
 
+/* Same value as ZEND_SHORT_CIRCUITING_INNER, AST kinds must not clash. */
+#define ZEND_CONST_OBJECT_FETCH 0x8000
+
+static void zend_mark_const_object_fetch(zend_ast *ast)
+{
+	while (ast->kind == ZEND_AST_DIM) {
+		ast = ast->child[0];
+	}
+
+	if (ast->kind == ZEND_AST_CONST || ast->kind == ZEND_AST_CLASS_CONST) {
+		ast->attr |= ZEND_CONST_OBJECT_FETCH;
+	}
+}
+
 static void zend_emit_jmp_null(znode *obj_node, uint32_t bp_type)
 {
 	uint32_t jmp_null_opnum = get_next_op_number();
@@ -2794,13 +2808,20 @@ static inline bool zend_is_unticked_stmt(const zend_ast *ast) /* {{{ */
 
 static inline bool zend_can_write_to_variable(const zend_ast *ast) /* {{{ */
 {
+	bool via_prop = false;
+
 	while (
 		ast->kind == ZEND_AST_DIM
 		|| ast->kind == ZEND_AST_PROP
 	) {
+		via_prop |= ast->kind == ZEND_AST_PROP;
 		ast = ast->child[0];
 	}
 
+	if ((ast->kind == ZEND_AST_CONST || ast->kind == ZEND_AST_CLASS_CONST) && via_prop) {
+		return true;
+	}
+
 	return zend_is_variable_or_call(ast) && !zend_ast_is_short_circuited(ast);
 }
 /* }}} */
@@ -3186,6 +3207,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
 		 * check for a nullsafe access. */
 	} else {
 		zend_short_circuiting_mark_inner(obj_ast);
+		zend_mark_const_object_fetch(obj_ast);
 		opline = zend_delayed_compile_var(&obj_node, obj_ast, type, false);
 		if (opline && (opline->opcode == ZEND_FETCH_DIM_W
 				|| opline->opcode == ZEND_FETCH_DIM_RW
@@ -11576,9 +11598,14 @@ static void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
 }
 /* }}} */
 
-static void zend_emit_fetch_constant(znode *result, zend_string *resolved_name, bool unqualified_in_namespace)
+static void zend_emit_fetch_constant(znode *result, zend_string *resolved_name, bool unqualified_in_namespace, uint8_t type)
 {
-	zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CONSTANT, NULL, NULL);
+	zend_op *opline = zend_emit_op(result, ZEND_FETCH_CONSTANT, NULL, NULL);
+	if (type == BP_VAR_R || type == BP_VAR_IS) {
+		opline->result_type = IS_TMP_VAR;
+		result->op_type = IS_TMP_VAR;
+	}
+
 	opline->op2_type = IS_CONST;
 
 	if (unqualified_in_namespace) {
@@ -11591,7 +11618,7 @@ static void zend_emit_fetch_constant(znode *result, zend_string *resolved_name,
 	opline->extended_value = zend_alloc_cache_slot();
 }
 
-static void zend_compile_const(znode *result, const zend_ast *ast) /* {{{ */
+static void zend_compile_const(znode *result, const zend_ast *ast, uint8_t type) /* {{{ */
 {
 	zend_ast *name_ast = ast->child[0];
 
@@ -11624,19 +11651,19 @@ static void zend_compile_const(znode *result, const zend_ast *ast) /* {{{ */
 	}
 
 	zend_emit_fetch_constant(result, resolved_name,
-		!is_fully_qualified && FC(current_namespace));
+		!is_fully_qualified && FC(current_namespace), type);
 }
 /* }}} */
 
-static void zend_compile_constant(znode *result, zend_ast *ast)
+static void zend_compile_constant(znode *result, zend_ast *ast, uint8_t type)
 {
 	zend_string *name = zend_ast_get_constant_name(ast);
 
 	zend_emit_fetch_constant(result, zend_string_copy(name),
-		(ast->attr & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) != 0);
+		(ast->attr & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) != 0, type);
 }
 
-static void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_class_const(znode *result, zend_ast *ast, uint8_t type) /* {{{ */
 {
 	zend_ast *class_ast;
 	zend_ast *const_ast;
@@ -11667,7 +11694,11 @@ static void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
 
 	zend_compile_expr(&const_node, const_ast);
 
-	opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_CONSTANT, NULL, &const_node);
+	opline = zend_emit_op(result, ZEND_FETCH_CLASS_CONSTANT, NULL, &const_node);
+	if (type == BP_VAR_R || type == BP_VAR_IS) {
+		opline->result_type = IS_TMP_VAR;
+		result->op_type = IS_TMP_VAR;
+	}
 
 	zend_set_class_name_op1(opline, &class_node);
 
@@ -12517,13 +12548,13 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
 			zend_compile_array(result, ast);
 			return;
 		case ZEND_AST_CONST:
-			zend_compile_const(result, ast);
+			zend_compile_const(result, ast, BP_VAR_R);
 			return;
 		case ZEND_AST_CONSTANT:
-			zend_compile_constant(result, ast);
+			zend_compile_constant(result, ast, BP_VAR_R);
 			return;
 		case ZEND_AST_CLASS_CONST:
-			zend_compile_class_const(result, ast);
+			zend_compile_class_const(result, ast, BP_VAR_R);
 			return;
 		case ZEND_AST_CLASS_NAME:
 			zend_compile_class_name(result, ast);
@@ -12665,9 +12696,31 @@ static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t
 		}
 		case ZEND_AST_STATIC_PROP:
 			return zend_compile_static_prop(result, ast, type, by_ref, true);
-		default:
-			return zend_compile_var(result, ast, type, false);
+		case ZEND_AST_CONST:
+			if (!(ast->attr & ZEND_CONST_OBJECT_FETCH)) {
+				break;
+			}
+			zend_compile_const(result, ast, type);
+			if (!(type == BP_VAR_R || type == BP_VAR_IS) && result->op_type == IS_CONST) {
+				znode op1 = *result;
+				/* Intentionally IS_VAR result. */
+				zend_emit_op(result, ZEND_QM_ASSIGN, &op1, NULL);
+			}
+			return NULL;
+		case ZEND_AST_CLASS_CONST:
+			if (!(ast->attr & ZEND_CONST_OBJECT_FETCH)) {
+				break;
+			}
+			zend_compile_class_const(result, ast, type);
+			if (!(type == BP_VAR_R || type == BP_VAR_IS) && result->op_type == IS_CONST) {
+				znode op1 = *result;
+				/* Intentionally IS_VAR result. */
+				zend_emit_op(result, ZEND_QM_ASSIGN, &op1, NULL);
+			}
+			return NULL;
 	}
+
+	return zend_compile_var(result, ast, type, false);
 }
 /* }}} */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.