Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7badfb5

Browse filesBrowse files
[VarExporter] Leverage native lazy objects
1 parent 6c0058a commit 7badfb5
Copy full SHA for 7badfb5

18 files changed

+805
-238
lines changed

‎.github/expected-missing-return-types.diff

Copy file name to clipboardExpand all lines: .github/expected-missing-return-types.diff
+5-5Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -593,17 +593,17 @@ diff --git a/src/Symfony/Component/VarDumper/Dumper/DataDumperInterface.php b/sr
593593
- public function dump(Data $data);
594594
+ public function dump(Data $data): ?string;
595595
}
596-
diff --git a/src/Symfony/Component/VarExporter/Tests/ProxyHelperTest.php b/src/Symfony/Component/VarExporter/Tests/ProxyHelperTest.php
597-
--- a/src/Symfony/Component/VarExporter/Tests/ProxyHelperTest.php
598-
+++ b/src/Symfony/Component/VarExporter/Tests/ProxyHelperTest.php
599-
@@ -172,5 +172,5 @@ class ProxyHelperTest extends TestCase
596+
diff --git a/src/Symfony/Component/VarExporter/Tests/LegacyProxyHelperTest.php b/src/Symfony/Component/VarExporter/Tests/LegacyProxyHelperTest.php
597+
--- a/src/Symfony/Component/VarExporter/Tests/LegacyProxyHelperTest.php
598+
+++ b/src/Symfony/Component/VarExporter/Tests/LegacyProxyHelperTest.php
599+
@@ -134,5 +134,5 @@ class LegacyProxyHelperTest extends ProxyHelperTest
600600
{
601601
yield 'not type hinted __unserialize method' => [new class {
602602
- public function __unserialize($array)
603603
+ public function __unserialize($array): void
604604
{
605605
}
606-
@@ -192,5 +192,5 @@ class ProxyHelperTest extends TestCase
606+
@@ -154,5 +154,5 @@ class LegacyProxyHelperTest extends ProxyHelperTest
607607

608608
yield 'type hinted __unserialize method' => [new class {
609609
- public function __unserialize(array $array)

‎UPGRADE-7.3.md

Copy file name to clipboardExpand all lines: UPGRADE-7.3.md
+8-2Lines 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/Cache/Tests/Traits/RedisProxiesTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php
+3-4Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use Symfony\Component\Cache\Traits\RedisProxyTrait;
1818
use Symfony\Component\Cache\Traits\RelayClusterProxy;
1919
use Symfony\Component\Cache\Traits\RelayProxy;
20-
use Symfony\Component\VarExporter\LazyProxyTrait;
2120
use Symfony\Component\VarExporter\ProxyHelper;
2221

2322
class RedisProxiesTest extends TestCase
@@ -37,7 +36,7 @@ public function testRedisProxy($class)
3736
$methods = [];
3837

3938
foreach ((new \ReflectionClass(\sprintf('Symfony\Component\Cache\Traits\\%s%dProxy', $class, $version)))->getMethods() as $method) {
40-
if ('reset' === $method->name || method_exists(LazyProxyTrait::class, $method->name)) {
39+
if ('reset' === $method->name) {
4140
continue;
4241
}
4342
$return = '__construct' === $method->name || $method->getReturnType() instanceof \ReflectionNamedType && 'void' === (string) $method->getReturnType() ? '' : 'return ';
@@ -89,7 +88,7 @@ public function testRelayProxy()
8988
$expectedMethods = [];
9089

9190
foreach ((new \ReflectionClass(RelayProxy::class))->getMethods() as $method) {
92-
if ('reset' === $method->name || method_exists(LazyProxyTrait::class, $method->name) || $method->isStatic()) {
91+
if ('reset' === $method->name || $method->isStatic()) {
9392
continue;
9493
}
9594

@@ -136,7 +135,7 @@ public function testRelayClusterProxy()
136135
$expectedMethods = [];
137136

138137
foreach ((new \ReflectionClass(RelayClusterProxy::class))->getMethods() as $method) {
139-
if ('reset' === $method->name || method_exists(LazyProxyTrait::class, $method->name) || $method->isStatic()) {
138+
if ('reset' === $method->name || $method->isStatic()) {
140139
continue;
141140
}
142141

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/DependencyInjection/ServicesResetter.php
+4Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/JsonStreamer/CacheWarmer/LazyGhostCacheWarmer.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
*
2323
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
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/JsonStreamer/Tests/CacheWarmer/LazyGhostCacheWarmerTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/JsonStreamer/Tests/CacheWarmer/LazyGhostCacheWarmerTest.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
use Symfony\Component\JsonStreamer\CacheWarmer\LazyGhostCacheWarmer;
1616
use Symfony\Component\JsonStreamer\Tests\Fixtures\Model\ClassicDummy;
1717

18+
/**
19+
* @group legacy
20+
*/
1821
class LazyGhostCacheWarmerTest extends TestCase
1922
{
2023
private string $lazyGhostsDir;

‎src/Symfony/Component/VarExporter/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/VarExporter/CHANGELOG.md
+7Lines 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

+172Lines 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 <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\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

Copy file name to clipboardExpand all lines: src/Symfony/Component/VarExporter/Internal/LazyObjectState.php
+15-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/VarExporter/LazyGhostTrait.php
+7Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/VarExporter/LazyProxyTrait.php
+9-5Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.