Skip to content

Commit 7498f01

Browse files
committed
Fix handling of assign-ops on overloaded props with ref return
Assign-ops and incdec on overloaded properties are implemented using a read_property followed by write_property. Previously, if __get() returned by-reference, pre-incdec and assign-op additionally also modified the reference, while post-incdec worked correctly. This change synchronizes the three code-paths to not modify the reference. The pre-incdec implementation matches the post-incdec implementation, the assign-op implementation uses a distinct result operand.
1 parent befc518 commit 7498f01

File tree

2 files changed

+67
-16
lines changed

2 files changed

+67
-16
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--TEST--
2+
Handling of assign-ops and incdecs on overloaded properties using &__get()
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
protected $a = 0;
8+
protected $b = 0;
9+
protected $c = 0;
10+
11+
public function &__get($name) {
12+
echo "get($name)\n";
13+
return $this->$name;
14+
}
15+
16+
public function __set($name, $value) {
17+
echo "set($name, $value)\n";
18+
}
19+
}
20+
21+
$test = new Test;
22+
23+
var_dump($test->a += 1);
24+
var_dump($test->b++);
25+
var_dump(++$test->c);
26+
27+
var_dump($test);
28+
29+
?>
30+
--EXPECT--
31+
get(a)
32+
set(a, 1)
33+
int(1)
34+
get(b)
35+
set(b, 1)
36+
int(0)
37+
get(c)
38+
set(c, 1)
39+
int(1)
40+
object(Test)#1 (3) {
41+
["a":protected]=>
42+
int(0)
43+
["b":protected]=>
44+
int(0)
45+
["c":protected]=>
46+
int(0)
47+
}

Zend/zend_execute.c

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,11 +1490,12 @@ static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object,
14901490
zval rv;
14911491

14921492
if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) {
1493-
zval *z, *zptr, obj;
1493+
zval *z, obj;
1494+
zval z_copy;
14941495

14951496
ZVAL_OBJ(&obj, Z_OBJ_P(object));
14961497
Z_ADDREF(obj);
1497-
zptr = z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv);
1498+
z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv);
14981499
if (UNEXPECTED(EG(exception))) {
14991500
OBJ_RELEASE(Z_OBJ(obj));
15001501
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
@@ -1512,18 +1513,23 @@ static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object,
15121513
}
15131514
ZVAL_COPY_VALUE(z, value);
15141515
}
1515-
ZVAL_DEREF(z);
1516+
if (UNEXPECTED(Z_TYPE_P(z) == IS_REFERENCE)) {
1517+
ZVAL_COPY(&z_copy, Z_REFVAL_P(z));
1518+
} else {
1519+
ZVAL_COPY(&z_copy, z);
1520+
}
15161521
if (inc) {
1517-
increment_function(z);
1522+
increment_function(&z_copy);
15181523
} else {
1519-
decrement_function(z);
1524+
decrement_function(&z_copy);
15201525
}
15211526
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
1522-
ZVAL_COPY(EX_VAR(opline->result.var), z);
1527+
ZVAL_COPY(EX_VAR(opline->result.var), &z_copy);
15231528
}
1524-
Z_OBJ_HT(obj)->write_property(&obj, property, z, cache_slot);
1529+
Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, cache_slot);
15251530
OBJ_RELEASE(Z_OBJ(obj));
1526-
zval_ptr_dtor(zptr);
1531+
zval_ptr_dtor(&z_copy);
1532+
zval_ptr_dtor(z);
15271533
} else {
15281534
zend_error(E_WARNING, "Attempt to increment/decrement property of non-object");
15291535
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
@@ -1535,8 +1541,7 @@ static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object,
15351541
static zend_never_inline void zend_assign_op_overloaded_property(zval *object, zval *property, void **cache_slot, zval *value, binary_op_type binary_op OPLINE_DC EXECUTE_DATA_DC)
15361542
{
15371543
zval *z;
1538-
zval rv, obj;
1539-
zval *zptr;
1544+
zval rv, obj, res;
15401545

15411546
ZVAL_OBJ(&obj, Z_OBJ_P(object));
15421547
Z_ADDREF(obj);
@@ -1558,14 +1563,13 @@ static zend_never_inline void zend_assign_op_overloaded_property(zval *object, z
15581563
}
15591564
ZVAL_COPY_VALUE(z, value);
15601565
}
1561-
zptr = z;
1562-
ZVAL_DEREF(z);
1563-
binary_op(z, z, value);
1564-
Z_OBJ_HT(obj)->write_property(&obj, property, z, cache_slot);
1566+
binary_op(&res, z, value);
1567+
Z_OBJ_HT(obj)->write_property(&obj, property, &res, cache_slot);
15651568
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
1566-
ZVAL_COPY(EX_VAR(opline->result.var), z);
1569+
ZVAL_COPY(EX_VAR(opline->result.var), &res);
15671570
}
1568-
zval_ptr_dtor(zptr);
1571+
zval_ptr_dtor(z);
1572+
zval_ptr_dtor(&res);
15691573
} else {
15701574
zend_error(E_WARNING, "Attempt to assign property of non-object");
15711575
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {

0 commit comments

Comments
 (0)