1--TEST--
2Lazy objects: Pre-initialization reference source types are properly handled after initializer exception
3--FILE--
4<?php
5
6class C {
7    public ?C $a;
8    public ?C $b;
9    public $c;
10    public function __construct() {
11        unset($this->b);
12        throw new \Exception('initializer exception');
13    }
14}
15
16function test(string $name, object $obj) {
17    $reflector = new ReflectionClass(C::class);
18
19    printf("# %s:\n", $name);
20
21    $reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, null);
22    $refA = &$obj->a;
23    $reflector->getProperty('b')->setRawValueWithoutLazyInitialization($obj, null);
24    $refB = &$obj->b;
25
26    var_dump($obj);
27    try {
28        var_dump($obj->c);
29    } catch (\Exception $e) {
30        printf("%s: %s\n", $e::class, $e->getMessage());
31    }
32    var_dump($obj);
33
34    try {
35        // $refA retained its reference source type (except for the proxy
36        // case: it is the responsibility of the initializer to propagate
37        // pre-initialized properties to the instance)
38        $refA = 1;
39    } catch (\Error $e) {
40        printf("%s: %s\n", $e::class, $e->getMessage());
41    }
42
43    // source type was not duplicated
44    unset($obj->a);
45    $refA = 1;
46
47    try {
48        // $refB retained its reference source type
49        $refB = 1;
50    } catch (\Error $e) {
51        printf("%s: %s\n", $e::class, $e->getMessage());
52    }
53
54    // source type was not duplicated
55    unset($obj->b);
56    $refB = 1;
57
58}
59
60$reflector = new ReflectionClass(C::class);
61
62$obj = $reflector->newLazyGhost(function ($obj) {
63    var_dump("initializer");
64    $obj->__construct();
65});
66
67test('Ghost', $obj);
68
69$obj = $reflector->newLazyProxy(function ($obj) {
70    var_dump("initializer");
71    return new C(null);
72});
73
74test('Proxy', $obj);
75--EXPECTF--
76# Ghost:
77lazy ghost object(C)#%d (2) {
78  ["a"]=>
79  &NULL
80  ["b"]=>
81  &NULL
82}
83string(11) "initializer"
84Exception: initializer exception
85lazy ghost object(C)#%d (2) {
86  ["a"]=>
87  &NULL
88  ["b"]=>
89  &NULL
90}
91TypeError: Cannot assign int to reference held by property C::$a of type ?C
92TypeError: Cannot assign int to reference held by property C::$b of type ?C
93# Proxy:
94lazy proxy object(C)#%d (2) {
95  ["a"]=>
96  &NULL
97  ["b"]=>
98  &NULL
99}
100string(11) "initializer"
101Exception: initializer exception
102lazy proxy object(C)#%d (2) {
103  ["a"]=>
104  &NULL
105  ["b"]=>
106  &NULL
107}
108TypeError: Cannot assign int to reference held by property C::$a of type ?C
109TypeError: Cannot assign int to reference held by property C::$b of type ?C
110