Skip to content

Commit 7bfb95f

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

6 files changed

+257
-91
lines changed

ext/reflection/php_reflection.c

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6639,10 +6639,6 @@ static zend_result get_ce_from_scope_name(zend_class_entry **scope, zend_string
66396639
*scope = NULL;
66406640
return SUCCESS;
66416641
}
6642-
if (zend_string_equals(scope_name, ZSTR_KNOWN(ZEND_STR_STATIC))) {
6643-
*scope = EX(prev_execute_data)->func->common.scope;
6644-
return SUCCESS;
6645-
}
66466642

66476643
*scope = zend_lookup_class(scope_name);
66486644
if (!*scope) {
@@ -6726,10 +6722,25 @@ ZEND_METHOD(ReflectionProperty, isReadable)
67266722
}
67276723
}
67286724

6729-
if (obj) {
6725+
if (obj && !prop->hooks) {
67306726
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;
6727+
if ( Z_TYPE_P(prop_val) == IS_UNDEF) {
6728+
if (!obj->ce->__get || (Z_PROP_FLAG_P(prop_val) & IS_PROP_UNINIT)) {
6729+
RETURN_FALSE;
6730+
}
6731+
if (obj->ce->__isset) {
6732+
uint32_t *guard = zend_get_property_guard(obj, ref->unmangled_name);
6733+
if (!((*guard) & ZEND_GUARD_PROPERTY_ISSET)) {
6734+
GC_ADDREF(obj);
6735+
*guard |= ZEND_GUARD_PROPERTY_ISSET;
6736+
zval member;
6737+
ZVAL_STR(&member, ref->unmangled_name);
6738+
zend_call_known_instance_method_with_1_params(obj->ce->__isset, obj, return_value, &member);
6739+
*guard &= ~ZEND_GUARD_PROPERTY_ISSET;
6740+
OBJ_RELEASE(obj);
6741+
return;
6742+
}
6743+
}
67336744
}
67346745
}
67356746

ext/reflection/tests/ReflectionProperty_isReadable.phpt

Lines changed: 0 additions & 84 deletions
This file was deleted.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Test ReflectionProperty::isReadable() hooks
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public $a { get => $this->a; }
8+
public $b { get => 42; }
9+
public $c { set {} }
10+
}
11+
12+
$test = static function ($scope) {
13+
$rc = new ReflectionClass(A::class);
14+
foreach ($rc->getProperties() as $rp) {
15+
echo $rp->getName() . ' from ' . ($scope ?? 'global') . ': ';
16+
var_dump($rp->isReadable(null, $scope));
17+
}
18+
};
19+
20+
$test('A');
21+
$test(null);
22+
23+
?>
24+
--EXPECT--
25+
a from A: bool(true)
26+
b from A: bool(true)
27+
c from A: bool(false)
28+
a from global: bool(true)
29+
b from global: bool(true)
30+
c from global: bool(false)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
--TEST--
2+
Test ReflectionProperty::isReadable() value checks
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public $a;
8+
public int $b;
9+
public int $c = 42;
10+
public int $d;
11+
public int $e;
12+
13+
public function __construct() {
14+
unset($this->e);
15+
}
16+
}
17+
18+
class B {
19+
public int $f;
20+
public int $g;
21+
public int $h;
22+
23+
public function __construct() {
24+
unset($this->g);
25+
unset($this->h);
26+
}
27+
28+
public function __isset($name) {
29+
return $name === 'h';
30+
}
31+
32+
public function __get($name) {}
33+
}
34+
35+
class C {
36+
public int $i;
37+
public int $j;
38+
public int $k;
39+
40+
public function __construct() {
41+
unset($this->j);
42+
unset($this->k);
43+
}
44+
45+
public function __get($name) {}
46+
}
47+
48+
$test = static function ($class) {
49+
$rc = new ReflectionClass($class);
50+
foreach ($rc->getProperties() as $rp) {
51+
echo $rp->getName() . ' from global: ';
52+
var_dump($rp->isReadable(new $class, NULL));
53+
}
54+
};
55+
56+
$test('A');
57+
$test('B');
58+
$test('C');
59+
60+
?>
61+
--EXPECT--
62+
a from global: bool(true)
63+
b from global: bool(false)
64+
c from global: bool(true)
65+
d from global: bool(false)
66+
e from global: bool(false)
67+
f from global: bool(false)
68+
g from global: bool(false)
69+
h from global: bool(true)
70+
i from global: bool(false)
71+
j from global: bool(true)
72+
k from global: bool(true)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--TEST--
2+
Test ReflectionProperty::isReadable() visibility
3+
--FILE--
4+
<?php
5+
6+
class A {}
7+
8+
class B extends A {
9+
public $a;
10+
protected $b;
11+
private $c;
12+
public protected(set) int $d;
13+
}
14+
15+
class C extends B {}
16+
17+
$test = static function ($scope) {
18+
$rc = new ReflectionClass(B::class);
19+
foreach ($rc->getProperties() as $rp) {
20+
echo $rp->getName() . ' from ' . ($scope ?? 'global') . ': ';
21+
var_dump($rp->isReadable(null, $scope));
22+
}
23+
};
24+
25+
foreach (['A', 'B', 'C'] as $scope) {
26+
$test($scope);
27+
}
28+
$test(null);
29+
30+
?>
31+
--EXPECT--
32+
a from A: bool(true)
33+
b from A: bool(true)
34+
c from A: bool(false)
35+
d from A: bool(true)
36+
a from B: bool(true)
37+
b from B: bool(true)
38+
c from B: bool(true)
39+
d from B: bool(true)
40+
a from C: bool(true)
41+
b from C: bool(true)
42+
c from C: bool(false)
43+
d from C: bool(true)
44+
a from global: bool(true)
45+
b from global: bool(false)
46+
c from global: bool(false)
47+
d from global: bool(true)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
--TEST--
2+
Test ReflectionProperty::isWritable() visibility check
3+
--FILE--
4+
<?php
5+
6+
class A {}
7+
8+
class B extends A {
9+
public $a;
10+
protected $b;
11+
private $c;
12+
public private(set) int $d;
13+
public protected(set) int $e;
14+
public readonly int $f;
15+
public readonly int $g;
16+
17+
public function __construct() {
18+
$this->f = 42;
19+
}
20+
}
21+
22+
class C extends B {}
23+
24+
$test = static function ($class, $scope) {
25+
$rc = new ReflectionClass(B::class);
26+
foreach ($rc->getProperties() as $rp) {
27+
echo $rp->getName() . ' from ' . ($scope ?? 'global') . ($class) . ': ';
28+
var_dump($rp->isWritable($class ? new $class : null, $scope));
29+
}
30+
};
31+
32+
$test(null, 'A');
33+
$test('B', 'B');
34+
$test('B', null);
35+
$test(null, 'B');
36+
$test(null, null);
37+
$test('C', 'C');
38+
$test('C', null);
39+
40+
?>
41+
--EXPECT--
42+
a from A: bool(true)
43+
b from A: bool(true)
44+
c from A: bool(false)
45+
d from A: bool(false)
46+
e from A: bool(true)
47+
f from A: bool(true)
48+
g from A: bool(true)
49+
a from B: bool(true)
50+
b from B: bool(true)
51+
c from B: bool(true)
52+
d from B: bool(true)
53+
e from B: bool(true)
54+
f from B: bool(false)
55+
g from B: bool(true)
56+
a from global: bool(true)
57+
b from global: bool(false)
58+
c from global: bool(false)
59+
d from global: bool(false)
60+
e from global: bool(false)
61+
f from global: bool(false)
62+
g from global: bool(false)
63+
a from B: bool(true)
64+
b from B: bool(true)
65+
c from B: bool(true)
66+
d from B: bool(true)
67+
e from B: bool(true)
68+
f from B: bool(true)
69+
g from B: bool(true)
70+
a from global: bool(true)
71+
b from global: bool(false)
72+
c from global: bool(false)
73+
d from global: bool(false)
74+
e from global: bool(false)
75+
f from global: bool(false)
76+
g from global: bool(false)
77+
a from C: bool(true)
78+
b from C: bool(true)
79+
c from C: bool(false)
80+
d from C: bool(false)
81+
e from C: bool(true)
82+
f from C: bool(false)
83+
g from C: bool(true)
84+
a from global: bool(true)
85+
b from global: bool(false)
86+
c from global: bool(false)
87+
d from global: bool(false)
88+
e from global: bool(false)
89+
f from global: bool(false)
90+
g from global: bool(false)

0 commit comments

Comments
 (0)