1--TEST--
2Lazy objects: Foreach initializes object
3--FILE--
4<?php
5
6#[AllowDynamicProperties]
7class C {
8    public int $a;
9    private int $_b;
10    public int $b {
11        &get { $ref = &$this->_b; return $ref; }
12    }
13    public function __construct(bool $addDynamic = true) {
14        var_dump(__METHOD__);
15        $this->a = 1;
16        $this->_b = 2;
17        if ($addDynamic) {
18            $this->c = 3;
19            $this->d = 4;
20            unset($this->c);
21        }
22    }
23}
24
25$reflector = new ReflectionClass(C::class);
26
27function test(string $name, object $obj) {
28    printf("# %s:\n", $name);
29    foreach ($obj as $prop => $value) {
30        var_dump($prop, $value);
31    }
32    foreach ($obj as $prop => &$value) {
33        var_dump($prop, $value);
34    }
35}
36
37$obj = $reflector->newLazyGhost(function ($obj) {
38    var_dump("initializer");
39    $obj->__construct();
40});
41
42test('Ghost', $obj);
43
44$obj = $reflector->newLazyProxy(function ($obj) {
45    var_dump("initializer");
46    return new C();
47});
48
49test('Proxy', $obj);
50
51$obj = $reflector->newLazyGhost(function ($obj) {
52    var_dump("initializer");
53    $obj->__construct(addDynamic: false);
54});
55
56test('Ghost (no dynamic)', $obj);
57
58$obj = $reflector->newLazyProxy(function ($obj) {
59    var_dump("initializer");
60    return new C(addDynamic: false);
61});
62
63test('Proxy (no dynamic)', $obj);
64
65print "# Proxy of proxy (initialization)\n";
66
67$obj = $reflector->newLazyProxy(function ($obj) use (&$obj2, $reflector) {
68    var_dump("initializer");
69    return $obj2 = new C();
70});
71$reflector->initializeLazyObject($obj);
72$reflector->resetAsLazyProxy($obj2, function () {
73    return new C();
74});
75
76test('Proxy of proxy', $obj);
77
78print "# Ghost (init exception):\n";
79
80$obj = $reflector->newLazyGhost(function ($obj) {
81    throw new \Exception("initializer");
82});
83
84try {
85    var_dump(json_encode($obj));
86} catch (\Exception $e) {
87    printf("%s: %s\n", $e::class, $e->getMessage());
88}
89
90print "# Proxy (init exception):\n";
91
92$obj = $reflector->newLazyProxy(function ($obj) {
93    throw new \Exception("initializer");
94});
95
96try {
97    var_dump(json_encode($obj));
98} catch (\Exception $e) {
99    printf("%s: %s\n", $e::class, $e->getMessage());
100}
101
102--EXPECT--
103# Ghost:
104string(11) "initializer"
105string(14) "C::__construct"
106string(1) "a"
107int(1)
108string(1) "b"
109int(2)
110string(1) "d"
111int(4)
112string(1) "a"
113int(1)
114string(1) "b"
115int(2)
116string(1) "d"
117int(4)
118# Proxy:
119string(11) "initializer"
120string(14) "C::__construct"
121string(1) "a"
122int(1)
123string(1) "b"
124int(2)
125string(1) "d"
126int(4)
127string(1) "a"
128int(1)
129string(1) "b"
130int(2)
131string(1) "d"
132int(4)
133# Ghost (no dynamic):
134string(11) "initializer"
135string(14) "C::__construct"
136string(1) "a"
137int(1)
138string(1) "b"
139int(2)
140string(1) "a"
141int(1)
142string(1) "b"
143int(2)
144# Proxy (no dynamic):
145string(11) "initializer"
146string(14) "C::__construct"
147string(1) "a"
148int(1)
149string(1) "b"
150int(2)
151string(1) "a"
152int(1)
153string(1) "b"
154int(2)
155# Proxy of proxy (initialization)
156string(11) "initializer"
157string(14) "C::__construct"
158# Proxy of proxy:
159string(14) "C::__construct"
160string(1) "a"
161int(1)
162string(1) "b"
163int(2)
164string(1) "d"
165int(4)
166string(1) "a"
167int(1)
168string(1) "b"
169int(2)
170string(1) "d"
171int(4)
172# Ghost (init exception):
173Exception: initializer
174# Proxy (init exception):
175Exception: initializer
176