Skip to content

Commit 9d94999

Browse files
committed
Fix lazy objects API: add ReflectionProperty::getRawValueWithoutLazyInitialization(), ReflectionProperty::isLazy()
1 parent cfd954f commit 9d94999

File tree

6 files changed

+465
-17
lines changed

6 files changed

+465
-17
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
--TEST--
2+
Lazy objects: getRawValueWithoutLazyInitialization() fetches raw value without triggering initialization
3+
--FILE--
4+
<?php
5+
6+
class C {
7+
public int $a;
8+
public $b = 2;
9+
}
10+
11+
$reflector = new ReflectionClass(C::class);
12+
$propA = $reflector->getProperty('a');
13+
$propB = $reflector->getProperty('b');
14+
15+
$obj = $reflector->newLazyGhost(function () {
16+
throw new \Exception('initializer');
17+
});
18+
19+
print "# Ghost: Lazy properties\n";
20+
21+
$isLazy = null;
22+
var_dump($propA->getRawValueWithoutLazyInitialization($obj, $isLazy));
23+
var_dump($isLazy);
24+
25+
$isLazy = null;
26+
var_dump($propB->getRawValueWithoutLazyInitialization($obj, $isLazy));
27+
var_dump($isLazy);
28+
29+
print "# Ghost: Initialized properties\n";
30+
31+
$propA->setRawValueWithoutLazyInitialization($obj, 1);
32+
33+
$isLazy = null;
34+
var_dump($propA->getRawValueWithoutLazyInitialization($obj, $isLazy));
35+
var_dump($isLazy);
36+
37+
$propB->skipLazyInitialization($obj);
38+
39+
$isLazy = null;
40+
var_dump($propB->getRawValueWithoutLazyInitialization($obj, $isLazy));
41+
var_dump($isLazy);
42+
43+
$obj = $reflector->newLazyProxy(function () {
44+
throw new \Exception('initializer');
45+
});
46+
47+
print "# Proxy: Lazy properties\n";
48+
49+
$isLazy = null;
50+
var_dump($propA->getRawValueWithoutLazyInitialization($obj, $isLazy));
51+
var_dump($isLazy);
52+
53+
$isLazy = null;
54+
var_dump($propB->getRawValueWithoutLazyInitialization($obj, $isLazy));
55+
var_dump($isLazy);
56+
57+
print "# Proxy: Initialized properties\n";
58+
59+
$propA->setRawValueWithoutLazyInitialization($obj, 1);
60+
61+
$isLazy = null;
62+
var_dump($propA->getRawValueWithoutLazyInitialization($obj, $isLazy));
63+
var_dump($isLazy);
64+
65+
$propB->skipLazyInitialization($obj);
66+
67+
$isLazy = null;
68+
var_dump($propB->getRawValueWithoutLazyInitialization($obj, $isLazy));
69+
var_dump($isLazy);
70+
71+
$obj = $reflector->newLazyProxy(function () {
72+
return new C();
73+
});
74+
$reflector->initializeLazyObject($obj);
75+
76+
print "# Initialized Proxy\n";
77+
78+
try {
79+
$propA->getRawValueWithoutLazyInitialization($obj, $isLazy);
80+
} catch (Error $e) {
81+
printf("%s: %s\n", $e::class, $e->getMessage());
82+
}
83+
84+
$isLazy = null;
85+
var_dump($propB->getRawValueWithoutLazyInitialization($obj, $isLazy));
86+
var_dump($isLazy);
87+
88+
?>
89+
--EXPECT--
90+
# Ghost: Lazy properties
91+
NULL
92+
bool(true)
93+
NULL
94+
bool(true)
95+
# Ghost: Initialized properties
96+
int(1)
97+
bool(false)
98+
int(2)
99+
bool(false)
100+
# Proxy: Lazy properties
101+
NULL
102+
bool(true)
103+
NULL
104+
bool(true)
105+
# Proxy: Initialized properties
106+
int(1)
107+
bool(false)
108+
int(2)
109+
bool(false)
110+
# Initialized Proxy
111+
Error: Typed property C::$a must not be accessed before initialization
112+
int(2)
113+
bool(false)
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
--TEST--
2+
Lazy objects: getRawValueWithoutLazyInitialization() behaves like getRawValue() on non-lazy objects
3+
--FILE--
4+
<?php
5+
6+
#[AllowDynamicProperties]
7+
class C {
8+
public static $static = 'static';
9+
10+
public int $a;
11+
public $b;
12+
public $c {
13+
get { return 'c'; }
14+
}
15+
public $d {
16+
get { return $this->d; }
17+
set($value) { $this->d = $value; }
18+
}
19+
}
20+
21+
class D extends C {
22+
public function __get($name) {
23+
return ord($name);
24+
}
25+
}
26+
27+
$reflector = new ReflectionClass(C::class);
28+
$propA = $reflector->getProperty('a');
29+
$propB = $reflector->getProperty('b');
30+
$propC = $reflector->getProperty('c');
31+
$propD = $reflector->getProperty('d');
32+
$propStatic = $reflector->getProperty('static');
33+
34+
$obj = new C();
35+
$obj->dynamic = 1;
36+
$propDynamic = new ReflectionProperty($obj, 'dynamic');
37+
38+
$obj = new C();
39+
40+
print "# Non initialized properties\n";
41+
42+
try {
43+
$propA->getRawValueWithoutLazyInitialization($obj);
44+
} catch (Error $e) {
45+
printf("%s: %s\n", $e::class, $e->getMessage());
46+
}
47+
48+
var_dump($propB->getRawValueWithoutLazyInitialization($obj));
49+
50+
try {
51+
var_dump($propC->getRawValueWithoutLazyInitialization($obj));
52+
} catch (Error $e) {
53+
printf("%s: %s\n", $e::class, $e->getMessage());
54+
}
55+
56+
var_dump($propD->getRawValueWithoutLazyInitialization($obj));
57+
58+
print "# Initialized properties\n";
59+
60+
$obj->a = 1;
61+
$obj->b = new stdClass;
62+
$obj->d = 4;
63+
64+
var_dump($propA->getRawValueWithoutLazyInitialization($obj));
65+
66+
var_dump($propB->getRawValueWithoutLazyInitialization($obj));
67+
68+
try {
69+
var_dump($propC->getRawValueWithoutLazyInitialization($obj));
70+
} catch (Error $e) {
71+
printf("%s: %s\n", $e::class, $e->getMessage());
72+
}
73+
74+
var_dump($propD->getRawValueWithoutLazyInitialization($obj));
75+
76+
try {
77+
$propStatic->getRawValueWithoutLazyInitialization($obj);
78+
} catch (ReflectionException $e) {
79+
printf("%s: %s\n", $e::class, $e->getMessage());
80+
}
81+
82+
var_dump($propDynamic->getRawValueWithoutLazyInitialization($obj));
83+
84+
print "# Unset properties and __get()\n";
85+
86+
$obj = new D();
87+
unset($obj->a);
88+
unset($obj->b);
89+
90+
var_dump($propA->getRawValueWithoutLazyInitialization($obj));
91+
92+
var_dump($propB->getRawValueWithoutLazyInitialization($obj));
93+
94+
print "# References\n";
95+
96+
$obj = new C();
97+
$obj->b = &$obj;
98+
99+
var_dump($propB->getRawValueWithoutLazyInitialization($obj));
100+
101+
print "# Internal class\n";
102+
103+
$reflector = new ReflectionClass(Exception::class);
104+
$propMessage = $reflector->getProperty('message');
105+
106+
$obj = new Exception('hello');
107+
108+
var_dump($propMessage->getRawValueWithoutLazyInitialization($obj));
109+
110+
?>
111+
--EXPECTF--
112+
# Non initialized properties
113+
Error: Typed property C::$a must not be accessed before initialization
114+
NULL
115+
Error: Must not read from virtual property C::$c
116+
NULL
117+
# Initialized properties
118+
int(1)
119+
object(stdClass)#%d (0) {
120+
}
121+
Error: Must not read from virtual property C::$c
122+
int(4)
123+
ReflectionException: May not use getRawValueWithoutLazyInitialization on static properties
124+
125+
Warning: Undefined property: C::$dynamic in %s on line %d
126+
NULL
127+
# Unset properties and __get()
128+
int(97)
129+
int(98)
130+
# References
131+
object(C)#%d (2) {
132+
["a"]=>
133+
uninitialized(int)
134+
["b"]=>
135+
*RECURSION*
136+
["d"]=>
137+
NULL
138+
}
139+
# Internal class
140+
string(5) "hello"
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
--TEST--
2+
Lazy Objects: ReflectionProperty::isLazy()
3+
--FILE--
4+
<?php
5+
6+
#[AllowDynamicProperties]
7+
class C {
8+
public static $staticProp;
9+
public int $typed;
10+
public $untyped;
11+
public $virtual {
12+
get {}
13+
}
14+
}
15+
16+
function testProps(ReflectionClass $reflector, object $obj) {
17+
foreach (['staticProp', 'typed', 'untyped', 'virtual', 'dynamic'] as $name) {
18+
if ('dynamic' === $name) {
19+
$tmp = new C();
20+
$tmp->dynamic = 1;
21+
$pr = new ReflectionProperty($tmp, $name);
22+
} else {
23+
$pr = $reflector->getProperty($name);
24+
}
25+
printf("%s: %d\n", $name, $pr->isLazy($obj));
26+
}
27+
}
28+
29+
$reflector = new ReflectionClass(C::class);
30+
31+
print "# Ghost\n";
32+
33+
$obj = $reflector->newLazyGhost(function () { });
34+
35+
testProps($reflector, $obj);
36+
37+
$pr = $reflector->getProperty('typed');
38+
$pr->skipLazyInitialization($obj);
39+
printf("typed (skipped): %d\n", $pr->isLazy($obj));
40+
41+
print "# Initialized Ghost\n";
42+
43+
$reflector->initializeLazyObject($obj);
44+
45+
testProps($reflector, $obj);
46+
47+
print "# Proxy\n";
48+
49+
$obj = $reflector->newLazyProxy(function () {
50+
return new C();
51+
});
52+
53+
testProps($reflector, $obj);
54+
55+
$pr = $reflector->getProperty('typed');
56+
$pr->skipLazyInitialization($obj);
57+
printf("typed (skipped prop): %d\n", $pr->isLazy($obj));
58+
59+
print "# Initialized Proxy\n";
60+
61+
$reflector->initializeLazyObject($obj);
62+
63+
testProps($reflector, $obj);
64+
65+
print "# Internal\n";
66+
67+
$obj = (new DateTime())->diff(new DateTime());
68+
$reflector = new ReflectionClass(DateInterval::class);
69+
$pr = new ReflectionProperty($obj, 'y');
70+
printf("y: %d\n", $pr->isLazy($obj));
71+
72+
?>
73+
--EXPECT--
74+
# Ghost
75+
staticProp: 0
76+
typed: 1
77+
untyped: 1
78+
virtual: 0
79+
dynamic: 0
80+
typed (skipped): 0
81+
# Initialized Ghost
82+
staticProp: 0
83+
typed: 0
84+
untyped: 0
85+
virtual: 0
86+
dynamic: 0
87+
# Proxy
88+
staticProp: 0
89+
typed: 1
90+
untyped: 1
91+
virtual: 0
92+
dynamic: 0
93+
typed (skipped prop): 0
94+
# Initialized Proxy
95+
staticProp: 0
96+
typed: 0
97+
untyped: 0
98+
virtual: 0
99+
dynamic: 0
100+
# Internal
101+
y: 0

0 commit comments

Comments
 (0)