1--TEST--
2Lazy objects: ReflectionProperty::skipLazyInitialization() prevent properties from triggering initializer
3--FILE--
4<?php
5
6#[\AllowDynamicProperties]
7class A {
8    private $priv = 'priv A';
9    private $privA = 'privA';
10    protected $prot = 'prot';
11    public $pubA = 'pubA';
12
13    public static $static = 'static';
14
15    public $noDefault;
16    public string $noDefaultTyped;
17    public $initialized;
18
19    public $hooked {
20        get { return $this->hooked; }
21        set ($value) { $this->hooked = strtoupper($value); }
22    }
23
24    public $virtual {
25        get { return 'virtual'; }
26        set ($value) { }
27    }
28}
29
30class B extends A {
31    private $priv = 'privB';
32    public $pubB = 'pubB';
33
34    private readonly string $readonly;
35}
36
37set_error_handler(function ($errno, $errstr) {
38    throw new Error($errstr);
39});
40
41function testProperty(callable $makeObj, $propReflector) {
42
43    $reflector = new ReflectionClass(B::class);
44
45    $getValue = function ($obj, $propReflector) {
46        $name = $propReflector->getName();
47        return $obj->$name;
48    };
49
50    printf("\n## %s", $propReflector);
51
52    printf("\nskipInitializerForProperty():\n");
53    $obj = $makeObj();
54    $skept = false;
55    try {
56        $propReflector->skipLazyInitialization($obj);
57        $skept = true;
58    } catch (ReflectionException $e) {
59        printf("%s: %s\n", $e::class, $e->getMessage());
60    }
61    if (!$reflector->isUninitializedLazyObject($obj)) {
62        printf("Object was unexpectedly initialized (1)\n");
63    }
64    if ($skept) {
65        try {
66            printf("getValue(): ");
67            var_dump($getValue($obj, $propReflector));
68        } catch (\Error $e) {
69            printf("%s: %s\n", $e::class, $e->getMessage());
70        }
71        if (!$propReflector->isStatic()) {
72            $propReflector->setValue($obj, '');
73        }
74        if (!$reflector->isUninitializedLazyObject($obj)) {
75            printf("Object was unexpectedly initialized (1)\n");
76        }
77    }
78
79    printf("\nsetRawValueWithoutLazyInitialization():\n");
80    $obj = $makeObj();
81    $skept = false;
82    try {
83        $propReflector->setRawValueWithoutLazyInitialization($obj, 'value');
84        $skept = true;
85    } catch (ReflectionException $e) {
86        printf("%s: %s\n", $e::class, $e->getMessage());
87    }
88    if (!$reflector->isUninitializedLazyObject($obj)) {
89        printf("Object was unexpectedly initialized (1)\n");
90    }
91    if ($skept) {
92        try {
93            printf("getValue(): ");
94            var_dump($getValue($obj, $propReflector));
95        } catch (\Error $e) {
96            printf("%s: %s\n", $e::class, $e->getMessage());
97        }
98        if (!$reflector->isUninitializedLazyObject($obj)) {
99            printf("Object was unexpectedly initialized (1)\n");
100        }
101    }
102}
103
104function test(string $name, callable $makeObj) {
105    printf("# %s:\n", $name);
106
107    $reflector = new ReflectionClass(B::class);
108
109    foreach ($reflector->getProperties() as $propReflector) {
110        testProperty($makeObj, $propReflector);
111    }
112
113    testProperty($makeObj, new class {
114        function getName() {
115            return 'dynamicProp';
116        }
117        function setValue($obj, $value) {
118            $obj->dynamicProp = $value;
119        }
120        function isStatic() {
121            return false;
122        }
123        // TODO: refactor this test
124        function skipLazyInitialization(object $object) {
125            throw new \ReflectionException();
126        }
127        function setRawValueWithoutLazyInitialization(object $object) {
128            throw new \ReflectionException();
129        }
130        function __toString() {
131            return "Property [ \$dynamicProp ]\n";
132        }
133    });
134}
135
136$reflector = new ReflectionClass(B::class);
137
138$factory = function () use ($reflector) {
139    return $reflector->newLazyGhost(function ($obj) {
140        throw new \Exception('initializer');
141    });
142};
143
144test('Ghost', $factory);
145
146$factory = function () use ($reflector) {
147    return $reflector->newLazyGhost(function ($obj) {
148        throw new \Exception('initializer');
149    });
150};
151
152test('Proxy', $factory);
153
154?>
155--EXPECT--
156# Ghost:
157
158## Property [ private $priv = 'privB' ]
159
160skipInitializerForProperty():
161getValue(): Error: Cannot access private property B::$priv
162
163setRawValueWithoutLazyInitialization():
164getValue(): Error: Cannot access private property B::$priv
165
166## Property [ public $pubB = 'pubB' ]
167
168skipInitializerForProperty():
169getValue(): string(4) "pubB"
170
171setRawValueWithoutLazyInitialization():
172getValue(): string(5) "value"
173
174## Property [ private readonly string $readonly ]
175
176skipInitializerForProperty():
177getValue(): Error: Cannot access private property B::$readonly
178
179setRawValueWithoutLazyInitialization():
180getValue(): Error: Cannot access private property B::$readonly
181
182## Property [ protected $prot = 'prot' ]
183
184skipInitializerForProperty():
185getValue(): Error: Cannot access protected property B::$prot
186
187setRawValueWithoutLazyInitialization():
188getValue(): Error: Cannot access protected property B::$prot
189
190## Property [ public $pubA = 'pubA' ]
191
192skipInitializerForProperty():
193getValue(): string(4) "pubA"
194
195setRawValueWithoutLazyInitialization():
196getValue(): string(5) "value"
197
198## Property [ public static $static = 'static' ]
199
200skipInitializerForProperty():
201ReflectionException: Can not use skipLazyInitialization on static property B::$static
202
203setRawValueWithoutLazyInitialization():
204ReflectionException: Can not use setRawValueWithoutLazyInitialization on static property B::$static
205
206## Property [ public $noDefault = NULL ]
207
208skipInitializerForProperty():
209getValue(): NULL
210
211setRawValueWithoutLazyInitialization():
212getValue(): string(5) "value"
213
214## Property [ public string $noDefaultTyped ]
215
216skipInitializerForProperty():
217getValue(): Error: Typed property A::$noDefaultTyped must not be accessed before initialization
218
219setRawValueWithoutLazyInitialization():
220getValue(): string(5) "value"
221
222## Property [ public $initialized = NULL ]
223
224skipInitializerForProperty():
225getValue(): NULL
226
227setRawValueWithoutLazyInitialization():
228getValue(): string(5) "value"
229
230## Property [ public $hooked = NULL ]
231
232skipInitializerForProperty():
233getValue(): NULL
234
235setRawValueWithoutLazyInitialization():
236getValue(): string(5) "value"
237
238## Property [ public $virtual ]
239
240skipInitializerForProperty():
241ReflectionException: Can not use skipLazyInitialization on virtual property B::$virtual
242
243setRawValueWithoutLazyInitialization():
244ReflectionException: Can not use setRawValueWithoutLazyInitialization on virtual property B::$virtual
245
246## Property [ $dynamicProp ]
247
248skipInitializerForProperty():
249ReflectionException:
250
251setRawValueWithoutLazyInitialization():
252ReflectionException:
253# Proxy:
254
255## Property [ private $priv = 'privB' ]
256
257skipInitializerForProperty():
258getValue(): Error: Cannot access private property B::$priv
259
260setRawValueWithoutLazyInitialization():
261getValue(): Error: Cannot access private property B::$priv
262
263## Property [ public $pubB = 'pubB' ]
264
265skipInitializerForProperty():
266getValue(): string(4) "pubB"
267
268setRawValueWithoutLazyInitialization():
269getValue(): string(5) "value"
270
271## Property [ private readonly string $readonly ]
272
273skipInitializerForProperty():
274getValue(): Error: Cannot access private property B::$readonly
275
276setRawValueWithoutLazyInitialization():
277getValue(): Error: Cannot access private property B::$readonly
278
279## Property [ protected $prot = 'prot' ]
280
281skipInitializerForProperty():
282getValue(): Error: Cannot access protected property B::$prot
283
284setRawValueWithoutLazyInitialization():
285getValue(): Error: Cannot access protected property B::$prot
286
287## Property [ public $pubA = 'pubA' ]
288
289skipInitializerForProperty():
290getValue(): string(4) "pubA"
291
292setRawValueWithoutLazyInitialization():
293getValue(): string(5) "value"
294
295## Property [ public static $static = 'static' ]
296
297skipInitializerForProperty():
298ReflectionException: Can not use skipLazyInitialization on static property B::$static
299
300setRawValueWithoutLazyInitialization():
301ReflectionException: Can not use setRawValueWithoutLazyInitialization on static property B::$static
302
303## Property [ public $noDefault = NULL ]
304
305skipInitializerForProperty():
306getValue(): NULL
307
308setRawValueWithoutLazyInitialization():
309getValue(): string(5) "value"
310
311## Property [ public string $noDefaultTyped ]
312
313skipInitializerForProperty():
314getValue(): Error: Typed property A::$noDefaultTyped must not be accessed before initialization
315
316setRawValueWithoutLazyInitialization():
317getValue(): string(5) "value"
318
319## Property [ public $initialized = NULL ]
320
321skipInitializerForProperty():
322getValue(): NULL
323
324setRawValueWithoutLazyInitialization():
325getValue(): string(5) "value"
326
327## Property [ public $hooked = NULL ]
328
329skipInitializerForProperty():
330getValue(): NULL
331
332setRawValueWithoutLazyInitialization():
333getValue(): string(5) "value"
334
335## Property [ public $virtual ]
336
337skipInitializerForProperty():
338ReflectionException: Can not use skipLazyInitialization on virtual property B::$virtual
339
340setRawValueWithoutLazyInitialization():
341ReflectionException: Can not use setRawValueWithoutLazyInitialization on virtual property B::$virtual
342
343## Property [ $dynamicProp ]
344
345skipInitializerForProperty():
346ReflectionException:
347
348setRawValueWithoutLazyInitialization():
349ReflectionException:
350