From 7380dca80f9a22d11ad97a230ddbd3d77cd61973 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 1 Aug 2025 17:44:34 +0200 Subject: [PATCH 1/4] Fix wrong boolean values --- Tests/Dumper/PhpDumperTest.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Tests/Dumper/PhpDumperTest.php b/Tests/Dumper/PhpDumperTest.php index b943c35ad..cb9666179 100644 --- a/Tests/Dumper/PhpDumperTest.php +++ b/Tests/Dumper/PhpDumperTest.php @@ -1856,7 +1856,7 @@ public function testClosureProxy() { $container = new ContainerBuilder(); $container->register('closure_proxy', SingleMethodInterface::class) - ->setPublic('true') + ->setPublic(true) ->setFactory(['Closure', 'fromCallable']) ->setArguments([[new Reference('foo'), 'cloneFoo']]) ->setLazy(true); @@ -1878,12 +1878,12 @@ public function testClosure() { $container = new ContainerBuilder(); $container->register('closure', 'Closure') - ->setPublic('true') + ->setPublic(true) ->setFactory(['Closure', 'fromCallable']) ->setArguments([new Reference('bar')]); $container->register('bar', 'stdClass'); $container->register('closure_of_service_closure', 'Closure') - ->setPublic('true') + ->setPublic(true) ->setFactory(['Closure', 'fromCallable']) ->setArguments([new ServiceClosureArgument(new Reference('bar2'))]); $container->register('bar2', 'stdClass'); @@ -1897,15 +1897,15 @@ public function testAutowireClosure() { $container = new ContainerBuilder(); $container->register('foo', Foo::class) - ->setPublic('true'); + ->setPublic(true); $container->register('my_callable', MyCallable::class) - ->setPublic('true'); + ->setPublic(true); $container->register('baz', \Closure::class) ->setFactory(['Closure', 'fromCallable']) ->setArguments(['var_dump']) - ->setPublic('true'); + ->setPublic(true); $container->register('bar', LazyClosureConsumer::class) - ->setPublic('true') + ->setPublic(true) ->setAutowired(true); $container->compile(); $dumper = new PhpDumper($container); @@ -1931,12 +1931,12 @@ public function testLazyClosure() { $container = new ContainerBuilder(); $container->register('closure1', 'Closure') - ->setPublic('true') + ->setPublic(true) ->setFactory(['Closure', 'fromCallable']) ->setLazy(true) ->setArguments([[new Reference('foo'), 'cloneFoo']]); $container->register('closure2', 'Closure') - ->setPublic('true') + ->setPublic(true) ->setFactory(['Closure', 'fromCallable']) ->setLazy(true) ->setArguments([[new Reference('foo_void'), '__invoke']]); @@ -1970,10 +1970,10 @@ public function testLazyAutowireAttribute() { $container = new ContainerBuilder(); $container->register('foo', Foo::class) - ->setPublic('true'); + ->setPublic(true); $container->setAlias(Foo::class, 'foo'); $container->register('bar', LazyServiceConsumer::class) - ->setPublic('true') + ->setPublic(true) ->setAutowired(true); $container->compile(); $dumper = new PhpDumper($container); @@ -1993,7 +1993,7 @@ public function testLazyAutowireAttributeWithIntersection() { $container = new ContainerBuilder(); $container->register('foo', AAndIInterfaceConsumer::class) - ->setPublic('true') + ->setPublic(true) ->setAutowired(true); $container->compile(); @@ -2017,7 +2017,7 @@ public function testCallableAdapterConsumer() $container = new ContainerBuilder(); $container->register('foo', Foo::class); $container->register('bar', CallableAdapterConsumer::class) - ->setPublic('true') + ->setPublic(true) ->setAutowired(true); $container->compile(); $dumper = new PhpDumper($container); From 51aa1898b2d2b3e2634e420992c00f0ecf0574fe Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 12 Aug 2025 17:12:44 +0200 Subject: [PATCH 2/4] Run high-deps tests on PHP 8.4 --- Tests/Dumper/PhpDumperTest.php | 19 ++- .../Fixtures/php/lazy_autowire_attribute.php | 15 ++- ...y_autowire_attribute_with_intersection.php | 9 +- .../php/legacy_lazy_autowire_attribute.php | 98 ++++++++++++++ ...y_autowire_attribute_with_intersection.php | 101 ++++++++++++++ .../php/legacy_services_dedup_lazy.php | 126 ++++++++++++++++++ .../php/legacy_services_wither_lazy.php | 86 ++++++++++++ ...legacy_services_wither_lazy_non_shared.php | 88 ++++++++++++ Tests/Fixtures/php/services_dedup_lazy.php | 3 +- Tests/Fixtures/php/services_wither_lazy.php | 39 +++++- .../php/services_wither_lazy_non_shared.php | 39 +++++- 11 files changed, 602 insertions(+), 21 deletions(-) create mode 100644 Tests/Fixtures/php/legacy_lazy_autowire_attribute.php create mode 100644 Tests/Fixtures/php/legacy_lazy_autowire_attribute_with_intersection.php create mode 100644 Tests/Fixtures/php/legacy_services_dedup_lazy.php create mode 100644 Tests/Fixtures/php/legacy_services_wither_lazy.php create mode 100644 Tests/Fixtures/php/legacy_services_wither_lazy_non_shared.php diff --git a/Tests/Dumper/PhpDumperTest.php b/Tests/Dumper/PhpDumperTest.php index cb9666179..8206a4a13 100644 --- a/Tests/Dumper/PhpDumperTest.php +++ b/Tests/Dumper/PhpDumperTest.php @@ -68,6 +68,7 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\WitherStaticReturnType; use Symfony\Component\DependencyInjection\TypedReference; use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\VarExporter\Internal\LazyDecoratorTrait; use Symfony\Component\VarExporter\LazyObjectInterface; require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; @@ -920,7 +921,9 @@ public function testDedupLazyProxy() $dumper = new PhpDumper($container); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_dedup_lazy.php', $dumper->dump()); + + $legacy = \PHP_VERSION_ID < 80400 || !trait_exists(LazyDecoratorTrait::class) ? 'legacy_' : ''; + $this->assertStringEqualsFile(self::$fixturesPath.'/php/'.$legacy.'services_dedup_lazy.php', $dumper->dump()); } public function testLazyArgumentProvideGenerator() @@ -1616,7 +1619,8 @@ public function testLazyWither() $container->compile(); $dumper = new PhpDumper($container); $dump = $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Service_Wither_Lazy']); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_wither_lazy.php', $dump); + $legacy = \PHP_VERSION_ID < 80400 || !trait_exists(LazyDecoratorTrait::class) ? 'legacy_' : ''; + $this->assertStringEqualsFile(self::$fixturesPath.'/php/'.$legacy.'services_wither_lazy.php', $dump); eval('?>'.$dump); $container = new \Symfony_DI_PhpDumper_Service_Wither_Lazy(); @@ -1641,7 +1645,8 @@ public function testLazyWitherNonShared() $container->compile(); $dumper = new PhpDumper($container); $dump = $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Service_Wither_Lazy_Non_Shared']); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_wither_lazy_non_shared.php', $dump); + $legacy = \PHP_VERSION_ID < 80400 || !trait_exists(LazyDecoratorTrait::class) ? 'legacy_' : ''; + $this->assertStringEqualsFile(self::$fixturesPath.'/php/'.$legacy.'services_wither_lazy_non_shared.php', $dump); eval('?>'.$dump); $container = new \Symfony_DI_PhpDumper_Service_Wither_Lazy_Non_Shared(); @@ -1978,9 +1983,10 @@ public function testLazyAutowireAttribute() $container->compile(); $dumper = new PhpDumper($container); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/lazy_autowire_attribute.php', $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Test_Lazy_Autowire_Attribute'])); + $legacy = \PHP_VERSION_ID < 80400 || !trait_exists(LazyDecoratorTrait::class) ? 'legacy_' : ''; + $this->assertStringEqualsFile(self::$fixturesPath.'/php/'.$legacy.'lazy_autowire_attribute.php', $dumper->dump(['class' => 'Symfony_DI_PhpDumper_Test_Lazy_Autowire_Attribute'])); - require self::$fixturesPath.'/php/lazy_autowire_attribute.php'; + require self::$fixturesPath.'/php/'.$legacy.'lazy_autowire_attribute.php'; $container = new \Symfony_DI_PhpDumper_Test_Lazy_Autowire_Attribute(); @@ -2009,7 +2015,8 @@ public function testLazyAutowireAttributeWithIntersection() $dumper = new PhpDumper($container); - $this->assertStringEqualsFile(self::$fixturesPath.'/php/lazy_autowire_attribute_with_intersection.php', $dumper->dump()); + $legacy = \PHP_VERSION_ID < 80400 || !trait_exists(LazyDecoratorTrait::class) ? 'legacy_' : ''; + $this->assertStringEqualsFile(self::$fixturesPath.'/php/'.$legacy.'lazy_autowire_attribute_with_intersection.php', $dumper->dump()); } public function testCallableAdapterConsumer() diff --git a/Tests/Fixtures/php/lazy_autowire_attribute.php b/Tests/Fixtures/php/lazy_autowire_attribute.php index 813407586..83c11c7ac 100644 --- a/Tests/Fixtures/php/lazy_autowire_attribute.php +++ b/Tests/Fixtures/php/lazy_autowire_attribute.php @@ -87,12 +87,23 @@ protected static function getFoo2Service($container, $lazyLoad = true) class FooProxy4048957 extends \Symfony\Component\DependencyInjection\Tests\Compiler\Foo implements \Symfony\Component\VarExporter\LazyObjectInterface { - use \Symfony\Component\VarExporter\LazyProxyTrait; + use \Symfony\Component\VarExporter\Internal\LazyDecoratorTrait; private const LAZY_OBJECT_PROPERTY_SCOPES = []; + + public function cloneFoo(?\stdClass $bar = null): static + { + ${0} = $this->lazyObjectState->realInstance; + ${1} = ${0}->cloneFoo(...\func_get_args()); + + return match (true) { + ${1} === ${0} => $this, + !${1} instanceof ${0} || !${0} instanceof ${1} => ${1}, + null !== $this->lazyObjectState->cloneInstance =& ${1} => clone $this, + }; + } } // Help opcache.preload discover always-needed symbols class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); -class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); diff --git a/Tests/Fixtures/php/lazy_autowire_attribute_with_intersection.php b/Tests/Fixtures/php/lazy_autowire_attribute_with_intersection.php index 8dc0eb50e..a4223cd64 100644 --- a/Tests/Fixtures/php/lazy_autowire_attribute_with_intersection.php +++ b/Tests/Fixtures/php/lazy_autowire_attribute_with_intersection.php @@ -81,21 +81,16 @@ protected static function get_Lazy_Foo_GDmfketService($container, $lazyLoad = tr class objectProxy8ac8e9a implements \Symfony\Component\DependencyInjection\Tests\Compiler\AInterface, \Symfony\Component\DependencyInjection\Tests\Compiler\IInterface, \Symfony\Component\VarExporter\LazyObjectInterface { - use \Symfony\Component\VarExporter\LazyProxyTrait; + use \Symfony\Component\VarExporter\Internal\LazyDecoratorTrait; private const LAZY_OBJECT_PROPERTY_SCOPES = []; public function initializeLazyObject(): \Symfony\Component\DependencyInjection\Tests\Compiler\AInterface&\Symfony\Component\DependencyInjection\Tests\Compiler\IInterface { - if ($state = $this->lazyObjectState ?? null) { - return $state->realInstance ??= ($state->initializer)(); - } - - return $this; + return $this->lazyObjectState->realInstance; } } // Help opcache.preload discover always-needed symbols class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); -class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); diff --git a/Tests/Fixtures/php/legacy_lazy_autowire_attribute.php b/Tests/Fixtures/php/legacy_lazy_autowire_attribute.php new file mode 100644 index 000000000..813407586 --- /dev/null +++ b/Tests/Fixtures/php/legacy_lazy_autowire_attribute.php @@ -0,0 +1,98 @@ +services = $this->privates = []; + $this->methodMap = [ + 'bar' => 'getBarService', + 'foo' => 'getFooService', + ]; + + $this->aliases = []; + } + + public function compile(): void + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled(): bool + { + return true; + } + + public function getRemovedIds(): array + { + return [ + '.lazy.Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo' => true, + 'Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo' => true, + ]; + } + + protected function createProxy($class, \Closure $factory) + { + return $factory(); + } + + /** + * Gets the public 'bar' shared autowired service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Dumper\LazyServiceConsumer + */ + protected static function getBarService($container) + { + return $container->services['bar'] = new \Symfony\Component\DependencyInjection\Tests\Dumper\LazyServiceConsumer(($container->privates['.lazy.Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo'] ?? self::getFoo2Service($container))); + } + + /** + * Gets the public 'foo' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Compiler\Foo + */ + protected static function getFooService($container) + { + return $container->services['foo'] = new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo(); + } + + /** + * Gets the private '.lazy.Symfony\Component\DependencyInjection\Tests\Compiler\Foo' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Compiler\Foo + */ + protected static function getFoo2Service($container, $lazyLoad = true) + { + if (true === $lazyLoad) { + return $container->privates['.lazy.Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo'] = $container->createProxy('FooProxy4048957', static fn () => \FooProxy4048957::createLazyProxy(static fn () => self::getFoo2Service($container, false))); + } + + return ($container->services['foo'] ??= new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo()); + } +} + +class FooProxy4048957 extends \Symfony\Component\DependencyInjection\Tests\Compiler\Foo implements \Symfony\Component\VarExporter\LazyObjectInterface +{ + use \Symfony\Component\VarExporter\LazyProxyTrait; + + private const LAZY_OBJECT_PROPERTY_SCOPES = []; +} + +// Help opcache.preload discover always-needed symbols +class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); diff --git a/Tests/Fixtures/php/legacy_lazy_autowire_attribute_with_intersection.php b/Tests/Fixtures/php/legacy_lazy_autowire_attribute_with_intersection.php new file mode 100644 index 000000000..8dc0eb50e --- /dev/null +++ b/Tests/Fixtures/php/legacy_lazy_autowire_attribute_with_intersection.php @@ -0,0 +1,101 @@ +services = $this->privates = []; + $this->methodMap = [ + 'foo' => 'getFooService', + ]; + + $this->aliases = []; + } + + public function compile(): void + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled(): bool + { + return true; + } + + public function getRemovedIds(): array + { + return [ + '.lazy.foo.gDmfket' => true, + ]; + } + + protected function createProxy($class, \Closure $factory) + { + return $factory(); + } + + /** + * Gets the public 'foo' shared autowired service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Compiler\AAndIInterfaceConsumer + */ + protected static function getFooService($container) + { + $a = ($container->privates['.lazy.foo.gDmfket'] ?? self::get_Lazy_Foo_GDmfketService($container)); + + if (isset($container->services['foo'])) { + return $container->services['foo']; + } + + return $container->services['foo'] = new \Symfony\Component\DependencyInjection\Tests\Compiler\AAndIInterfaceConsumer($a); + } + + /** + * Gets the private '.lazy.foo.gDmfket' shared service. + * + * @return \object + */ + protected static function get_Lazy_Foo_GDmfketService($container, $lazyLoad = true) + { + if (true === $lazyLoad) { + return $container->privates['.lazy.foo.gDmfket'] = $container->createProxy('objectProxy8ac8e9a', static fn () => \objectProxy8ac8e9a::createLazyProxy(static fn () => self::get_Lazy_Foo_GDmfketService($container, false))); + } + + return ($container->services['foo'] ?? self::getFooService($container)); + } +} + +class objectProxy8ac8e9a implements \Symfony\Component\DependencyInjection\Tests\Compiler\AInterface, \Symfony\Component\DependencyInjection\Tests\Compiler\IInterface, \Symfony\Component\VarExporter\LazyObjectInterface +{ + use \Symfony\Component\VarExporter\LazyProxyTrait; + + private const LAZY_OBJECT_PROPERTY_SCOPES = []; + + public function initializeLazyObject(): \Symfony\Component\DependencyInjection\Tests\Compiler\AInterface&\Symfony\Component\DependencyInjection\Tests\Compiler\IInterface + { + if ($state = $this->lazyObjectState ?? null) { + return $state->realInstance ??= ($state->initializer)(); + } + + return $this; + } +} + +// Help opcache.preload discover always-needed symbols +class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); diff --git a/Tests/Fixtures/php/legacy_services_dedup_lazy.php b/Tests/Fixtures/php/legacy_services_dedup_lazy.php new file mode 100644 index 000000000..006820f52 --- /dev/null +++ b/Tests/Fixtures/php/legacy_services_dedup_lazy.php @@ -0,0 +1,126 @@ +services = $this->privates = []; + $this->methodMap = [ + 'bar' => 'getBarService', + 'baz' => 'getBazService', + 'buz' => 'getBuzService', + 'foo' => 'getFooService', + ]; + + $this->aliases = []; + } + + public function compile(): void + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled(): bool + { + return true; + } + + protected function createProxy($class, \Closure $factory) + { + return $factory(); + } + + /** + * Gets the public 'bar' shared service. + * + * @return \stdClass + */ + protected static function getBarService($container, $lazyLoad = true) + { + if (true === $lazyLoad) { + return $container->services['bar'] = $container->createProxy('stdClassGhost2fc7938', static fn () => \stdClassGhost2fc7938::createLazyGhost(static fn ($proxy) => self::getBarService($container, $proxy))); + } + + return $lazyLoad; + } + + /** + * Gets the public 'baz' shared service. + * + * @return \stdClass + */ + protected static function getBazService($container, $lazyLoad = true) + { + if (true === $lazyLoad) { + return $container->services['baz'] = $container->createProxy('stdClassProxy2fc7938', static fn () => \stdClassProxy2fc7938::createLazyProxy(static fn () => self::getBazService($container, false))); + } + + return \foo_bar(); + } + + /** + * Gets the public 'buz' shared service. + * + * @return \stdClass + */ + protected static function getBuzService($container, $lazyLoad = true) + { + if (true === $lazyLoad) { + return $container->services['buz'] = $container->createProxy('stdClassProxy2fc7938', static fn () => \stdClassProxy2fc7938::createLazyProxy(static fn () => self::getBuzService($container, false))); + } + + return \foo_bar(); + } + + /** + * Gets the public 'foo' shared service. + * + * @return \stdClass + */ + protected static function getFooService($container, $lazyLoad = true) + { + if (true === $lazyLoad) { + return $container->services['foo'] = $container->createProxy('stdClassGhost2fc7938', static fn () => \stdClassGhost2fc7938::createLazyGhost(static fn ($proxy) => self::getFooService($container, $proxy))); + } + + return $lazyLoad; + } +} + +class stdClassGhost2fc7938 extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface +{ + use \Symfony\Component\VarExporter\LazyGhostTrait; + + private const LAZY_OBJECT_PROPERTY_SCOPES = []; +} + +// Help opcache.preload discover always-needed symbols +class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); + +class stdClassProxy2fc7938 extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface +{ + use \Symfony\Component\VarExporter\LazyProxyTrait; + + private const LAZY_OBJECT_PROPERTY_SCOPES = []; +} + +// Help opcache.preload discover always-needed symbols +class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); diff --git a/Tests/Fixtures/php/legacy_services_wither_lazy.php b/Tests/Fixtures/php/legacy_services_wither_lazy.php new file mode 100644 index 000000000..81dd1a0b9 --- /dev/null +++ b/Tests/Fixtures/php/legacy_services_wither_lazy.php @@ -0,0 +1,86 @@ +services = $this->privates = []; + $this->methodMap = [ + 'wither' => 'getWitherService', + ]; + + $this->aliases = []; + } + + public function compile(): void + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled(): bool + { + return true; + } + + public function getRemovedIds(): array + { + return [ + 'Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo' => true, + ]; + } + + protected function createProxy($class, \Closure $factory) + { + return $factory(); + } + + /** + * Gets the public 'wither' shared autowired service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Compiler\Wither + */ + protected static function getWitherService($container, $lazyLoad = true) + { + if (true === $lazyLoad) { + return $container->services['wither'] = $container->createProxy('WitherProxy580fe0f', static fn () => \WitherProxy580fe0f::createLazyProxy(static fn () => self::getWitherService($container, false))); + } + + $instance = new \Symfony\Component\DependencyInjection\Tests\Compiler\Wither(); + + $a = ($container->privates['Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo'] ??= new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo()); + + $instance = $instance->withFoo1($a); + $instance = $instance->withFoo2($a); + $instance->setFoo($a); + + return $instance; + } +} + +class WitherProxy580fe0f extends \Symfony\Component\DependencyInjection\Tests\Compiler\Wither implements \Symfony\Component\VarExporter\LazyObjectInterface +{ + use \Symfony\Component\VarExporter\LazyProxyTrait; + + private const LAZY_OBJECT_PROPERTY_SCOPES = [ + 'foo' => [parent::class, 'foo', null, 4], + ]; +} + +// Help opcache.preload discover always-needed symbols +class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); diff --git a/Tests/Fixtures/php/legacy_services_wither_lazy_non_shared.php b/Tests/Fixtures/php/legacy_services_wither_lazy_non_shared.php new file mode 100644 index 000000000..8952ebd6d --- /dev/null +++ b/Tests/Fixtures/php/legacy_services_wither_lazy_non_shared.php @@ -0,0 +1,88 @@ +services = $this->privates = []; + $this->methodMap = [ + 'wither' => 'getWitherService', + ]; + + $this->aliases = []; + } + + public function compile(): void + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled(): bool + { + return true; + } + + public function getRemovedIds(): array + { + return [ + 'Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo' => true, + ]; + } + + protected function createProxy($class, \Closure $factory) + { + return $factory(); + } + + /** + * Gets the public 'wither' autowired service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Compiler\Wither + */ + protected static function getWitherService($container, $lazyLoad = true) + { + $container->factories['wither'] ??= fn () => self::getWitherService($container); + + if (true === $lazyLoad) { + return $container->createProxy('WitherProxyDd381be', static fn () => \WitherProxyDd381be::createLazyProxy(static fn () => self::getWitherService($container, false))); + } + + $instance = new \Symfony\Component\DependencyInjection\Tests\Compiler\Wither(); + + $a = ($container->privates['Symfony\\Component\\DependencyInjection\\Tests\\Compiler\\Foo'] ??= new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo()); + + $instance = $instance->withFoo1($a); + $instance = $instance->withFoo2($a); + $instance->setFoo($a); + + return $instance; + } +} + +class WitherProxyDd381be extends \Symfony\Component\DependencyInjection\Tests\Compiler\Wither implements \Symfony\Component\VarExporter\LazyObjectInterface +{ + use \Symfony\Component\VarExporter\LazyProxyTrait; + + private const LAZY_OBJECT_PROPERTY_SCOPES = [ + 'foo' => [parent::class, 'foo', null, 4], + ]; +} + +// Help opcache.preload discover always-needed symbols +class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); +class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); diff --git a/Tests/Fixtures/php/services_dedup_lazy.php b/Tests/Fixtures/php/services_dedup_lazy.php index 006820f52..ceabc0bbf 100644 --- a/Tests/Fixtures/php/services_dedup_lazy.php +++ b/Tests/Fixtures/php/services_dedup_lazy.php @@ -115,7 +115,7 @@ class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); class stdClassProxy2fc7938 extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface { - use \Symfony\Component\VarExporter\LazyProxyTrait; + use \Symfony\Component\VarExporter\Internal\LazyDecoratorTrait; private const LAZY_OBJECT_PROPERTY_SCOPES = []; } @@ -123,4 +123,3 @@ class stdClassProxy2fc7938 extends \stdClass implements \Symfony\Component\VarEx // Help opcache.preload discover always-needed symbols class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); -class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); diff --git a/Tests/Fixtures/php/services_wither_lazy.php b/Tests/Fixtures/php/services_wither_lazy.php index 81dd1a0b9..6a1a511dd 100644 --- a/Tests/Fixtures/php/services_wither_lazy.php +++ b/Tests/Fixtures/php/services_wither_lazy.php @@ -73,14 +73,49 @@ protected static function getWitherService($container, $lazyLoad = true) class WitherProxy580fe0f extends \Symfony\Component\DependencyInjection\Tests\Compiler\Wither implements \Symfony\Component\VarExporter\LazyObjectInterface { - use \Symfony\Component\VarExporter\LazyProxyTrait; + use \Symfony\Component\VarExporter\Internal\LazyDecoratorTrait; private const LAZY_OBJECT_PROPERTY_SCOPES = [ 'foo' => [parent::class, 'foo', null, 4], ]; + + public function setFoo(\Symfony\Component\DependencyInjection\Tests\Compiler\Foo $foo) + { + ${0} = $this->lazyObjectState->realInstance; + ${1} = ${0}->setFoo(...\func_get_args()); + + return match (true) { + ${1} === ${0} => $this, + !${1} instanceof ${0} || !${0} instanceof ${1} => ${1}, + null !== $this->lazyObjectState->cloneInstance =& ${1} => clone $this, + }; + } + + public function withFoo1(\Symfony\Component\DependencyInjection\Tests\Compiler\Foo $foo): static + { + ${0} = $this->lazyObjectState->realInstance; + ${1} = ${0}->withFoo1(...\func_get_args()); + + return match (true) { + ${1} === ${0} => $this, + !${1} instanceof ${0} || !${0} instanceof ${1} => ${1}, + null !== $this->lazyObjectState->cloneInstance =& ${1} => clone $this, + }; + } + + public function withFoo2(\Symfony\Component\DependencyInjection\Tests\Compiler\Foo $foo): static + { + ${0} = $this->lazyObjectState->realInstance; + ${1} = ${0}->withFoo2(...\func_get_args()); + + return match (true) { + ${1} === ${0} => $this, + !${1} instanceof ${0} || !${0} instanceof ${1} => ${1}, + null !== $this->lazyObjectState->cloneInstance =& ${1} => clone $this, + }; + } } // Help opcache.preload discover always-needed symbols class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); -class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); diff --git a/Tests/Fixtures/php/services_wither_lazy_non_shared.php b/Tests/Fixtures/php/services_wither_lazy_non_shared.php index 8952ebd6d..86d798ad9 100644 --- a/Tests/Fixtures/php/services_wither_lazy_non_shared.php +++ b/Tests/Fixtures/php/services_wither_lazy_non_shared.php @@ -75,14 +75,49 @@ protected static function getWitherService($container, $lazyLoad = true) class WitherProxyDd381be extends \Symfony\Component\DependencyInjection\Tests\Compiler\Wither implements \Symfony\Component\VarExporter\LazyObjectInterface { - use \Symfony\Component\VarExporter\LazyProxyTrait; + use \Symfony\Component\VarExporter\Internal\LazyDecoratorTrait; private const LAZY_OBJECT_PROPERTY_SCOPES = [ 'foo' => [parent::class, 'foo', null, 4], ]; + + public function setFoo(\Symfony\Component\DependencyInjection\Tests\Compiler\Foo $foo) + { + ${0} = $this->lazyObjectState->realInstance; + ${1} = ${0}->setFoo(...\func_get_args()); + + return match (true) { + ${1} === ${0} => $this, + !${1} instanceof ${0} || !${0} instanceof ${1} => ${1}, + null !== $this->lazyObjectState->cloneInstance =& ${1} => clone $this, + }; + } + + public function withFoo1(\Symfony\Component\DependencyInjection\Tests\Compiler\Foo $foo): static + { + ${0} = $this->lazyObjectState->realInstance; + ${1} = ${0}->withFoo1(...\func_get_args()); + + return match (true) { + ${1} === ${0} => $this, + !${1} instanceof ${0} || !${0} instanceof ${1} => ${1}, + null !== $this->lazyObjectState->cloneInstance =& ${1} => clone $this, + }; + } + + public function withFoo2(\Symfony\Component\DependencyInjection\Tests\Compiler\Foo $foo): static + { + ${0} = $this->lazyObjectState->realInstance; + ${1} = ${0}->withFoo2(...\func_get_args()); + + return match (true) { + ${1} === ${0} => $this, + !${1} instanceof ${0} || !${0} instanceof ${1} => ${1}, + null !== $this->lazyObjectState->cloneInstance =& ${1} => clone $this, + }; + } } // Help opcache.preload discover always-needed symbols class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); -class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); From 900da8a42eceeb4a13a0ec34caa7db49328daff3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 13 Aug 2025 10:17:00 +0200 Subject: [PATCH 3/4] Remove deprecated calls to deprecated methods of SplObjectStorage --- Dumper/PhpDumper.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dumper/PhpDumper.php b/Dumper/PhpDumper.php index 6604d82e7..b897cb2fc 100644 --- a/Dumper/PhpDumper.php +++ b/Dumper/PhpDumper.php @@ -814,7 +814,7 @@ private function addServiceConfigurator(Definition $definition, string $variable if (\is_array($callable)) { if ($callable[0] instanceof Reference - || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0])) + || ($callable[0] instanceof Definition && $this->definitionVariables->offsetExists($callable[0])) ) { return \sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); } @@ -1205,7 +1205,7 @@ private function addNewInstance(Definition $definition, string $return = '', ?st if (['...'] === $arguments && ('Closure' !== ($class = $definition->getClass() ?: 'Closure') || $definition->isLazy() && ( $callable[0] instanceof Reference - || ($callable[0] instanceof Definition && !$this->definitionVariables->contains($callable[0])) + || ($callable[0] instanceof Definition && !$this->definitionVariables->offsetExists($callable[0])) ))) { $initializer = 'fn () => '.$this->dumpValue($callable[0]); @@ -1213,7 +1213,7 @@ private function addNewInstance(Definition $definition, string $return = '', ?st } if ($callable[0] instanceof Reference - || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0])) + || ($callable[0] instanceof Definition && $this->definitionVariables->offsetExists($callable[0])) ) { return $return.\sprintf('%s->%s(%s)', $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '').$tail; } @@ -1920,7 +1920,7 @@ private function dumpValue(mixed $value, bool $interpolate = true): string if ($value->hasErrors() && $e = $value->getErrors()) { return \sprintf('throw new RuntimeException(%s)', $this->export(reset($e))); } - if ($this->definitionVariables?->contains($value)) { + if ($this->definitionVariables?->offsetExists($value)) { return $this->dumpValue($this->definitionVariables[$value], $interpolate); } if ($value->getMethodCalls()) { From ab6c38dad5da9b15b1f7afb2f5c5814112e70261 Mon Sep 17 00:00:00 2001 From: matlec Date: Thu, 14 Aug 2025 11:13:45 +0200 Subject: [PATCH 4/4] =?UTF-8?q?[DependencyInjection]=20Don=E2=80=99t=20aut?= =?UTF-8?q?owire=20excluded=20services?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Compiler/AutowirePass.php | 23 +++++++++++++++-------- Tests/Compiler/AutowirePassTest.php | 4 ++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Compiler/AutowirePass.php b/Compiler/AutowirePass.php index e394cf105..ef5642c3a 100644 --- a/Compiler/AutowirePass.php +++ b/Compiler/AutowirePass.php @@ -459,26 +459,26 @@ private function getAutowiredReference(TypedReference $reference, bool $filterTy $name = $target = (array_filter($reference->getAttributes(), static fn ($a) => $a instanceof Target)[0] ?? null)?->name; if (null !== $name ??= $reference->getName()) { - if ($this->container->has($alias = $type.' $'.$name) && !$this->container->findDefinition($alias)->isAbstract()) { + if ($this->container->has($alias = $type.' $'.$name) && $this->canDefinitionBeAutowired($alias)) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } - if (null !== ($alias = $this->getCombinedAlias($type, $name)) && !$this->container->findDefinition($alias)->isAbstract()) { + if (null !== ($alias = $this->getCombinedAlias($type, $name)) && $this->canDefinitionBeAutowired($alias)) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } $parsedName = (new Target($name))->getParsedName(); - if ($this->container->has($alias = $type.' $'.$parsedName) && !$this->container->findDefinition($alias)->isAbstract()) { + if ($this->container->has($alias = $type.' $'.$parsedName) && $this->canDefinitionBeAutowired($alias)) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } - if (null !== ($alias = $this->getCombinedAlias($type, $parsedName)) && !$this->container->findDefinition($alias)->isAbstract()) { + if (null !== ($alias = $this->getCombinedAlias($type, $parsedName)) && $this->canDefinitionBeAutowired($alias)) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } - if (($this->container->has($n = $name) && !$this->container->findDefinition($n)->isAbstract()) - || ($this->container->has($n = $parsedName) && !$this->container->findDefinition($n)->isAbstract()) + if (($this->container->has($n = $name) && $this->canDefinitionBeAutowired($n)) + || ($this->container->has($n = $parsedName) && $this->canDefinitionBeAutowired($n)) ) { foreach ($this->container->getAliases() as $id => $alias) { if ($n === (string) $alias && str_starts_with($id, $type.' $')) { @@ -492,17 +492,24 @@ private function getAutowiredReference(TypedReference $reference, bool $filterTy } } - if ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract()) { + if ($this->container->has($type) && $this->canDefinitionBeAutowired($type)) { return new TypedReference($type, $type, $reference->getInvalidBehavior()); } - if (null !== ($alias = $this->getCombinedAlias($type)) && !$this->container->findDefinition($alias)->isAbstract()) { + if (null !== ($alias = $this->getCombinedAlias($type)) && $this->canDefinitionBeAutowired($alias)) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } return null; } + private function canDefinitionBeAutowired(string $id): bool + { + $definition = $this->container->findDefinition($id); + + return !$definition->isAbstract() && !$definition->hasTag('container.excluded'); + } + /** * Populates the list of available types. */ diff --git a/Tests/Compiler/AutowirePassTest.php b/Tests/Compiler/AutowirePassTest.php index 114d514ad..d6bbbc70f 100644 --- a/Tests/Compiler/AutowirePassTest.php +++ b/Tests/Compiler/AutowirePassTest.php @@ -1322,7 +1322,7 @@ public function testTypeSymbolExcluded() { $container = new ContainerBuilder(); - $container->register(Foo::class)->setAbstract(true)->addTag('container.excluded', ['source' => 'for tests']); + $container->register(Foo::class)->addTag('container.excluded', ['source' => 'for tests']); $aDefinition = $container->register('a', NotGuessableArgument::class); $aDefinition->setAutowired(true); @@ -1339,7 +1339,7 @@ public function testTypeNamespaceExcluded() { $container = new ContainerBuilder(); - $container->register(__NAMESPACE__)->setAbstract(true)->addTag('container.excluded'); + $container->register(__NAMESPACE__)->addTag('container.excluded'); $aDefinition = $container->register('a', NotGuessableArgument::class); $aDefinition->setAutowired(true);