Skip to content

Commit a87b698

Browse files
committed
fix: keep lazy init inside __get guard, move above deprecation checks
The previous commit hoisted the lazy init check above the __get guard conditional, which caused proxy initialization even when __get should handle the property access. Move the check back inside the guard but before the deprecation/error handling so dynamic property deprecation still only fires once (on the instance, not the proxy). Add regression test for proxy-with-__get not initializing on dynamic property access (from Ilija's review).
1 parent 13c6331 commit a87b698

3 files changed

Lines changed: 49 additions & 19 deletions

File tree

Zend/tests/lazy_objects/gh20875.phpt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ Warning: Undefined variable $a in %s on line %d
3131

3232
Warning: Undefined variable $v in %s on line %d
3333

34+
Notice: Indirect modification of overloaded property A::$b has no effect in %s on line %d
35+
36+
Warning: Undefined variable $x in %s on line %d
37+
38+
Notice: Object of class stdClass could not be converted to int in %s on line %d
39+
40+
Warning: Undefined variable $v in %s on line %d
41+
3442
Notice: Indirect modification of overloaded property A::$f has no effect in %s on line %d
3543

3644
Fatal error: Uncaught Error: Cannot assign by reference to overloaded object in %s:%d
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
GH-20875 (Lazy proxy should not initialize when __get handles dynamic property access)
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $_;
8+
9+
public function &__get($name) {
10+
echo "__get\n";
11+
return $name;
12+
}
13+
}
14+
15+
$proxy = (new ReflectionClass(Foo::class))->newLazyProxy(function () {
16+
echo "init\n";
17+
return new Foo();
18+
});
19+
$x = &$proxy->x;
20+
21+
?>
22+
--EXPECT--
23+
__get

Zend/zend_object_handlers.c

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,28 +1459,27 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam
14591459
return retval;
14601460
}
14611461
}
1462-
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
1463-
bool guarded = zobj->ce->__get
1464-
&& (*zend_get_property_guard(zobj, name) & IN_GET);
1465-
zend_object *instance = zend_lazy_object_init(zobj);
1466-
if (!instance) {
1467-
return &EG(error_zval);
1468-
}
1462+
if (EXPECTED(!zobj->ce->__get) ||
1463+
UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) {
1464+
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
1465+
bool guarded = (zobj->ce->__get != NULL);
1466+
zend_object *instance = zend_lazy_object_init(zobj);
1467+
if (!instance) {
1468+
return &EG(error_zval);
1469+
}
14691470

1470-
if (guarded && (instance->ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
1471-
uint32_t *guard = zend_get_property_guard(instance, name);
1472-
if (!(*guard & IN_GET)) {
1473-
(*guard) |= IN_GET;
1474-
retval = zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
1475-
(*guard) &= ~IN_GET;
1476-
return retval;
1471+
if (guarded && (instance->ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
1472+
uint32_t *guard = zend_get_property_guard(instance, name);
1473+
if (!(*guard & IN_GET)) {
1474+
(*guard) |= IN_GET;
1475+
retval = zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
1476+
(*guard) &= ~IN_GET;
1477+
return retval;
1478+
}
14771479
}
1478-
}
14791480

1480-
return zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
1481-
}
1482-
if (EXPECTED(!zobj->ce->__get) ||
1483-
UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) {
1481+
return zend_std_get_property_ptr_ptr(instance, name, type, cache_slot);
1482+
}
14841483
if (UNEXPECTED(zobj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) {
14851484
zend_forbidden_dynamic_property(zobj->ce, name);
14861485
return &EG(error_zval);

0 commit comments

Comments
 (0)