Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0bbd405

Browse files
[VarExporter] Leverage native lazy objects
1 parent 6c0058a commit 0bbd405

15 files changed

+797
-229
lines changed

UPGRADE-7.3.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ Read more about this in the [Symfony documentation](https://symfony.com/doc/7.3/
88

99
If you're upgrading from a version below 7.2, follow the [7.2 upgrade guide](UPGRADE-7.2.md) first.
1010

11+
AssetMapper
12+
-----------
13+
14+
* `ImportMapRequireCommand` now takes `projectDir` as a required third constructor argument
15+
1116
Ldap
1217
----
1318

@@ -192,7 +197,8 @@ VarDumper
192197
* Deprecate `ResourceCaster::castCurl()`, `ResourceCaster::castGd()` and `ResourceCaster::castOpensslX509()`
193198
* Mark all casters as `@internal`
194199

195-
AssetMapper
200+
VarExporter
196201
-----------
197202

198-
* `ImportMapRequireCommand` now takes `projectDir` as a required third constructor argument
203+
* Deprecate `LazyGhostTrait` and `LazyProxyTrait`, use native lazy objects instead
204+
* Deprecate `ProxyHelper::generateLazyGhost()`, use native lazy objects instead

src/Symfony/Component/HttpKernel/DependencyInjection/ServicesResetter.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ public function reset(): void
4646
continue;
4747
}
4848

49+
if (\PHP_VERSION_ID >= 80400 && (new \ReflectionClass($service))->isUninitializedLazyObject($service)) {
50+
continue;
51+
}
52+
4953
foreach ((array) $this->resetMethods[$id] as $resetMethod) {
5054
if ('?' === $resetMethod[0] && !method_exists($service, $resetMethod = substr($resetMethod, 1))) {
5155
continue;

src/Symfony/Component/JsonStreamer/CacheWarmer/LazyGhostCacheWarmer.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
*
2323
* @author Mathias Arlaud <[email protected]>
2424
*
25+
* @deprecated since Symfony 7.3, native lazy objects will be used instead
26+
*
2527
* @internal
2628
*/
2729
final class LazyGhostCacheWarmer extends CacheWarmer

src/Symfony/Component/VarExporter/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
CHANGELOG
22
=========
33

4+
7.3
5+
---
6+
7+
* Leverage native lazy objects in `ProxyHelper::generateLazyProxy()` on PHP 8.4+
8+
* Deprecate `LazyGhostTrait` and `LazyProxyTrait`, use native lazy objects instead
9+
* Deprecate `ProxyHelper::generateLazyGhost()`, use native lazy objects instead
10+
411
7.2
512
---
613

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Internal;
13+
14+
use Symfony\Component\Serializer\Attribute\Ignore;
15+
use Symfony\Component\VarExporter\Internal\LazyObjectRegistry as Registry;
16+
17+
/**
18+
* @internal
19+
*/
20+
trait LazyDecoratorTrait
21+
{
22+
#[Ignore]
23+
private readonly LazyObjectState $lazyObjectState;
24+
25+
/**
26+
* Creates a lazy-loading decorator.
27+
*
28+
* @param \Closure():object $initializer Returns the proxied object
29+
* @param static|null $instance
30+
*/
31+
private static function doCreateLazyProxy(\Closure $initializer, ?object $instance = null): static
32+
{
33+
$class = $instance ? $instance::class : static::class;
34+
35+
if (self::class === $class && \defined($class.'::LAZY_OBJECT_PROPERTY_SCOPES')) {
36+
Hydrator::$propertyScopes[$class] ??= $class::LAZY_OBJECT_PROPERTY_SCOPES;
37+
}
38+
39+
$r = Registry::$classReflectors[$class] ??= ($r = new \ReflectionClass($class))->hasProperty('lazyObjectState') ? $r : throw new \LogicException('Cannot create a lazy proxy for a non-decorator object.');
40+
$initializer = static function ($ghost) use ($initializer, $class) {
41+
foreach (Registry::$classResetters[$class] ??= Registry::getClassResetters($class) as $reset) {
42+
$reset($ghost, []);
43+
}
44+
45+
$state = $ghost->lazyObjectState ??= new LazyObjectState();
46+
$state->realInstance = $initializer();
47+
$state->initializer = $initializer;
48+
};
49+
50+
if (!$instance) {
51+
return $r->newLazyGhost($initializer);
52+
}
53+
54+
$r->resetAsLazyGhost($instance, $initializer);
55+
56+
return $instance;
57+
}
58+
59+
public function __construct(...$args)
60+
{
61+
self::createLazyProxy(static fn () => new parent(...$args), $this);
62+
}
63+
64+
public function __destruct()
65+
{
66+
}
67+
68+
/**
69+
* Returns whether the object is initialized.
70+
*
71+
* @param bool $partial Whether partially initialized objects should be considered as initialized
72+
*/
73+
#[Ignore]
74+
public function isLazyObjectInitialized(bool $partial = false): bool
75+
{
76+
$r = Registry::$classReflectors[static::class] ??= new \ReflectionClass(static::class);
77+
78+
return !$r->isUninitializedLazyObject($this);
79+
}
80+
81+
/**
82+
* Forces initialization of a lazy object and returns it.
83+
*/
84+
public function initializeLazyObject(): parent
85+
{
86+
return $this->lazyObjectState->realInstance;
87+
}
88+
89+
/**
90+
* @return bool Returns false when the object cannot be reset, ie when it's not a lazy object
91+
*/
92+
public function resetLazyObject(): bool
93+
{
94+
$r = Registry::$classReflectors[static::class] ??= new \ReflectionClass(static::class);
95+
96+
if ($r->isUninitializedLazyObject($this)) {
97+
return true;
98+
}
99+
100+
return isset($this->lazyObjectState->initializer) && self::createLazyProxy($this->lazyObjectState->initializer, $this);
101+
}
102+
103+
public function &__get($name): mixed
104+
{
105+
$instance = $this->lazyObjectState->realInstance;
106+
$class = $this::class;
107+
108+
$propertyScopes = Hydrator::$propertyScopes[$class] ??= Hydrator::getPropertyScopes($class);
109+
$notByRef = 0;
110+
111+
if ([, , , $access] = $propertyScopes[$name] ?? null) {
112+
$notByRef = $access & Hydrator::PROPERTY_NOT_BY_REF || ($access >> 2) & \ReflectionProperty::IS_PRIVATE_SET;
113+
}
114+
115+
if ($notByRef || 2 !== ((Registry::$parentMethods[$class] ??= Registry::getParentMethods($class))['get'] ?: 2)) {
116+
$value = $instance->$name;
117+
118+
return $value;
119+
}
120+
121+
try {
122+
return $instance->$name;
123+
} catch (\Error $e) {
124+
if (\Error::class !== $e::class || !str_starts_with($e->getMessage(), 'Cannot access uninitialized non-nullable property')) {
125+
throw $e;
126+
}
127+
128+
try {
129+
$instance->$name = [];
130+
131+
return $instance->$name;
132+
} catch (\Error) {
133+
if (preg_match('/^Cannot access uninitialized non-nullable property ([^ ]++) by reference$/', $e->getMessage(), $matches)) {
134+
throw new \Error('Typed property '.$matches[1].' must not be accessed before initialization', $e->getCode(), $e->getPrevious());
135+
}
136+
137+
throw $e;
138+
}
139+
}
140+
}
141+
142+
public function __set($name, $value): void
143+
{
144+
$this->lazyObjectState->realInstance->$name = $value;
145+
}
146+
147+
public function __isset($name): bool
148+
{
149+
return isset($this->lazyObjectState->realInstance->$name);
150+
}
151+
152+
public function __unset($name): void
153+
{
154+
unset($this->lazyObjectState->realInstance->$name);
155+
}
156+
157+
public function __serialize(): array
158+
{
159+
return [$this->lazyObjectState->realInstance];
160+
}
161+
162+
public function __unserialize($data): void
163+
{
164+
$this->lazyObjectState = new LazyObjectState();
165+
$this->lazyObjectState->realInstance = $data[0];
166+
}
167+
168+
public function __clone(): void
169+
{
170+
$this->lazyObjectState = clone $this->lazyObjectState;
171+
}
172+
}

src/Symfony/Component/VarExporter/Internal/LazyObjectState.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@ class LazyObjectState
3333
public int $status = self::STATUS_UNINITIALIZED_FULL;
3434

3535
public object $realInstance;
36+
public object $cloneInstance;
3637

3738
/**
3839
* @param array<string, true> $skippedProperties
3940
*/
4041
public function __construct(
41-
public \Closure $initializer,
42+
public ?\Closure $initializer = null,
4243
public array $skippedProperties = [],
4344
) {
4445
}
@@ -94,4 +95,17 @@ public function reset($instance): void
9495

9596
$this->status = self::STATUS_UNINITIALIZED_FULL;
9697
}
98+
99+
public function __clone()
100+
{
101+
if (isset($this->cloneInstance)) {
102+
try {
103+
$this->realInstance = $this->cloneInstance;
104+
} finally {
105+
unset($this->cloneInstance);
106+
}
107+
} elseif (isset($this->realInstance)) {
108+
$this->realInstance = clone $this->realInstance;
109+
}
110+
}
97111
}

src/Symfony/Component/VarExporter/LazyGhostTrait.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
use Symfony\Component\VarExporter\Internal\LazyObjectState;
1818
use Symfony\Component\VarExporter\Internal\LazyObjectTrait;
1919

20+
if (\PHP_VERSION_ID >= 80400) {
21+
trigger_deprecation('symfony/var-exporter', '7.3', 'The "%s" trait is deprecated, use native lazy objects instead.', LazyGhostTrait::class);
22+
}
23+
24+
/**
25+
* @deprecated since Symfony 7.3, use native lazy objects instead
26+
*/
2027
trait LazyGhostTrait
2128
{
2229
use LazyObjectTrait;

src/Symfony/Component/VarExporter/LazyProxyTrait.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
use Symfony\Component\VarExporter\Internal\LazyObjectState;
1919
use Symfony\Component\VarExporter\Internal\LazyObjectTrait;
2020

21+
if (\PHP_VERSION_ID >= 80400) {
22+
trigger_deprecation('symfony/var-exporter', '7.3', 'The "%s" trait is deprecated, use native lazy objects instead.', LazyProxyTrait::class);
23+
}
24+
25+
/**
26+
* @deprecated since Symfony 7.3, use native lazy objects instead
27+
*/
2128
trait LazyProxyTrait
2229
{
2330
use LazyObjectTrait;
@@ -38,11 +45,12 @@ public static function createLazyProxy(\Closure $initializer, ?object $instance
3845
Registry::$classReflectors[$class] ??= new \ReflectionClass($class);
3946
$instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor();
4047
Registry::$defaultProperties[$class] ??= (array) $instance;
41-
Registry::$classResetters[$class] ??= Registry::getClassResetters($class);
4248

4349
if (self::class === $class && \defined($class.'::LAZY_OBJECT_PROPERTY_SCOPES')) {
4450
Hydrator::$propertyScopes[$class] ??= $class::LAZY_OBJECT_PROPERTY_SCOPES;
4551
}
52+
53+
Registry::$classResetters[$class] ??= Registry::getClassResetters($class);
4654
} else {
4755
$instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor();
4856
}
@@ -290,10 +298,6 @@ public function __clone(): void
290298
}
291299

292300
$this->lazyObjectState = clone $this->lazyObjectState;
293-
294-
if (isset($this->lazyObjectState->realInstance)) {
295-
$this->lazyObjectState->realInstance = clone $this->lazyObjectState->realInstance;
296-
}
297301
}
298302

299303
public function __serialize(): array

0 commit comments

Comments
 (0)