8000 feature #59890 [VarExporter] Leverage native lazy objects (nicolas-gr… · symfony/symfony@4bcf132 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4bcf132

Browse files
feature #59890 [VarExporter] Leverage native lazy objects (nicolas-grekas)
This PR was merged into the 7.3 branch. Discussion ---------- [VarExporter] Leverage native lazy objects | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT Let's leverage native lazy objects. Instead of keeping/updating LazyGhostTrait and LazyProxyTrait, I'm deprecating them in favor of using native lazy proxies directly. There is one use case that is not covered by native lazy objects: lazy decorators - aka lazy proxies built against an interface or an internal class. For this use case, we keep `ProxyHelper::generateLazyProxy()`. Commits ------- a00855a [VarExporter] Leverage native lazy objects
2 parents 146c128 + a00855a commit 4bcf132

33 files changed

+981
-226
lines changed

UPGRADE-7.3.md

+7
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,10 @@ VarDumper
201201

202202
* Deprecate `ResourceCaster::castCurl()`, `ResourceCaster::castGd()` and `ResourceCaster::castOpensslX509()`
203203
* Mark all casters as `@internal`
204+
205+
VarExporter
206+
-----------
207+
208+
* Deprecate using `ProxyHelper::generateLazyProxy()` when native lazy proxies can be used - the method should be used to generate abstraction-based lazy decorators only
209+
* Deprecate `LazyGhostTrait` and `LazyProxyTrait`, use native lazy objects instead
210+
* Deprecate `ProxyHelper::generateLazyGhost()`, use native lazy objects instead

src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ public function testRefreshInvalidUser()
136136
$provider->refreshUser($user2);
137137
}
138138

139+
/**
140+
* @group legacy
141+
*/
139142
public function testSupportProxy()
140143
{
141144
$em = DoctrineTestHelper::createTestEntityManager();
@@ -202,6 +205,9 @@ public function testPasswordUpgrades()
202205
$provider->upgradePassword($user, 'foobar');
203206
}
204207

208+
/**
209+
* @group legacy
210+
*/
205211
public function testRefreshedUserProxyIsLoaded()
206212
{
207213
$em = DoctrineTestHelper::createTestEntityManager();

src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php

+3
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ public static function provideUniquenessConstraints(): iterable
223223
yield 'Named arguments' => [new UniqueEntity(message: 'myMessage', fields: ['name'], em: 'foo')];
224224
}
225225

226+
/**
227+
* @group legacy
228+
*/
226229
public function testValidateEntityWithPrivatePropertyAndProxyObject()
227230
{
228231
$entity = new SingleIntIdWithPrivateNameEntity(1, 'Foo');

src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php

+17-7
Original file line numberDiff line numberDiff line change
@@ -187,18 +187,28 @@ public function getProxyClass(Definition $definition, bool $asGhostObject, ?\Ref
187187
$class = 'object' !== $definition->getClass() ? $definition->getClass() : 'stdClass';
188188
$class = new \ReflectionClass($class);
189189

190-
if (\PHP_VERSION_ID >= 80400) {
191-
if ($asGhostObject) {
192-
return $class->name;
193-
}
190+
if (\PHP_VERSION_ID < 80400) {
191+
return preg_replace('/^.*\\\\/', '', $definition->getClass())
192+
.($asGhostObject ? 'Ghost' : 'Proxy')
193+
.ucfirst(substr(hash('xxh128', $this->salt.'+'.$class->name.'+'.serialize($definition->getTag('proxy'))), -7));
194+
}
195+
196+
if ($asGhostObject) {
197+
return $class->name;
198+
}
199+
200+
if (!$definition->hasTag('proxy') && !$class->isInterface()) {
201+
$parent = $class;
202+
do {
203+
$extendsInternalClass = $parent->isInternal();
204+
} while (!$extendsInternalClass && $parent = $parent->getParentClass());
194205

195-
if (!$definition->hasTag('proxy') && !$class->isInterface()) {
206+
if (!$extendsInternalClass) {
196207
return $class->name;
197208
}
198209
}
199210

200-
return preg_replace('/^.*\\\\/', '', $definition->getClass())
201-
.($asGhostObject ? 'Ghost' : 'Proxy')
211+
return preg_replace('/^.*\\\\/', '', $definition->getClass()).'Proxy'
202212
.ucfirst(substr(hash('xxh128', $this->salt.'+'.$class->name.'+'.serialize($definition->getTag('proxy'))), -7));
203213
}
204214
}

src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -2036,7 +2036,11 @@ public function testLazyAutowireAttributeWithIntersection()
20362036

20372037
$dumper = new PhpDumper($container);
20382038

2039-
$this->assertStringEqualsFile(self::$fixturesPath.'/php/lazy_autowire_attribute_with_intersection.php', $dumper->dump());
2039+
if (\PHP_VERSION_ID >= 80400) {
2040+
$this->assertStringEqualsFile(self::$fixturesPath.'/php/lazy_autowire_attribute_with_intersection.php', $dumper->dump());
2041+
} else {
2042+
$this->assertStringEqualsFile(self::$fixturesPath.'/php/legacy_lazy_autowire_attribute_with_intersection.php', $dumper->dump());
2043+
}
20402044
}
20412045

20422046
public function testCallableAdapterConsumer()

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/lazy_autowire_attribute_with_intersection.php

+2-7
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,16 @@ protected static function get_Lazy_Foo_QFdMZVKService($container, $lazyLoad = tr
7474

7575
class objectProxy1fd6daa implements \Symfony\Component\DependencyInjection\Tests\Compiler\AInterface, \Symfony\Component\DependencyInjection\Tests\Compiler\IInterface, \Symfony\Component\VarExporter\LazyObjectInterface
7676
{
77-
use \Symfony\Component\VarExporter\LazyProxyTrait;
77+
use \Symfony\Component\VarExporter\Internal\LazyDecoratorTrait;
7878

7979
private const LAZY_OBJECT_PROPERTY_SCOPES = [];
8080

8181
public function initializeLazyObject(): \Symfony\Component\DependencyInjection\Tests\Compiler\AInterface&\Symfony\Component\DependencyInjection\Tests\Compiler\IInterface
8282
{
83-
if ($state = $this->lazyObjectState ?? null) {
84-
return $state->realInstance ??= ($state->initializer)();
85-
}
86-
87-
return $this;
83+
return $this->lazyObjectState->realInstance;
8884
}
8985
}
9086

9187
// Help opcache.preload discover always-needed symbols
9288
class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class);
9389
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class);
94-
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
4+
use Symfony\Component\DependencyInjection\ContainerInterface;
5+
use Symfony\Component\DependencyInjection\Container;
6+
use Symfony\Component\DependencyInjection\Exception\LogicException;
7+
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
8+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
9+
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
10+
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
11+
12+
/**
13+
* @internal This class has been auto-generated by the Symfony Dependency Injection Component.
14+
*/
15+
class ProjectServiceContainer extends Container
16+
{
17+
protected $parameters = [];
18+
19+
public function __construct()
20+
{
21+
$this->services = $this->privates = [];
22+
$this->methodMap = [
23+
'foo' => 'getFooService',
24+
];
25+
26+
$this->aliases = [];
27+
}
28+
29+
public function compile(): void
30+
{
31+
throw new LogicException('You cannot compile a dumped container that was already compiled.');
32+
}
33+
34+
public function isCompiled(): bool
35+
{
36+
return true;
37+
}
38+
39+
protected function createProxy($class, \Closure $factory)
40+
{
41+
return $factory();
42+
}
43+
44+
/**
45+
* Gets the public 'foo' shared autowired service.
46+
*
47+
* @return \Symfony\Component\DependencyInjection\Tests\Compiler\AAndIInterfaceConsumer
48+
*/
49+
protected static function getFooService($container)
50+
{
51+
$a = ($container->privates['.lazy.foo.qFdMZVK'] ?? self::get_Lazy_Foo_QFdMZVKService($container));
52+
53+
if (isset($container->services['foo'])) {
54+
return $container->services['foo'];
55+
}
56+
57+
return $container->services['foo'] = new \Symfony\Component\DependencyInjection\Tests\Compiler\AAndIInterfaceConsumer($a);
58+
}
59+
60+
/**
61+
* Gets the private '.lazy.foo.qFdMZVK' shared service.
62+
*
63+
* @return \object
64+
*/
65+
protected static function get_Lazy_Foo_QFdMZVKService($container, $lazyLoad = true)
66+
{
67+
if (true === $lazyLoad) {
68+
return $container->privates['.lazy.foo.qFdMZVK'] = $container->createProxy('objectProxy1fd6daa', static fn () => \objectProxy1fd6daa::createLazyProxy(static fn () => self::get_Lazy_Foo_QFdMZVKService($container, false)));
69+
}
70+
71+
return ($container->services['foo'] ?? self::getFooService($container));
72+
}
73+
}
74+
75+
class objectProxy1fd6daa implements \Symfony\Component\DependencyInjection\Tests\Compiler\AInterface, \Symfony\Component\DependencyInjection\Tests\Compiler\IInterface, \Symfony\Component\VarExporter\LazyObjectInterface
76+
{
77+
use \Symfony\Component\VarExporter\LazyProxyTrait;
78+
79+
private const LAZY_OBJECT_PROPERTY_SCOPES = [];
80+
81+
public function initializeLazyObject(): \Symfony\Component\DependencyInjection\Tests\Compiler\AInterface&\Symfony\Component\DependencyInjection\Tests\Compiler\IInterface
82+
{
83+
if ($state = $this->lazyObjectState ?? null) {
84+
return $state->realInstance ??= ($state->initializer)();
85+
}
86+
87+
return $this;
88+
}
89+
}
90+
91+
// Help opcache.preload discover always-needed symbols
92+
class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class);
93+
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class);
94+
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class);

src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy.php

+13-2
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ protected static function getBarService($container, $lazyLoad = true)
6666
protected static function getBazService($container, $lazyLoad = true)
6767
{
6868
if (true === $lazyLoad) {
69-
return $container->services['baz'] = new \ReflectionClass('stdClass')->newLazyProxy(s D7AE tatic fn () => self::getBazService($container, false));
69+
return $container->services['baz'] = $container->createProxy('stdClassProxyAa01f12', static fn () => \stdClassProxyAa01f12::createLazyProxy(static fn () => self::getBazService($container, false)));
7070
}
7171

7272
return \foo_bar();
@@ -80,7 +80,7 @@ protected static function getBazService($container, $lazyLoad = true)
8080
protected static function getBuzService($container, $lazyLoad = true)
8181
{
8282
if (true === $lazyLoad) {
83-
return $container->services['buz'] = new \ReflectionClass('stdClass')->newLazyProxy(static fn () => self::getBuzService($container, false));
83+
return $container->services['buz'] = $container->createProxy('stdClassProxyAa01f12', static fn () => \stdClassProxyAa01f12::createLazyProxy(static fn () => self::getBuzService($container, false)));
8484
}
8585

8686
return \foo_bar();
@@ -100,3 +100,14 @@ protected static function getFooService($container, $lazyLoad = true)
100100
return $lazyLoad;
101101
}
102102
}
103+
104+
class stdClassProxyAa01f12 extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface
105+
{
106+
use \Symfony\Component\VarExporter\Internal\LazyDecoratorTrait;
107+
108+
private const LAZY_OBJECT_PROPERTY_SCOPES = [];
109+
}
110+
111+
// Help opcache.preload discover always-needed symbols
112+
class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class);
113+
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class);

src/Symfony/Component/HttpKernel/Tests/Fixtures/LazyResettableService.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace Symfony\Component\HttpKernel\Tests\Fixtures;
1313

14-
class LazyResettableService
14+
class LazyResettableService extends \stdClass
1515
{
1616
public static $counter = 0;
1717

src/Symfony/Component/VarExporter/CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
CHANGELOG
22
=========
33

4+
7.3
5+
---
6+
7+
* Deprecate using `ProxyHelper::generateLazyProxy()` when native lazy proxies can be used - the method should be used to generate abstraction-based lazy decorators only
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

0 commit comments

Comments
 (0)
0