1616use Symfony \Component \VarExporter \Internal \LazyObjectState ;
1717
1818/**
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
2020 */
2121trait LazyGhostTrait
2222{
2323 /**
2424 * Creates a lazy-loading ghost instance.
2525 *
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.
2828 *
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.
3133 *
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
3937 */
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
4139 {
4240 if (self ::class !== $ class = $ instance ? $ instance ::class : static ::class) {
4341 $ skippedProperties ["\0" .self ::class."\0lazyObjectId " ] = true ;
@@ -49,9 +47,10 @@ public static function createLazyGhost(\Closure $initializer, array $skippedProp
4947 Registry::$ defaultProperties [$ class ] ??= (array ) $ instance ;
5048 $ instance ->lazyObjectId = $ id = spl_object_id ($ instance );
5149 Registry::$ states [$ id ] = new LazyObjectState ($ initializer , $ skippedProperties );
50+ $ onlyProperties = \is_array ($ initializer ) ? $ initializer : null ;
5251
5352 foreach (Registry::$ classResetters [$ class ] ??= Registry::getClassResetters ($ class ) as $ reset ) {
54- $ reset ($ instance , $ skippedProperties );
53+ $ reset ($ instance , $ skippedProperties, $ onlyProperties );
5554 }
5655
5756 return $ instance ;
@@ -66,17 +65,15 @@ public function isLazyObjectInitialized(): bool
6665 return true ;
6766 }
6867
69- if (LazyObjectState:: STATUS_INITIALIZED_PARTIAL !== $ state ->status ) {
68+ if (! \is_array ( $ state ->initializer ) ) {
7069 return LazyObjectState::STATUS_INITIALIZED_FULL === $ state ->status ;
7170 }
7271
7372 $ class = $ this ::class;
7473 $ properties = (array ) $ this ;
7574 $ 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 ])) {
8077 return false ;
8178 }
8279 }
@@ -93,7 +90,7 @@ public function initializeLazyObject(): static
9390 return $ this ;
9491 }
9592
96- if (LazyObjectState:: STATUS_INITIALIZED_PARTIAL !== ($ state ->status ?: $ state -> initialize ( $ this , null , null ) )) {
93+ if (! \is_array ($ state ->initializer )) {
9794 if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $ state ->status ) {
9895 $ state ->initialize ($ this , '' , null );
9996 }
@@ -104,10 +101,8 @@ public function initializeLazyObject(): static
104101 $ class = $ this ::class;
105102 $ properties = (array ) $ this ;
106103 $ 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 ) {
111106 continue ;
112107 }
113108
@@ -127,14 +122,8 @@ public function resetLazyObject(): bool
127122 return false ;
128123 }
129124
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 );
138127 }
139128
140129 return true ;
@@ -149,8 +138,9 @@ public function &__get($name): mixed
149138 $ scope = Registry::getScope ($ propertyScopes , $ class , $ name );
150139 $ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ;
151140
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+ ) {
154144 goto get_in_scope;
155145 }
156146 }
@@ -192,10 +182,10 @@ public function __set($name, $value): void
192182
193183 if ([$ class , , $ readonlyScope ] = $ propertyScopes [$ name ] ?? null ) {
194184 $ scope = Registry::getScope ($ propertyScopes , $ class , $ name , $ readonlyScope );
195-
196185 $ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ;
186+
197187 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 ) {
199189 $ state ->initialize ($ this , $ name , $ readonlyScope ?? $ scope );
200190 }
201191 goto set_in_scope;
@@ -227,8 +217,9 @@ public function __isset($name): bool
227217 $ scope = Registry::getScope ($ propertyScopes , $ class , $ name );
228218 $ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ;
229219
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+ ) {
232223 goto isset_in_scope;
233224 }
234225 }
@@ -257,7 +248,7 @@ public function __unset($name): void
257248 $ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ;
258249
259250 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 ) {
261252 $ state ->initialize ($ this , $ name , $ readonlyScope ?? $ scope );
262253 }
263254 goto unset_in_scope;
@@ -328,7 +319,7 @@ public function __destruct()
328319 $ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ;
329320
330321 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 )) {
332323 return ;
333324 }
334325
@@ -344,7 +335,9 @@ public function __destruct()
344335
345336 private function setLazyObjectAsInitialized (bool $ initialized ): void
346337 {
347- if ($ state = Registry::$ states [$ this ->lazyObjectId ?? '' ] ?? null ) {
338+ $ state = Registry::$ states [$ this ->lazyObjectId ?? '' ];
339+
340+ if ($ state && !\is_array ($ state ->initializer )) {
348341 $ state ->status = $ initialized ? LazyObjectState::STATUS_INITIALIZED_FULL : LazyObjectState::STATUS_UNINITIALIZED_FULL ;
349342 }
350343 }
0 commit comments