Skip to content

Commit 8466b8b

Browse files
committed
WIP: Handle magic methods and call __isset
1 parent 15c28c2 commit 8466b8b

File tree

3 files changed

+243
-5
lines changed

3 files changed

+243
-5
lines changed

ext/reflection/php_reflection.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6726,10 +6726,25 @@ ZEND_METHOD(ReflectionProperty, isReadable)
67266726
}
67276727
}
67286728

6729-
if (obj) {
6729+
if (obj && !prop->hooks) {
67306730
zval *prop_val = OBJ_PROP(obj, prop->offset);
6731-
if (Z_TYPE_P(prop_val) != IS_UNDEF && !(Z_PROP_FLAG_P(prop_val) & IS_PROP_REINITABLE)) {
6732-
RETURN_FALSE;
6731+
if ( Z_TYPE_P(prop_val) == IS_UNDEF) {
6732+
if (!obj->ce->__get || (Z_PROP_FLAG_P(prop_val) & IS_PROP_UNINIT)) {
6733+
RETURN_FALSE;
6734+
}
6735+
if (obj->ce->__isset) {
6736+
uint32_t *guard = zend_get_property_guard(obj, ref->unmangled_name);
6737+
if (!((*guard) & ZEND_GUARD_PROPERTY_ISSET)) {
6738+
GC_ADDREF(obj);
6739+
*guard |= ZEND_GUARD_PROPERTY_ISSET;
6740+
zval member;
6741+
ZVAL_STR(&member, ref->unmangled_name);
6742+
zend_call_known_instance_method_with_1_params(obj->ce->__isset, obj, return_value, &member);
6743+
*guard &= ~ZEND_GUARD_PROPERTY_ISSET;
6744+
OBJ_RELEASE(obj);
6745+
return;
6746+
}
6747+
}
67336748
}
67346749
}
67356750

ext/reflection/tests/ReflectionProperty_isReadable.phpt

Lines changed: 157 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,37 @@ class B extends A {
99
public $a;
1010
protected $b;
1111
private $c;
12-
public protected(set) int $d;
12+
public protected(set) int $d = 42;
1313
public $e { get => 42; }
1414
public $f { set {} }
15+
public int $g;
16+
public int $h = 42;
17+
public int $i;
18+
public int $j;
19+
20+
public function __construct() {
21+
unset($this->i);
22+
unset($this->j);
23+
}
24+
25+
public function __isset($name) {
26+
return $name === 'i';
27+
}
28+
29+
public function __get($name) {}
1530
}
1631

17-
class C extends B {}
32+
class C extends B {
33+
public function __get($name) {}
34+
}
1835

1936
$test = static function ($scope) {
2037
$rc = new ReflectionClass(B::class);
2138
foreach ($rc->getProperties() as $rp) {
2239
echo $rp->getName() . ' from ' . ($scope ?? 'global') . ': ';
2340
var_dump($rp->isReadable(null, $scope));
41+
echo $rp->getName() . ' from ' . ($scope ?? 'global') . ' (instance): ';
42+
var_dump($rp->isReadable(new B(), $scope));
2443
}
2544
};
2645

@@ -32,53 +51,189 @@ foreach (['A', 'B', 'C'] as $scope) {
3251
$test(null);
3352
$test->bindTo(null, null)('static');
3453

54+
$rp = new ReflectionProperty('B', 'a');
55+
$b = new B();
56+
echo $rp->getName() . ' from global (uninitialized): ';
57+
var_dump($rp->isReadable($b));
58+
$b->a = 42;
59+
echo $rp->getName() . ' from global (initialized): ';
60+
var_dump($rp->isReadable($b));
61+
unset($b->a);
62+
echo $rp->getName() . ' from global (unset): ';
63+
var_dump($rp->isReadable($b));
64+
65+
$rp = new ReflectionProperty('C', 'a');
66+
$c = new C();
67+
echo $rp->getName() . ' from global (uninitialized): ';
68+
var_dump($rp->isReadable($c));
69+
unset($c->a);
70+
echo $rp->getName() . ' from global (unset): ';
71+
var_dump($rp->isReadable($c));
72+
3573
?>
3674
--EXPECT--
3775
a from A: bool(true)
76+
a from A (instance): bool(true)
3877
b from A: bool(true)
78+
b from A (instance): bool(true)
3979
c from A: bool(false)
80+
c from A (instance): bool(false)
4081
d from A: bool(true)
82+
d from A (instance): bool(false)
4183
e from A: bool(true)
84+
e from A (instance): bool(true)
4285
f from A: bool(false)
86+
f from A (instance): bool(false)
87+
g from A: bool(true)
88+
g from A (instance): bool(false)
89+
h from A: bool(true)
90+
h from A (instance): bool(true)
91+
i from A: bool(true)
92+
i from A (instance): bool(true)
93+
j from A: bool(true)
94+
j from A (instance): bool(true)
4395
a from static: bool(true)
96+
a from static (instance): bool(true)
4497
b from static: bool(true)
98+
b from static (instance): bool(true)
4599
c from static: bool(false)
100+
c from static (instance): bool(false)
46101
d from static: bool(true)
102+
d from static (instance): bool(false)
47103
e from static: bool(true)
104+
e from static (instance): bool(true)
48105
f from static: bool(false)
106+
f from static (instance): bool(false)
107+
g from static: bool(true)
108+
g from static (instance): bool(false)
109+
h from static: bool(true)
110+
h from static (instance): bool(true)
111+
i from static: bool(true)
112+
i from static (instance): bool(true)
113+
j from static: bool(true)
114+
j from static (instance): bool(true)
49115
a from B: bool(true)
116+
a from B (instance): bool(true)
50117
b from B: bool(true)
118+
b from B (instance): bool(true)
51119
c from B: bool(true)
120+
c from B (instance): bool(true)
52121
d from B: bool(true)
122+
d from B (instance): bool(false)
53123
e from B: bool(true)
124+
e from B (instance): bool(true)
54125
f from B: bool(false)
126+
f from B (instance): bool(false)
127+
g from B: bool(true)
128+
g from B (instance): bool(false)
129+
h from B: bool(true)
130+
h from B (instance): bool(true)
131+
i from B: bool(true)
132+
i from B (instance): bool(true)
133+
j from B: bool(true)
134+
j from B (instance): bool(true)
55135
a from static: bool(true)
136+
a from static (instance): bool(true)
56137
b from static: bool(true)
138+
b from static (instance): bool(true)
57139
c from static: bool(true)
140+
c from static (instance): bool(true)
58141
d from static: bool(true)
142+
d from static (instance): bool(false)
59143
e from static: bool(true)
144+
e from static (instance): bool(true)
60145
f from static: bool(false)
146+
f from static (instance): bool(false)
147+
g from static: bool(true)
148+
g from static (instance): bool(false)
149+
h from static: bool(true)
150+
h from static (instance): bool(true)
151+
i from static: bool(true)
152+
i from static (instance): bool(true)
153+
j from static: bool(true)
154+
j from static (instance): bool(true)
61155
a from C: bool(true)
156+
a from C (instance): bool(true)
62157
b from C: bool(true)
158+
b from C (instance): bool(true)
63159
c from C: bool(false)
160+
c from C (instance): bool(false)
64161
d from C: bool(true)
162+
d from C (instance): bool(false)
65163
e from C: bool(true)
164+
e from C (instance): bool(true)
66165
f from C: bool(false)
166+
f from C (instance): bool(false)
167+
g from C: bool(true)
168+
g from C (instance): bool(false)
169+
h from C: bool(true)
170+
h from C (instance): bool(true)
171+
i from C: bool(true)
172+
i from C (instance): bool(true)
173+
j from C: bool(true)
174+
j from C (instance): bool(true)
67175
a from static: bool(true)
176+
a from static (instance): bool(true)
68177
b from static: bool(true)
178+
b from static (instance): bool(true)
69179
c from static: bool(false)
180+
c from static (instance): bool(false)
70181
d from static: bool(true)
182+
d from static (instance): bool(false)
71183
e from static: bool(true)
184+
e from static (instance): bool(true)
72185
f from static: bool(false)
186+
f from static (instance): bool(false)
187+
g from static: bool(true)
188+
g from static (instance): bool(false)
189+
h from static: bool(true)
190+
h from static (instance): bool(true)
191+
i from static: bool(true)
192+
i from static (instance): bool(true)
193+
j from static: bool(true)
194+
j from static (instance): bool(true)
73195
a from global: bool(true)
196+
a from global (instance): bool(true)
74197
b from global: bool(false)
198+
b from global (instance): bool(false)
75199
c from global: bool(false)
200+
c from global (instance): bool(false)
76201
d from global: bool(true)
202+
d from global (instance): bool(false)
77203
e from global: bool(true)
204+
e from global (instance): bool(true)
78205
f from global: bool(false)
206+
f from global (instance): bool(false)
207+
g from global: bool(true)
208+
g from global (instance): bool(false)
209+
h from global: bool(true)
210+
h from global (instance): bool(true)
211+
i from global: bool(true)
212+
i from global (instance): bool(true)
213+
j from global: bool(true)
214+
j from global (instance): bool(true)
79215
a from static: bool(true)
216+
a from static (instance): bool(true)
80217
b from static: bool(false)
218+
b from static (instance): bool(false)
81219
c from static: bool(false)
220+
c from static (instance): bool(false)
82221
d from static: bool(true)
222+
d from static (instance): bool(false)
83223
e from static: bool(true)
224+
e from static (instance): bool(true)
84225
f from static: bool(false)
226+
f from static (instance): bool(false)
227+
g from static: bool(true)
228+
g from static (instance): bool(false)
229+
h from static: bool(true)
230+
h from static (instance): bool(true)
231+
i from static: bool(true)
232+
i from static (instance): bool(true)
233+
j from static: bool(true)
234+
j from static (instance): bool(true)
235+
a from global (uninitialized): bool(true)
236+
a from global (initialized): bool(true)
237+
a from global (unset): bool(true)
238+
a from global (uninitialized): bool(true)
239+
a from global (unset): bool(true)

ext/reflection/tests/ReflectionProperty_isWritable.phpt

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class A {
1212
public $f { set {} }
1313
public readonly int $g;
1414
public private(set) int $h;
15+
public public(set) readonly int $i;
1516

1617
public function setG($g) {
1718
$this->g = $g;
@@ -24,6 +25,10 @@ class A {
2425
}
2526
}
2627

28+
class B extends A {
29+
public function __set($name, $value) {}
30+
}
31+
2732
$test = static function ($scope) {
2833
$rc = new ReflectionClass(A::class);
2934
foreach ($rc->getProperties() as $rp) {
@@ -37,6 +42,9 @@ $test = static function ($scope) {
3742
var_dump($rp->isWritable($a, $scope));
3843
clone $a;
3944
}
45+
46+
echo $rp->getName() . ' from ' . ($scope ?? 'global') . ' (null): ';
47+
var_dump($rp->isWritable(null, $scope));
4048
}
4149
};
4250

@@ -46,45 +54,105 @@ $test->bindTo(null, 'A')('static');
4654
$test(null);
4755
$test->bindTo(null, null)('static');
4856

57+
$rp = new ReflectionProperty('A', 'i');
58+
$a = new A();
59+
echo $rp->getName() . ' from global (uninitialized): ';
60+
var_dump($rp->isWritable($a));
61+
$a->i = 42;
62+
echo $rp->getName() . ' from global (initialized): ';
63+
var_dump($rp->isWritable($a));
64+
65+
$rp = new ReflectionProperty('B', 'i');
66+
$b = new B();
67+
echo $rp->getName() . ' from global (uninitialized): ';
68+
var_dump($rp->isWritable($b));
69+
unset($b->a);
70+
echo $rp->getName() . ' from global (unset): ';
71+
var_dump($rp->isWritable($b));
72+
4973
?>
5074
--EXPECT--
5175
a from A: bool(true)
76+
a from A (null): bool(true)
5277
b from A: bool(true)
78+
b from A (null): bool(true)
5379
c from A: bool(true)
80+
c from A (null): bool(true)
5481
d from A: bool(true)
82+
d from A (null): bool(true)
5583
e from A: bool(false)
84+
e from A (null): bool(false)
5685
f from A: bool(true)
86+
f from A (null): bool(true)
5787
g from A: bool(true)
5888
g from A (initialized): bool(false)
5989
g from static (initialized, clone): bool(true)
90+
g from A (null): bool(true)
6091
h from A: bool(true)
92+
h from A (null): bool(true)
93+
i from A: bool(true)
94+
i from A (null): bool(true)
6195
a from static: bool(true)
96+
a from static (null): bool(true)
6297
b from static: bool(true)
98+
b from static (null): bool(true)
6399
c from static: bool(true)
100+
c from static (null): bool(true)
64101
d from static: bool(true)
102+
d from static (null): bool(true)
65103
e from static: bool(false)
104+
e from static (null): bool(false)
66105
f from static: bool(true)
106+
f from static (null): bool(true)
67107
g from static: bool(true)
68108
g from static (initialized): bool(false)
69109
g from static (initialized, clone): bool(true)
110+
g from static (null): bool(true)
70111
h from static: bool(true)
112+
h from static (null): bool(true)
113+
i from static: bool(true)
114+
i from static (null): bool(true)
71115
a from global: bool(true)
116+
a from global (null): bool(true)
72117
b from global: bool(false)
118+
b from global (null): bool(false)
73119
c from global: bool(false)
120+
c from global (null): bool(false)
74121
d from global: bool(false)
122+
d from global (null): bool(false)
75123
e from global: bool(false)
124+
e from global (null): bool(false)
76125
f from global: bool(true)
126+
f from global (null): bool(true)
77127
g from global: bool(false)
78128
g from global (initialized): bool(false)
79129
g from static (initialized, clone): bool(true)
130+
g from global (null): bool(false)
80131
h from global: bool(false)
132+
h from global (null): bool(false)
133+
i from global: bool(true)
134+
i from global (null): bool(true)
81135
a from static: bool(true)
136+
a from static (null): bool(true)
82137
b from static: bool(false)
138+
b from static (null): bool(false)
83139
c from static: bool(false)
140+
c from static (null): bool(false)
84141
d from static: bool(false)
142+
d from static (null): bool(false)
85143
e from static: bool(false)
144+
e from static (null): bool(false)
86145
f from static: bool(true)
146+
f from static (null): bool(true)
87147
g from static: bool(false)
88148
g from static (initialized): bool(false)
89149
g from static (initialized, clone): bool(true)
150+
g from static (null): bool(false)
90151
h from static: bool(false)
152+
h from static (null): bool(false)
153+
i from static: bool(true)
154+
i from static (null): bool(true)
155+
i from global (uninitialized): bool(true)
156+
i from global (initialized): bool(false)
157+
i from global (uninitialized): bool(true)
158+
i from global (unset): bool(true)

0 commit comments

Comments
 (0)