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 declaredas private in classes using this trait
2020 */
2121trait LazyGhostTrait
2222{
2323/**
2424 * Creates a lazy-loading ghost instance.
2525 *
26- *The initializercan take two forms. In both forms,
27- * the instance to initializeis passed as first argument.
26+ *When the initializeris a closure, it should initialize all properties at
27+ *once and is given the instance to initializeas 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 {
4240if (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
5352foreach (Registry::$ classResetters [$ class ] ??= Registry::getClassResetters ($ class )as $ reset ) {
54- $ reset ($ instance ,$ skippedProperties );
53+ $ reset ($ instance ,$ skippedProperties, $ onlyProperties );
5554 }
5655
5756return $ instance ;
@@ -66,17 +65,15 @@ public function isLazyObjectInitialized(): bool
6665return true ;
6766 }
6867
69- if (LazyObjectState:: STATUS_INITIALIZED_PARTIAL !== $ state ->status ) {
68+ if (! \is_array ( $ state ->initializer ) ) {
7069return 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 ($ propertyScopesas $ 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 ])) {
8077return false ;
8178 }
8279 }
@@ -93,7 +90,7 @@ public function initializeLazyObject(): static
9390return $ this ;
9491 }
9592
96- if (LazyObjectState:: STATUS_INITIALIZED_PARTIAL !== ($ state ->status ?: $ state -> initialize ( $ this , null , null ) )) {
93+ if (! \is_array ($ state ->initializer )) {
9794if (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 ($ propertyScopesas $ 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 ) {
111106continue ;
112107 }
113108
@@ -127,14 +122,8 @@ public function resetLazyObject(): bool
127122return 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
140129return 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+ ) {
154144goto get_in_scope;
155145 }
156146 }
@@ -192,10 +182,10 @@ public function __set($name, $value): void
192182
193183if ([$ class , ,$ readonlyScope ] =$ propertyScopes [$ name ] ??null ) {
194184$ scope = Registry::getScope ($ propertyScopes ,$ class ,$ name ,$ readonlyScope );
195-
196185$ state = Registry::$ states [$ this ->lazyObjectId ??'' ] ??null ;
186+
197187if ($ 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 }
201191goto 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+ ) {
232223goto isset_in_scope;
233224 }
234225 }
@@ -257,7 +248,7 @@ public function __unset($name): void
257248$ state = Registry::$ states [$ this ->lazyObjectId ??'' ] ??null ;
258249
259250if ($ 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 }
263254goto unset_in_scope;
@@ -328,7 +319,7 @@ public function __destruct()
328319$ state = Registry::$ states [$ this ->lazyObjectId ??'' ] ??null ;
329320
330321try {
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 )) {
332323return ;
333324 }
334325
@@ -344,7 +335,9 @@ public function __destruct()
344335
345336private 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 }