Skip to content

Commit b0a2437

Browse files
committed
Rebase
1 parent 359acf6 commit b0a2437

8 files changed

Lines changed: 134 additions & 48 deletions

UPGRADING

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ PHP 8.6 UPGRADE NOTES
5555
========================================
5656

5757
- Core:
58+
. It is now possible to write to object properties on objects stored in
59+
constants. Previously this resulted in "Cannot use temporary expression
60+
in write context". This change is limited to object property access
61+
(->); dim/array writes on constants (e.g. CONST[0] = value) remain
62+
a compile error. RFC: https://wiki.php.net/rfc/const_object_property_write
5863
. It is now possible to use reference assign on WeakMap without the key
5964
needing to be present beforehand.
6065

Zend/tests/gh10497.phpt

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,61 @@
11
--TEST--
2-
GH-10497: Allow direct modification of object stored in a constant
2+
GH-10497: Allow direct modification of object properties on constants
33
--FILE--
44
<?php
5-
const a = new stdClass;
6-
a->b = 42;
7-
var_dump(a->b);
8-
9-
const obj = new stdClass;
10-
obj->foo = 'bar';
11-
obj->baz = 123;
12-
var_dump(obj->foo, obj->baz);
13-
14-
const nested = new stdClass;
15-
nested->inner = new stdClass;
16-
nested->inner->value = 999;
17-
var_dump(nested->inner->value);
18-
19-
const readTest = new stdClass;
20-
readTest->prop = 'test';
21-
echo readTest->prop . "\n";
22-
23-
var_dump(isset(readTest->prop));
24-
var_dump(empty(readTest->missing));
25-
26-
const modTest = new stdClass;
27-
modTest->val = 1;
28-
modTest->val = 2;
29-
var_dump(modTest->val);
5+
6+
const OBJ = new stdClass;
7+
OBJ->prop = 123;
8+
var_dump(OBJ->prop);
9+
10+
OBJ->foo = 'bar';
11+
OBJ->baz = 456;
12+
var_dump(OBJ->foo, OBJ->baz);
13+
14+
OBJ->prop = 'overwritten';
15+
var_dump(OBJ->prop);
16+
17+
OBJ->inner = new stdClass;
18+
OBJ->inner->value = 999;
19+
var_dump(OBJ->inner->value);
20+
21+
OBJ->counter = 0;
22+
OBJ->counter++;
23+
OBJ->counter++;
24+
OBJ->counter--;
25+
var_dump(OBJ->counter);
26+
27+
OBJ->str = 'hello';
28+
OBJ->str .= ' world';
29+
var_dump(OBJ->str);
30+
31+
OBJ->temp = 'remove me';
32+
var_dump(isset(OBJ->temp));
33+
unset(OBJ->temp);
34+
var_dump(isset(OBJ->temp));
35+
36+
var_dump(isset(OBJ->foo));
37+
var_dump(empty(OBJ->foo));
38+
var_dump(isset(OBJ->nonexistent));
39+
var_dump(empty(OBJ->nonexistent));
40+
41+
function incr(&$v) { $v++; }
42+
OBJ->reftest = 10;
43+
incr(OBJ->reftest);
44+
var_dump(OBJ->reftest);
45+
3046
?>
3147
--EXPECT--
32-
int(42)
33-
string(3) "bar"
3448
int(123)
49+
string(3) "bar"
50+
int(456)
51+
string(11) "overwritten"
3552
int(999)
36-
test
53+
int(1)
54+
string(11) "hello world"
55+
bool(true)
56+
bool(false)
3757
bool(true)
58+
bool(false)
59+
bool(false)
3860
bool(true)
39-
int(2)
61+
int(11)

Zend/tests/gh10497_func_arg.phpt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
GH-10497: Passing constant object property by reference via FUNC_ARG
3+
--FILE--
4+
<?php
5+
6+
// Forward-reference: function declared after call site, so the compiler
7+
// uses BP_VAR_FUNC_ARG rather than BP_VAR_W for the property fetch.
8+
const OBJ = new stdClass;
9+
OBJ->val = 10;
10+
modify(OBJ->val);
11+
var_dump(OBJ->val);
12+
13+
function modify(&$v) {
14+
$v = 42;
15+
}
16+
17+
?>
18+
--EXPECT--
19+
int(42)

Zend/tests/gh10497_guardrails.phpt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
GH-10497: Guardrail - array dim write on constant still fails
3+
--FILE--
4+
<?php
5+
const ARR = [1, 2, 3];
6+
ARR[0] = 9;
7+
?>
8+
--EXPECTF--
9+
Fatal error: Cannot use temporary expression in write context in %s on line %d
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
GH-10497: Guardrail - dim write on constant object still fails
3+
--FILE--
4+
<?php
5+
const OBJ = new stdClass;
6+
OBJ["x"] = 1;
7+
?>
8+
--EXPECTF--
9+
Fatal error: Cannot use temporary expression in write context in %s on line %d
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
GH-10497: Guardrail - constant rebinding still fails
3+
--FILE--
4+
<?php
5+
const OBJ = new stdClass;
6+
OBJ = new stdClass;
7+
?>
8+
--EXPECTF--
9+
Parse error: syntax error, unexpected token "=" in %s on line %d

Zend/tests/gh12102_3.phpt

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ GH-12102: Incorrect "Cannot use temporary expression in write context" error for
55

66
function test() {
77
byVal(C[0]);
8-
byRef(C[0]);
9-
var_dump(C);
8+
try {
9+
byRef(C[0]);
10+
} catch (Error $e) {
11+
echo $e->getMessage(), "\n";
12+
}
1013
}
1114

1215
/* Intentionally declared after test() to avoid compile-time checking of ref args. */
@@ -18,7 +21,6 @@ function byVal($arg) {
1821
}
1922

2023
function byRef(&$arg) {
21-
$arg = 'modified';
2224
var_dump($arg);
2325
}
2426

@@ -27,8 +29,4 @@ test('y');
2729
?>
2830
--EXPECT--
2931
string(3) "foo"
30-
string(8) "modified"
31-
array(1) {
32-
[0]=>
33-
string(3) "foo"
34-
}
32+
Cannot use temporary expression in write context

Zend/zend_compile.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3179,6 +3179,28 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
31793179

31803180
/* We will throw if $this doesn't exist, so there's no need to emit a JMP_NULL
31813181
* check for a nullsafe access. */
3182+
} else if (obj_ast->kind == ZEND_AST_CONST
3183+
&& (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET
3184+
|| type == BP_VAR_FUNC_ARG)) {
3185+
zend_ast *name_ast = obj_ast->child[0];
3186+
bool is_fully_qualified;
3187+
zend_string *orig_name = zend_ast_get_str(name_ast);
3188+
zend_string *resolved_name = zend_resolve_const_name(
3189+
orig_name, name_ast->attr, &is_fully_qualified);
3190+
3191+
opline = zend_emit_op(&obj_node, ZEND_FETCH_CONSTANT, NULL, NULL);
3192+
opline->op2_type = IS_CONST;
3193+
3194+
if (is_fully_qualified || !FC(current_namespace)) {
3195+
opline->op1.num = 0;
3196+
opline->op2.constant = zend_add_const_name_literal(
3197+
resolved_name, false);
3198+
} else {
3199+
opline->op1.num = IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE;
3200+
opline->op2.constant = zend_add_const_name_literal(
3201+
resolved_name, true);
3202+
}
3203+
opline->extended_value = zend_alloc_cache_slot();
31823204
} else {
31833205
zend_short_circuiting_mark_inner(obj_ast);
31843206
opline = zend_delayed_compile_var(&obj_node, obj_ast, type, false);
@@ -11309,7 +11331,7 @@ static void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
1130911331
}
1131011332
/* }}} */
1131111333

11312-
static void zend_compile_const(znode *result, const zend_ast *ast, uint32_t type) /* {{{ */
11334+
static void zend_compile_const(znode *result, const zend_ast *ast) /* {{{ */
1131311335
{
1131411336
zend_ast *name_ast = ast->child[0];
1131511337

@@ -11343,11 +11365,7 @@ static void zend_compile_const(znode *result, const zend_ast *ast, uint32_t type
1134311365
return;
1134411366
}
1134511367

11346-
if (type == BP_VAR_R || type == BP_VAR_IS) {
11347-
opline = zend_emit_op_tmp(result, ZEND_FETCH_CONSTANT, NULL, NULL);
11348-
} else {
11349-
opline = zend_emit_op(result, ZEND_FETCH_CONSTANT, NULL, NULL);
11350-
}
11368+
opline = zend_emit_op_tmp(result, ZEND_FETCH_CONSTANT, NULL, NULL);
1135111369
opline->op2_type = IS_CONST;
1135211370

1135311371
if (is_fully_qualified || !FC(current_namespace)) {
@@ -12222,7 +12240,7 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
1222212240
zend_compile_array(result, ast);
1222312241
return;
1222412242
case ZEND_AST_CONST:
12225-
zend_compile_const(result, ast, BP_VAR_R);
12243+
zend_compile_const(result, ast);
1222612244
return;
1222712245
case ZEND_AST_CLASS_CONST:
1222812246
zend_compile_class_const(result, ast);
@@ -12318,9 +12336,6 @@ static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t ty
1231812336
case ZEND_AST_ASSIGN:
1231912337
zend_compile_assign(result, ast, false, type);
1232012338
return NULL;
12321-
case ZEND_AST_CONST:
12322-
zend_compile_const(result, ast, type);
12323-
return NULL;
1232412339
default:
1232512340
if (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) {
1232612341
zend_error_noreturn(E_COMPILE_ERROR,

0 commit comments

Comments
 (0)