16
16
use Symfony \Component\VarExporter \Internal \LazyObjectState ;
17
17
18
18
/**
19
- * @property int $lazyObjectId This property must be declared in classes using this trait
19
+ * @property int $lazyObjectId This property must be declared as private in classes using this trait
20
20
*/
21
21
trait LazyGhostTrait
22
22
{
23
23
/**
24
24
* Creates a lazy-loading ghost instance.
25
25
*
26
- * The initializer can take two forms. In both forms,
27
- * the instance to initialize is passed as first argument.
26
+ * When the initializer is a closure, it should initialize all properties at
27
+ * once and is given the instance to initialize as argument.
28
28
*
29
- * When the initializer takes only one argument, it is expected to initialize all
30
- * properties at once.
29
+ * When the initializer is an array of closures, it should be indexed by
30
+ * properties and closures should accept 4 arguments: the instance to
31
+ * initialize, the property to initialize, its write-scope, and its default
32
+ * value. Each closure should return the value of the corresponding property.
31
33
*
32
- * When 4 arguments are required, the initializer is expected to return the value
33
- * of each property one by one. The extra arguments are the name of the property
34
- * to initialize, the write-scope of that property, and its default value.
35
- *
36
- * @param \Closure(static):void|\Closure(static, string, ?string, mixed):mixed $initializer
37
- * @param array<string, true> $skippedProperties An array indexed by the properties to skip,
38
- * aka the ones that the initializer doesn't set
34
+ * @param \Closure(static):void|array<string, \Closure(static, string, ?string, mixed):mixed> $initializer
35
+ * @param array<string, true> $skippedProperties An array indexed by the properties to skip, aka the ones
36
+ * that the initializer doesn't set when its a closure
39
37
*/
40
- public static function createLazyGhost (\Closure $ initializer , array $ skippedProperties = [], self $ instance = null ): static
38
+ public static function createLazyGhost (\Closure | array $ initializer , array $ skippedProperties = [], self $ instance = null ): static
41
39
{
42
40
if (self ::class !== $ class = $ instance ? $ instance ::class : static ::class) {
43
41
$ skippedProperties ["\0" .self ::class."\0lazyObjectId " ] = true ;
@@ -49,9 +47,10 @@ public static function createLazyGhost(\Closure $initializer, array $skippedProp
49
47
Registry::$ defaultProperties [$ class ] ??= (array ) $ instance ;
50
48
$ instance ->lazyObjectId = $ id = spl_object_id ($ instance );
51
49
Registry::$ states [$ id ] = new LazyObjectState ($ initializer , $ skippedProperties );
50
+ $ onlyProperties = \is_array ($ initializer ) ? $ initializer : null ;
52
51
53
52
foreach (Registry::$ classResetters [$ class ] ??= Registry::getClassResetters ($ class ) as $ reset ) {
54
- $ reset ($ instance , $ skippedProperties );
53
+ $ reset ($ instance , $ skippedProperties, $ onlyProperties );
55
54
}
56
55
57
56
return $ instance ;
@@ -66,17 +65,15 @@ public function isLazyObjectInitialized(): bool
66
65
return true ;
67
66
}
68
67
69
- if (LazyObjectState:: STATUS_INITIALIZED_PARTIAL !== $ state ->status ) {
68
+ if (! \is_array ( $ state ->initializer ) ) {
70
69
return LazyObjectState::STATUS_INITIALIZED_FULL === $ state ->status ;
71
70
}
72
71
73
72
$ class = $ this ::class;
74
73
$ properties = (array ) $ this ;
75
74
$ propertyScopes = Hydrator::$ propertyScopes [$ class ] ??= Hydrator::getPropertyScopes ($ class );
76
- foreach ($ propertyScopes as $ key => [$ scope , $ name ]) {
77
- $ propertyScopes [$ k = "\0$ scope \0$ name " ] ?? $ propertyScopes [$ k = "\0* \0$ name " ] ?? $ k = $ name ;
78
-
79
- if ($ k === $ key && !\array_key_exists ($ k , $ properties )) {
75
+ foreach ($ state ->initializer as $ key => $ initializer ) {
76
+ if (!\array_key_exists ($ key , $ properties ) && isset ($ propertyScopes [$ key ])) {
80
77
return false ;
81
78
}
82
79
}
@@ -93,7 +90,7 @@ public function initializeLazyObject(): static
93
90
return $ this ;
94
91
}
95
92
96
- if (LazyObjectState:: STATUS_INITIALIZED_PARTIAL !== ($ state ->status ?: $ state -> initialize ( $ this , null , null ) )) {
93
+ if (! \is_array ($ state ->initializer )) {
97
94
if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $ state ->status ) {
98
95
$ state ->initialize ($ this , '' , null );
99
96
}
@@ -104,10 +101,8 @@ public function initializeLazyObject(): static
104
101
$ class = $ this ::class;
105
102
$ properties = (array ) $ this ;
106
103
$ propertyScopes = Hydrator::$ propertyScopes [$ class ] ??= Hydrator::getPropertyScopes ($ class );
107
- foreach ($ propertyScopes as $ key => [$ scope , $ name , $ readonlyScope ]) {
108
- $ propertyScopes [$ k = "\0$ scope \0$ name " ] ?? $ propertyScopes [$ k = "\0" .($ scope = '* ' )."\0$ name " ] ?? $ k = $ name ;
109
-
110
- if ($ k !== $ key || \array_key_exists ($ k , $ properties )) {
104
+ foreach ($ state ->initializer as $ key => $ initializer ) {
105
+ if (\array_key_exists ($ key , $ properties ) || ![$ scope , $ name , $ readonlyScope ] = $ propertyScopes [$ key ] ?? null ) {
111
106
continue ;
112
107
}
113
108
@@ -127,14 +122,8 @@ public function resetLazyObject(): bool
127
122
return false ;
128
123
}
129
124
130
- if (!$ state ->status ) {
131
- return $ state ->initialize ($ this , null , null ) || true ;
132
- }
133
-
134
- $ state ->reset ($ this );
135
-
136
- if (LazyObjectState::STATUS_INITIALIZED_FULL === $ state ->status ) {
137
- $ state ->status = LazyObjectState::STATUS_UNINITIALIZED_FULL ;
125
+ if (LazyObjectState::STATUS_UNINITIALIZED_FULL !== $ state ->status ) {
126
+ $ state ->reset ($ this );
138
127
}
139
128
140
129
return true ;
10000
@@ -149,8 +138,9 @@ public function &__get($name): mixed
149
138
$ scope = Registry::getScope ($ propertyScopes , $ class , $ name );
150
139
$ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ;
151
140
152
- if ($ state && (null === $ scope || isset ($ propertyScopes ["\0$ scope \0$ name " ]))) {
153
- $ state ->initialize ($ this , $ name , $ readonlyScope ?? $ scope );
141
+ if ($ state && (null === $ scope || isset ($ propertyScopes ["\0$ scope \0$ name " ]))
142
+ && LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $ state ->initialize ($ this , $ name , $ readonlyScope ?? $ scope )
143
+ ) {
154
144
goto get_in_scope;
155
145
}
156
146
}
@@ -192,10 +182,10 @@ public function __set($name, $value): void
192
182
193
183
if ([$ class , , $ readonlyScope ] = $ propertyScopes [$ name ] ?? null ) {
194
184
$ scope = Registry::getScope ($ propertyScopes , $ class , $ name , $ readonlyScope );
195
-
196
185
$ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ;
186
+
197
187
if ($ state && ($ readonlyScope === $ scope || isset ($ propertyScopes ["\0$ scope \0$ name " ]))) {
198
- if (LazyObjectState::STATUS_UNINITIALIZED_FULL === ( $ state ->status ?: $ state -> initialize ( $ this , null , null )) ) {
188
+ if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $ state ->status ) {
199
189
$ state ->initialize ($ this , $ name , $ readonlyScope ?? $ scope );
200
190
}
201
191
goto set_in_scope;
@@ -227,8 +217,9 @@ public function __isset($name): bool
227
217
$ scope = Registry::getScope ($ propertyScopes , $ class , $ name );
228
218
$ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ;
229
219
230
- if ($ state && (null === $ scope || isset ($ propertyScopes ["\0$ scope \0$ name " ]))) {
231
- $ state ->initialize ($ this , $ name , $ readonlyScope ?? $ scope );
220
+ if ($ state && (null === $ scope || isset ($ propertyScopes ["\0$ scope \0$ name " ]))
221
+ && LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $ state ->initialize ($ this , $ name , $ readonlyScope ?? $ scope )
222
+ ) {
232
223
goto isset_in_scope;
233
224
}
234
225
}
@@ -257,7 +248,7 @@ public function __unset($name): void
257
248
$ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ;
258
249
259
250
if ($ state && ($ readonlyScope === $ scope || isset ($ propertyScopes ["\0$ scope \0$ name " ]))) {
260
- if (LazyObjectState::STATUS_UNINITIALIZED_FULL === ( $ state ->status ?: $ state -> initialize ( $ this , null , null )) ) {
251
+ if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $ state ->status ) {
261
252
$ state ->initialize ($ this , $ name , $ readonlyScope ?? $ scope );
262
253
}
263
254
goto unset_in_scope;
@@ -328,7 +319,7 @@ public function __destruct()
328
319
$ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ;
329
320
330
321
try {
331
- if ($ state && ! \in_array ($ state ->status , [LazyObjectState::STATUS_INITIALIZED_FULL , LazyObjectState::STATUS_INITIALIZED_PARTIAL ], true )) {
322
+ if ($ state && \in_array ($ state ->status , [LazyObjectState::STATUS_UNINITIALIZED_FULL , LazyObjectState::STATUS_UNINITIALIZED_PARTIAL ], true )) {
332
323
return ;
333
324
}
334
325
@@ -344,7 +335,9 @@ public function __destruct()
344
335
345
336
private function setLazyObjectAsInitialized (bool $ initialized ): void
346
337
{
347
- if ($ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ) {
338
+ $ state = Registry::$ states [$ this ->lazyObjectId ?? '' ];
339
+
340
+ if ($ state && !\is_array ($ state ->initializer )) {
348
341
$ state ->status = $ initialized ? LazyObjectState::STATUS_INITIALIZED_FULL : LazyObjectState::STATUS_UNINITIALIZED_FULL ;
349
342
}
350
343
}
0 commit comments