mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix lazy proxy calling set hook twice
Writing to an uninitialized lazy proxy will initialize the underlying object and then call zend_std_write_property() on it. If this happens inside a hook, zend_std_write_property() should not call the hook again but directly write to the property slot. This didn't previously work because zend_should_call_hook() would compare the parent frame containing the proxy to the underlying object. This is now handled explicitly. Fixes GH-18000 Closes GH-18001
This commit is contained in:
2
NEWS
2
NEWS
@@ -17,6 +17,8 @@ PHP NEWS
|
||||
get_object_vars()). (ilutov)
|
||||
. Fixed bug GH-17998 (Skipped lazy object initialization on primed
|
||||
SIMPLE_WRITE cache). (ilutov)
|
||||
. Fixed bug GH-17998 (Assignment to backing value in set hook of lazy proxy
|
||||
calls hook again). (ilutov)
|
||||
|
||||
- DOM:
|
||||
. Fixed bug GH-17991 (Assertion failure dom_attr_value_write). (nielsdos)
|
||||
|
||||
33
Zend/tests/property_hooks/gh18000.phpt
Normal file
33
Zend/tests/property_hooks/gh18000.phpt
Normal file
@@ -0,0 +1,33 @@
|
||||
--TEST--
|
||||
GH-18000: Lazy proxy calls set hook twice
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public $prop {
|
||||
set {
|
||||
echo "set\n";
|
||||
$this->prop = $value * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rc = new ReflectionClass(C::class);
|
||||
|
||||
$obj = $rc->newLazyProxy(function () {
|
||||
echo "init\n";
|
||||
return new C;
|
||||
});
|
||||
|
||||
function foo(C $c) {
|
||||
$c->prop = 1;
|
||||
var_dump($c->prop);
|
||||
}
|
||||
|
||||
foo($obj);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
set
|
||||
init
|
||||
int(2)
|
||||
@@ -673,9 +673,23 @@ static bool zend_is_in_hook(const zend_property_info *prop_info)
|
||||
|
||||
static bool zend_should_call_hook(const zend_property_info *prop_info, const zend_object *obj)
|
||||
{
|
||||
return !zend_is_in_hook(prop_info)
|
||||
/* execute_data and This are guaranteed to be set if zend_is_in_hook() returns true. */
|
||||
|| Z_OBJ(EG(current_execute_data)->This) != obj;
|
||||
if (!zend_is_in_hook(prop_info)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* execute_data and This are guaranteed to be set if zend_is_in_hook() returns true. */
|
||||
zend_object *parent_obj = Z_OBJ(EG(current_execute_data)->This);
|
||||
if (parent_obj == obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (zend_object_is_lazy_proxy(parent_obj)
|
||||
&& zend_lazy_object_initialized(parent_obj)
|
||||
&& zend_lazy_object_get_instance(parent_obj) == obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static ZEND_COLD void zend_throw_no_prop_backing_value_access(zend_string *class_name, zend_string *prop_name, bool is_read)
|
||||
|
||||
Reference in New Issue
Block a user