diff options
Diffstat (limited to 'vendor/symfony/var-exporter/LazyGhostTrait.php')
-rw-r--r-- | vendor/symfony/var-exporter/LazyGhostTrait.php | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/vendor/symfony/var-exporter/LazyGhostTrait.php b/vendor/symfony/var-exporter/LazyGhostTrait.php new file mode 100644 index 0000000..fa82ced --- /dev/null +++ b/vendor/symfony/var-exporter/LazyGhostTrait.php | |||
@@ -0,0 +1,352 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of the Symfony package. | ||
5 | * | ||
6 | * (c) Fabien Potencier <fabien@symfony.com> | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | |||
12 | namespace Symfony\Component\VarExporter; | ||
13 | |||
14 | use Symfony\Component\Serializer\Attribute\Ignore; | ||
15 | use Symfony\Component\VarExporter\Internal\Hydrator; | ||
16 | use Symfony\Component\VarExporter\Internal\LazyObjectRegistry as Registry; | ||
17 | use Symfony\Component\VarExporter\Internal\LazyObjectState; | ||
18 | use Symfony\Component\VarExporter\Internal\LazyObjectTrait; | ||
19 | |||
20 | trait LazyGhostTrait | ||
21 | { | ||
22 | use LazyObjectTrait; | ||
23 | |||
24 | /** | ||
25 | * Creates a lazy-loading ghost instance. | ||
26 | * | ||
27 | * Skipped properties should be indexed by their array-cast identifier, see | ||
28 | * https://php.net/manual/language.types.array#language.types.array.casting | ||
29 | * | ||
30 | * @param (\Closure(static):void $initializer The closure should initialize the object it receives as argument | ||
31 | * @param array<string, true>|null $skippedProperties An array indexed by the properties to skip, a.k.a. the ones | ||
32 | * that the initializer doesn't initialize, if any | ||
33 | * @param static|null $instance | ||
34 | */ | ||
35 | public static function createLazyGhost(\Closure $initializer, ?array $skippedProperties = null, ?object $instance = null): static | ||
36 | { | ||
37 | if (self::class !== $class = $instance ? $instance::class : static::class) { | ||
38 | $skippedProperties["\0".self::class."\0lazyObjectState"] = true; | ||
39 | } | ||
40 | |||
41 | if (!isset(Registry::$defaultProperties[$class])) { | ||
42 | Registry::$classReflectors[$class] ??= new \ReflectionClass($class); | ||
43 | $instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor(); | ||
44 | Registry::$defaultProperties[$class] ??= (array) $instance; | ||
45 | Registry::$classResetters[$class] ??= Registry::getClassResetters($class); | ||
46 | |||
47 | if (self::class === $class && \defined($class.'::LAZY_OBJECT_PROPERTY_SCOPES')) { | ||
48 | Hydrator::$propertyScopes[$class] ??= $class::LAZY_OBJECT_PROPERTY_SCOPES; | ||
49 | } | ||
50 | } else { | ||
51 | $instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor(); | ||
52 | } | ||
53 | |||
54 | $instance->lazyObjectState = new LazyObjectState($initializer, $skippedProperties ??= []); | ||
55 | |||
56 | foreach (Registry::$classResetters[$class] as $reset) { | ||
57 | $reset($instance, $skippedProperties); | ||
58 | } | ||
59 | |||
60 | return $instance; | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * Returns whether the object is initialized. | ||
65 | * | ||
66 | * @param $partial Whether partially initialized objects should be considered as initialized | ||
67 | */ | ||
68 | #[Ignore] | ||
69 | public function isLazyObjectInitialized(bool $partial = false): bool | ||
70 | { | ||
71 | if (!$state = $this->lazyObjectState ?? null) { | ||
72 | return true; | ||
73 | } | ||
74 | |||
75 | return LazyObjectState::STATUS_INITIALIZED_FULL === $state->status; | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * Forces initialization of a lazy object and returns it. | ||
80 | */ | ||
81 | public function initializeLazyObject(): static | ||
82 | { | ||
83 | if (!$state = $this->lazyObjectState ?? null) { | ||
84 | return $this; | ||
85 | } | ||
86 | |||
87 | if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) { | ||
88 | $state->initialize($this, '', null); | ||
89 | } | ||
90 | |||
91 | return $this; | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * @return bool Returns false when the object cannot be reset, ie when it's not a lazy object | ||
96 | */ | ||
97 | public function resetLazyObject(): bool | ||
98 | { | ||
99 | if (!$state = $this->lazyObjectState ?? null) { | ||
100 | return false; | ||
101 | } | ||
102 | |||
103 | if (LazyObjectState::STATUS_UNINITIALIZED_FULL !== $state->status) { | ||
104 | $state->reset($this); | ||
105 | } | ||
106 | |||
107 | return true; | ||
108 | } | ||
109 | |||
110 | public function &__get($name): mixed | ||
111 | { | ||
112 | $propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class); | ||
113 | $scope = null; | ||
114 | |||
115 | if ([$class, , $readonlyScope] = $propertyScopes[$name] ?? null) { | ||
116 | $scope = Registry::getScope($propertyScopes, $class, $name); | ||
117 | $state = $this->lazyObjectState ?? null; | ||
118 | |||
119 | if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))) { | ||
120 | if (LazyObjectState::STATUS_INITIALIZED_FULL === $state->status) { | ||
121 | // Work around php/php-src#12695 | ||
122 | $property = null === $scope ? $name : "\0$scope\0$name"; | ||
123 | $property = $propertyScopes[$property][3] | ||
124 | ?? Hydrator::$propertyScopes[$this::class][$property][3] = new \ReflectionProperty($scope ?? $class, $name); | ||
125 | } else { | ||
126 | $property = null; | ||
127 | } | ||
128 | |||
129 | if ($property?->isInitialized($this) ?? LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $readonlyScope ?? $scope)) { | ||
130 | goto get_in_scope; | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
135 | if ($parent = (Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['get']) { | ||
136 | if (2 === $parent) { | ||
137 | return parent::__get($name); | ||
138 | } | ||
139 | $value = parent::__get($name); | ||
140 | |||
141 | return $value; | ||
142 | } | ||
143 | |||
144 | if (null === $class) { | ||
145 | $frame = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]; | ||
146 | trigger_error(sprintf('Undefined property: %s::$%s in %s on line %s', $this::class, $name, $frame['file'], $frame['line']), \E_USER_NOTICE); | ||
147 | } | ||
148 | |||
149 | get_in_scope: | ||
150 | |||
151 | try { | ||
152 | if (null === $scope) { | ||
153 | if (null === $readonlyScope) { | ||
154 | return $this->$name; | ||
155 | } | ||
156 | $value = $this->$name; | ||
157 | |||
158 | return $value; | ||
159 | } | ||
160 | $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope); | ||
161 | |||
162 | return $accessor['get']($this, $name, null !== $readonlyScope); | ||
163 | } catch (\Error $e) { | ||
164 | if (\Error::class !== $e::class || !str_starts_with($e->getMessage(), 'Cannot access uninitialized non-nullable property')) { | ||
165 | throw $e; | ||
166 | } | ||
167 | |||
168 | try { | ||
169 | if (null === $scope) { | ||
170 | $this->$name = []; | ||
171 | |||
172 | return $this->$name; | ||
173 | } | ||
174 | |||
175 | $accessor['set']($this, $name, []); | ||
176 | |||
177 | return $accessor['get']($this, $name, null !== $readonlyScope); | ||
178 | } catch (\Error) { | ||
179 | if (preg_match('/^Cannot access uninitialized non-nullable property ([^ ]++) by reference$/', $e->getMessage(), $matches)) { | ||
180 | throw new \Error('Typed property '.$matches[1].' must not be accessed before initialization', $e->getCode(), $e->getPrevious()); | ||
181 | } | ||
182 | |||
183 | throw $e; | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | public function __set($name, $value): void | ||
189 | { | ||
190 | $propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class); | ||
191 | $scope = null; | ||
192 | |||
193 | if ([$class, , $readonlyScope] = $propertyScopes[$name] ?? null) { | ||
194 | $scope = Registry::getScope($propertyScopes, $class, $name, $readonlyScope); | ||
195 | $state = $this->lazyObjectState ?? null; | ||
196 | |||
197 | if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"])) | ||
198 | && LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status | ||
199 | ) { | ||
200 | if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) { | ||
201 | $state->initialize($this, $name, $readonlyScope ?? $scope); | ||
202 | } | ||
203 | goto set_in_scope; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['set']) { | ||
208 | parent::__set($name, $value); | ||
209 | |||
210 | return; | ||
211 | } | ||
212 | |||
213 | set_in_scope: | ||
214 | |||
215 | if (null === $scope) { | ||
216 | $this->$name = $value; | ||
217 | } else { | ||
218 | $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope); | ||
219 | $accessor['set']($this, $name, $value); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | public function __isset($name): bool | ||
224 | { | ||
225 | $propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class); | ||
226 | $scope = null; | ||
227 | |||
228 | if ([$class, , $readonlyScope] = $propertyScopes[$name] ?? null) { | ||
229 | $scope = Registry::getScope($propertyScopes, $class, $name); | ||
230 | $state = $this->lazyObjectState ?? null; | ||
231 | |||
232 | if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"])) | ||
233 | && LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status | ||
234 | && LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $readonlyScope ?? $scope) | ||
235 | ) { | ||
236 | goto isset_in_scope; | ||
237 | } | ||
238 | } | ||
239 | |||
240 | if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['isset']) { | ||
241 | return parent::__isset($name); | ||
242 | } | ||
243 | |||
244 | isset_in_scope: | ||
245 | |||
246 | if (null === $scope) { | ||
247 | return isset($this->$name); | ||
248 | } | ||
249 | $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope); | ||
250 | |||
251 | return $accessor['isset']($this, $name); | ||
252 | } | ||
253 | |||
254 | public function __unset($name): void | ||
255 | { | ||
256 | $propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class); | ||
257 | $scope = null; | ||
258 | |||
259 | if ([$class, , $readonlyScope] = $propertyScopes[$name] ?? null) { | ||
260 | $scope = Registry::getScope($propertyScopes, $class, $name, $readonlyScope); | ||
261 | $state = $this->lazyObjectState ?? null; | ||
262 | |||
263 | if ($state && ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"])) | ||
264 | && LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status | ||
265 | ) { | ||
266 | if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) { | ||
267 | $state->initialize($this, $name, $readonlyScope ?? $scope); | ||
268 | } | ||
269 | goto unset_in_scope; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['unset']) { | ||
274 | parent::__unset($name); | ||
275 | |||
276 | return; | ||
277 | } | ||
278 | |||
279 | unset_in_scope: | ||
280 | |||
281 | if (null === $scope) { | ||
282 | unset($this->$name); | ||
283 | } else { | ||
284 | $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope); | ||
285 | $accessor['unset']($this, $name); | ||
286 | } | ||
287 | } | ||
288 | |||
289 | public function __clone(): void | ||
290 | { | ||
291 | if ($state = $this->lazyObjectState ?? null) { | ||
292 | $this->lazyObjectState = clone $state; | ||
293 | } | ||
294 | |||
295 | if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['clone']) { | ||
296 | parent::__clone(); | ||
297 | } | ||
298 | } | ||
299 | |||
300 | public function __serialize(): array | ||
301 | { | ||
302 | $class = self::class; | ||
303 | |||
304 | if ((Registry::$parentMethods[$class] ??= Registry::getParentMethods($class))['serialize']) { | ||
305 | $properties = parent::__serialize(); | ||
306 | } else { | ||
307 | $this->initializeLazyObject(); | ||
308 | $properties = (array) $this; | ||
309 | } | ||
310 | unset($properties["\0$class\0lazyObjectState"]); | ||
311 | |||
312 | if (Registry::$parentMethods[$class]['serialize'] || !Registry::$parentMethods[$class]['sleep']) { | ||
313 | return $properties; | ||
314 | } | ||
315 | |||
316 | $scope = get_parent_class($class); | ||
317 | $data = []; | ||
318 | |||
319 | foreach (parent::__sleep() as $name) { | ||
320 | $value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0$class\0$name"] ?? $properties[$k = "\0$scope\0$name"] ?? $k = null; | ||
321 | |||
322 | if (null === $k) { | ||
323 | trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $name), \E_USER_NOTICE); | ||
324 | } else { | ||
325 | $data[$k] = $value; | ||
326 | } | ||
327 | } | ||
328 | |||
329 | return $data; | ||
330 | } | ||
331 | |||
332 | public function __destruct() | ||
333 | { | ||
334 | $state = $this->lazyObjectState ?? null; | ||
335 | |||
336 | if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state?->status) { | ||
337 | return; | ||
338 | } | ||
339 | |||
340 | if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['destruct']) { | ||
341 | parent::__destruct(); | ||
342 | } | ||
343 | } | ||
344 | |||
345 | #[Ignore] | ||
346 | private function setLazyObjectAsInitialized(bool $initialized): void | ||
347 | { | ||
348 | if ($state = $this->lazyObjectState ?? null) { | ||
349 | $state->status = $initialized ? LazyObjectState::STATUS_INITIALIZED_FULL : LazyObjectState::STATUS_UNINITIALIZED_FULL; | ||
350 | } | ||
351 | } | ||
352 | } | ||