Skip to content

Commit 75c65bf

Browse files
committed
Fix lazy object foreach edge cases
- Fix foreach on a object whose initialization failed before - Do not allow to reset an object during iteration
1 parent 5cc8db8 commit 75c65bf

9 files changed

+495
-53
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
--TEST--
2+
Lazy Objects: resetAsLazy() during foreach() is not allowed
3+
--FILE--
4+
<?php
5+
6+
class NoHooks {
7+
public int $a = 1;
8+
}
9+
10+
class Hooks {
11+
public int $a = 1;
12+
public int $b { get { return 2; } }
13+
}
14+
15+
function test(string $name, object $obj) {
16+
printf("# %s\n", $name);
17+
$reflector = new ReflectionClass($obj::class);
18+
foreach ($obj as $key => $value) {
19+
var_dump($key);
20+
try {
21+
$reflector->resetAsLazyProxy($obj, function () {});
22+
} catch (Error $e) {
23+
printf("%s: %s\n", $e::class, $e->getMessage());
24+
}
25+
}
26+
}
27+
28+
$nohooks = new ReflectionClass(NoHooks::class);
29+
$hooks = new ReflectionClass(Hooks::class);
30+
31+
$obj = $nohooks->newLazyGhost(function () {});
32+
33+
test('Ghost', $obj);
34+
35+
$obj = $hooks->newLazyGhost(function () {});
36+
37+
test('Ghost (hooks)', $obj);
38+
39+
$obj = $nohooks->newLazyProxy(function () {
40+
return new NoHooks();
41+
});
42+
43+
test('Proxy', $obj);
44+
45+
$obj = $hooks->newLazyProxy(function () {
46+
return new Hooks();
47+
});
48+
49+
test('Proxy', $obj);
50+
51+
$obj = new NoHooks();
52+
53+
test('Non lazy', $obj);
54+
55+
$obj = new Hooks();
56+
57+
test('Non lazy (hooks)', $obj);
58+
59+
$obj = new NoHooks();
60+
ob_start();
61+
var_dump($obj);
62+
ob_end_clean();
63+
64+
test('Non lazy with ht', $obj);
65+
66+
$obj = new Hooks();
67+
ob_start();
68+
var_dump($obj);
69+
ob_end_clean();
70+
71+
test('Non lazy with ht (hooks)', $obj);
72+
73+
?>
74+
==DONE==
75+
--EXPECT--
76+
# Ghost
77+
string(1) "a"
78+
Error: Can not reset an object during property iteration
79+
# Ghost (hooks)
80+
string(1) "a"
81+
Error: Can not reset an object during property iteration
82+
string(1) "b"
83+
Error: Can not reset an object during property iteration
84+
# Proxy
85+
string(1) "a"
86+
Error: Can not reset an object during property iteration
87+
# Proxy
88+
string(1) "a"
89+
Error: Can not reset an object during property iteration
90+
string(1) "b"
91+
Error: Can not reset an object during property iteration
92+
# Non lazy
93+
string(1) "a"
94+
Error: Can not reset an object during property iteration
95+
# Non lazy (hooks)
96+
string(1) "a"
97+
Error: Can not reset an object during property iteration
98+
string(1) "b"
99+
Error: Can not reset an object during property iteration
100+
# Non lazy with ht
101+
string(1) "a"
102+
Error: Can not reset an object during property iteration
103+
# Non lazy with ht (hooks)
104+
string(1) "a"
105+
Error: Can not reset an object during property iteration
106+
string(1) "b"
107+
Error: Can not reset an object during property iteration
108+
==DONE==

Zend/tests/lazy_objects/init_trigger_foreach.phpt

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ Lazy objects: Foreach initializes object
55

66
class C {
77
public int $a;
8+
public int $b;
89
public function __construct() {
910
var_dump(__METHOD__);
1011
$this->a = 1;
12+
$this->b = 2;
1113
}
1214
}
1315

@@ -24,6 +26,17 @@ foreach ($obj as $prop => $value) {
2426
var_dump($prop, $value);
2527
}
2628

29+
print "# Ghost (by ref):\n";
30+
31+
$obj = $reflector->newLazyGhost(function ($obj) {
32+
var_dump("initializer");
33+
$obj->__construct();
34+
});
35+
36+
foreach ($obj as $prop => &$value) {
37+
var_dump($prop, $value);
38+
}
39+
2740
print "# Proxy:\n";
2841

2942
$obj = $reflector->newLazyProxy(function ($obj) {
@@ -35,14 +48,110 @@ foreach ($obj as $prop => $value) {
3548
var_dump($prop, $value);
3649
}
3750

38-
--EXPECTF--
51+
print "# Proxy (by ref):\n";
52+
53+
$obj = $reflector->newLazyProxy(function ($obj) {
54+
var_dump("initializer");
55+
return new C();
56+
});
57+
58+
foreach ($obj as $prop => &$value) {
59+
var_dump($prop, $value);
60+
}
61+
62+
print "# Ghost (init failure)\n";
63+
64+
$fail = true;
65+
$obj = $reflector->newLazyGhost(function ($obj) use (&$fail) {
66+
if ($fail) {
67+
throw new Exception("initializer");
68+
} else {
69+
var_dump("initializer");
70+
$obj->__construct();
71+
}
72+
});
73+
74+
try {
75+
foreach ($obj as $prop => $value) {
76+
var_dump($prop, $value);
77+
}
78+
} catch (Exception $e) {
79+
printf("%s: %s\n", $e::class, $e->getMessage());
80+
}
81+
82+
$fail = false;
83+
foreach ($obj as $prop => $value) {
84+
var_dump($prop, $value);
85+
}
86+
87+
print "# Ghost (init failure, by ref)\n";
88+
89+
$fail = true;
90+
$obj = $reflector->newLazyGhost(function ($obj) use (&$fail) {
91+
if ($fail) {
92+
throw new Exception("initializer");
93+
} else {
94+
var_dump("initializer");
95+
$obj->__construct();
96+
}
97+
});
98+
99+
try {
100+
foreach ($obj as $prop => &$value) {
101+
var_dump($prop, $value);
102+
}
103+
} catch (Exception $e) {
104+
printf("%s: %s\n", $e::class, $e->getMessage());
105+
}
106+
107+
$fail = false;
108+
foreach ($obj as $prop => &$value) {
109+
var_dump($prop, $value);
110+
}
111+
112+
?>
113+
--EXPECT--
39114
# Ghost:
40115
string(11) "initializer"
41116
string(14) "C::__construct"
42117
string(1) "a"
43118
int(1)
119+
string(1) "b"
120+
int(2)
121+
# Ghost (by ref):
122+
string(11) "initializer"
123+
string(14) "C::__construct"
124+
string(1) "a"
125+
int(1)
126+
string(1) "b"
127+
int(2)
44128
# Proxy:
45129
string(11) "initializer"
46130
string(14) "C::__construct"
47131
string(1) "a"
48132
int(1)
133+
string(1) "b"
134+
int(2)
135+
# Proxy (by ref):
136+
string(11) "initializer"
137+
string(14) "C::__construct"
138+
string(1) "a"
139+
int(1)
140+
string(1) "b"
141+
int(2)
142+
# Ghost (init failure)
143+
Exception: initializer
144+
string(11) "initializer"
145+
string(14) "C::__construct"
146+
string(1) "a"
147+
int(1)
148+
string(1) "b"
149+
int(2)
150+
# Ghost (init failure, by ref)
151+
Exception: initializer
152+
string(11) "initializer"
153+
string(14) "C::__construct"
154+
string(1) "a"
155+
int(1)
156+
string(1) "b"
157+
int(2)

0 commit comments

Comments
 (0)