diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2018-06-09 00:29:33 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2018-06-09 00:36:46 +0200 |
commit | 7498f0163b6141cfd56ab317e86a84065c0e887b (patch) | |
tree | c26f7c4ec5c0f2f65e5a0fc9ec1eed554bb6f39d | |
parent | befc518c82758f4167ca07841c3ea1af3ca78c0b (diff) | |
download | php-git-7498f0163b6141cfd56ab317e86a84065c0e887b.tar.gz |
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.
-rw-r--r-- | Zend/tests/overloaded_prop_assign_op_refs.phpt | 47 | ||||
-rw-r--r-- | Zend/zend_execute.c | 36 |
2 files changed, 67 insertions, 16 deletions
diff --git a/Zend/tests/overloaded_prop_assign_op_refs.phpt b/Zend/tests/overloaded_prop_assign_op_refs.phpt new file mode 100644 index 0000000000..3f39f4d2dd --- /dev/null +++ b/Zend/tests/overloaded_prop_assign_op_refs.phpt @@ -0,0 +1,47 @@ +--TEST-- +Handling of assign-ops and incdecs on overloaded properties using &__get() +--FILE-- +<?php + +class Test { + protected $a = 0; + protected $b = 0; + protected $c = 0; + + public function &__get($name) { + echo "get($name)\n"; + return $this->$name; + } + + public function __set($name, $value) { + echo "set($name, $value)\n"; + } +} + +$test = new Test; + +var_dump($test->a += 1); +var_dump($test->b++); +var_dump(++$test->c); + +var_dump($test); + +?> +--EXPECT-- +get(a) +set(a, 1) +int(1) +get(b) +set(b, 1) +int(0) +get(c) +set(c, 1) +int(1) +object(Test)#1 (3) { + ["a":protected]=> + int(0) + ["b":protected]=> + int(0) + ["c":protected]=> + int(0) +} diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index e863328121..9a170edd3d 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1490,11 +1490,12 @@ static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object, zval rv; if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, *zptr, obj; + zval *z, obj; + zval z_copy; ZVAL_OBJ(&obj, Z_OBJ_P(object)); Z_ADDREF(obj); - zptr = z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv); + z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv); if (UNEXPECTED(EG(exception))) { OBJ_RELEASE(Z_OBJ(obj)); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -1512,18 +1513,23 @@ static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object, } ZVAL_COPY_VALUE(z, value); } - ZVAL_DEREF(z); + if (UNEXPECTED(Z_TYPE_P(z) == IS_REFERENCE)) { + ZVAL_COPY(&z_copy, Z_REFVAL_P(z)); + } else { + ZVAL_COPY(&z_copy, z); + } if (inc) { - increment_function(z); + increment_function(&z_copy); } else { - decrement_function(z); + decrement_function(&z_copy); } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); + ZVAL_COPY(EX_VAR(opline->result.var), &z_copy); } - Z_OBJ_HT(obj)->write_property(&obj, property, z, cache_slot); + Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, cache_slot); OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(zptr); + zval_ptr_dtor(&z_copy); + zval_ptr_dtor(z); } else { zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -1535,8 +1541,7 @@ static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object, 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) { zval *z; - zval rv, obj; - zval *zptr; + zval rv, obj, res; ZVAL_OBJ(&obj, Z_OBJ_P(object)); Z_ADDREF(obj); @@ -1558,14 +1563,13 @@ static zend_never_inline void zend_assign_op_overloaded_property(zval *object, z } ZVAL_COPY_VALUE(z, value); } - zptr = z; - ZVAL_DEREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, cache_slot); + binary_op(&res, z, value); + Z_OBJ_HT(obj)->write_property(&obj, property, &res, cache_slot); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); + ZVAL_COPY(EX_VAR(opline->result.var), &res); } - zval_ptr_dtor(zptr); + zval_ptr_dtor(z); + zval_ptr_dtor(&res); } else { zend_error(E_WARNING, "Attempt to assign property of non-object"); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { |