Skip to content

Commit 025ed70

Browse files
committed
Fix ReflectionProperty::isInitialized() for hooked props
In zend_std_has_property with ZEND_PROPERTY_EXISTS, we'd just return true when no get hook was present. However, this function is supposed to return false for uninitialized properties. PROPERTY_EXISTS is somewhat of a misnomer. Virtual properties continue to always return true, given there's no backing value to check. Fixes GH-15694 Closes GH-15822
1 parent bdcb218 commit 025ed70

File tree

3 files changed

+67
-7
lines changed

3 files changed

+67
-7
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ PHP NEWS
4949
- Reflection:
5050
. Fixed bug GH-15718 (Segfault on ReflectionProperty::get{Hook,Hooks}() on
5151
dynamic properties). (DanielEScherzer)
52+
. Fixed bug GH-15694 (ReflectionProperty::isInitialized() is incorrect for
53+
hooked properties). (ilutov)
5254

5355
- SOAP:
5456
. Fixed bug #61525 (SOAP functions require at least one space after HTTP

Zend/zend_object_handlers.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2208,6 +2208,15 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has
22082208
}
22092209
} else if (IS_HOOKED_PROPERTY_OFFSET(property_offset)) {
22102210
zend_function *get = prop_info->hooks[ZEND_PROPERTY_HOOK_GET];
2211+
2212+
if (has_set_exists == ZEND_PROPERTY_EXISTS) {
2213+
if (prop_info->flags & ZEND_ACC_VIRTUAL) {
2214+
return true;
2215+
}
2216+
property_offset = prop_info->offset;
2217+
goto try_again;
2218+
}
2219+
22112220
if (!get) {
22122221
if (prop_info->flags & ZEND_ACC_VIRTUAL) {
22132222
zend_throw_error(NULL, "Property %s::$%s is write-only",
@@ -2219,19 +2228,12 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has
22192228
}
22202229
}
22212230

2222-
if (has_set_exists == ZEND_PROPERTY_EXISTS) {
2223-
return 1;
2224-
}
2225-
22262231
zval rv;
22272232
if (!zend_call_get_hook(prop_info, name, get, zobj, &rv)) {
22282233
if (EG(exception)) {
22292234
return 0;
22302235
}
22312236
property_offset = prop_info->offset;
2232-
if (!ZEND_TYPE_IS_SET(prop_info->type)) {
2233-
prop_info = NULL;
2234-
}
22352237
goto try_again;
22362238
}
22372239

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
ReflectionProperty::isInitialized() on hooked properties
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
// Plain
8+
public $p1;
9+
public string $p2;
10+
// Virtual
11+
public $v1 { get => throw new Exception(); }
12+
public $v2 { set { throw new Exception(); } }
13+
// Backed
14+
public $b1 { get => throw new Exception($this->b1); }
15+
public string $b2 { get => throw new Exception($this->b2); }
16+
public $b3 { set => throw new Exception(); }
17+
public string $b4 { set => throw new Exception(); }
18+
}
19+
20+
$test = new Test();
21+
$rc = new ReflectionClass(Test::class);
22+
foreach ($rc->getProperties() as $rp) {
23+
echo $rp->getName(), "\n";
24+
var_dump($rp->isInitialized($test));
25+
try {
26+
$rp->setRawValue($test, 42);
27+
} catch (Error $e) {}
28+
var_dump($rp->isInitialized($test));
29+
}
30+
31+
?>
32+
--EXPECT--
33+
p1
34+
bool(true)
35+
bool(true)
36+
p2
37+
bool(false)
38+
bool(true)
39+
v1
40+
bool(true)
41+
bool(true)
42+
v2
43+
bool(true)
44+
bool(true)
45+
b1
46+
bool(true)
47+
bool(true)
48+
b2
49+
bool(false)
50+
bool(true)
51+
b3
52+
bool(true)
53+
bool(true)
54+
b4
55+
bool(false)
56+
bool(true)

0 commit comments

Comments
 (0)