From 58a184826ada85e58769ef5ae7c7f3efb8c5dad0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 23 Jun 2022 10:47:36 +0200 Subject: [PATCH] [DependencyInjection] Use lazy-loading ghost object proxies out of the box --- .../Bridge/Doctrine/ManagerRegistry.php | 10 +- .../Tests/LazyProxy/ContainerBuilderTest.php | 5 +- .../Tests/LazyProxy/Dumper/PhpDumperTest.php | 7 +- src/Symfony/Bridge/ProxyManager/composer.json | 4 +- .../Bundle/FrameworkBundle/composer.json | 2 +- .../Bundle/SecurityBundle/composer.json | 2 +- .../DependencyInjection/CHANGELOG.md | 2 + .../DependencyInjection/ContainerBuilder.php | 5 +- .../DependencyInjection/Dumper/PhpDumper.php | 5 +- .../Instantiator/LazyServiceInstantiator.php | 43 +++++ .../Instantiator/RealServiceInstantiator.php | 4 + .../LazyProxy/PhpDumper/LazyServiceDumper.php | 153 ++++++++++++++++++ .../Tests/ContainerBuilderTest.php | 5 + .../Tests/Dumper/PhpDumperTest.php | 16 +- .../Tests/Fixtures/includes/classes.php | 7 +- .../php/services_dedup_lazy_ghost.php | 17 +- .../php/services_non_shared_lazy_as_files.txt | 21 ++- .../php/services_non_shared_lazy_ghost.php | 13 +- .../RealServiceInstantiatorTest.php | 2 + src/Symfony/Component/HttpKernel/Kernel.php | 9 -- .../Component/HttpKernel/composer.json | 4 +- 21 files changed, 284 insertions(+), 52 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php create mode 100644 src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php diff --git a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php index 5e958d1865300..269eb517ba1a3 100644 --- a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php +++ b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php @@ -16,6 +16,7 @@ use ProxyManager\Proxy\LazyLoadingInterface; use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\VarExporter\LazyGhostObjectInterface; /** * References Doctrine connections and entity/document managers. @@ -47,8 +48,15 @@ protected function resetService($name): void } $manager = $this->container->get($name); + if ($manager instanceof LazyGhostObjectInterface) { + if (!$manager->resetLazyGhostObject()) { + throw new \LogicException(sprintf('Resetting a non-lazy manager service is not supported. Declare the "%s" service as lazy.', $name)); + } + + return; + } if (!$manager instanceof LazyLoadingInterface) { - throw new \LogicException('Resetting a non-lazy manager service is not supported. '.(interface_exists(LazyLoadingInterface::class) && class_exists(RuntimeInstantiator::class) ? sprintf('Declare the "%s" service as lazy.', $name) : 'Try running "composer require symfony/proxy-manager-bridge".')); + throw new \LogicException(sprintf('Resetting a non-lazy manager service is not supported. Declare the "%s" service as lazy.', $name)); } if ($manager instanceof GhostObjectInterface) { throw new \LogicException('Resetting a lazy-ghost-object manager service is not supported.'); diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php index 69b7239655944..85455d222194e 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php @@ -16,7 +16,6 @@ use PHPUnit\Framework\TestCase; use ProxyManager\Proxy\LazyLoadingInterface; use ProxyManagerBridgeFooClass; -use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -31,10 +30,8 @@ public function testCreateProxyServiceWithRuntimeInstantiator() { $builder = new ContainerBuilder(); - $builder->setProxyInstantiator(new RuntimeInstantiator()); - $builder->register('foo1', ProxyManagerBridgeFooClass::class)->setFile(__DIR__.'/Fixtures/includes/foo.php')->setPublic(true); - $builder->getDefinition('foo1')->setLazy(true); + $builder->getDefinition('foo1')->setLazy(true)->addTag('proxy', ['interface' => ProxyManagerBridgeFooClass::class]); $builder->compile(); diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php index 8bc017bb8df71..bef5e5062bb95 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\TestCase; use ProxyManager\Proxy\LazyLoadingInterface; -use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; @@ -62,14 +61,12 @@ private function dumpLazyServiceProjectServiceContainer() { $container = new ContainerBuilder(); - $container->register('foo', 'stdClass')->setPublic(true); - $container->getDefinition('foo')->setLazy(true); + $container->register('foo', \stdClass::class)->setPublic(true); + $container->getDefinition('foo')->setLazy(true)->addTag('proxy', ['interface' => \stdClass::class]); $container->compile(); $dumper = new PhpDumper($container); - $dumper->setProxyDumper(new ProxyDumper()); - return $dumper->dump(['class' => 'LazyServiceProjectServiceContainer']); } } diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index d141f99ba6ee5..1e4333d8ad96d 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -18,10 +18,10 @@ "require": { "php": ">=8.1", "friendsofphp/proxy-manager-lts": "^1.0.2", - "symfony/dependency-injection": "^5.4|^6.0" + "symfony/dependency-injection": "^6.2" }, "require-dev": { - "symfony/config": "^5.4|^6.0" + "symfony/config": "^6.1" }, "autoload": { "psr-4": { "Symfony\\Bridge\\ProxyManager\\": "" }, diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index b4c389ff68a99..0e80a238a6e30 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -21,7 +21,7 @@ "ext-xml": "*", "symfony/cache": "^5.4|^6.0", "symfony/config": "^6.1", - "symfony/dependency-injection": "^6.1", + "symfony/dependency-injection": "^6.2", "symfony/deprecation-contracts": "^2.1|^3", "symfony/error-handler": "^6.1", "symfony/event-dispatcher": "^5.4|^6.0", diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index fb8519ab9a04a..1788b1685573a 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -20,7 +20,7 @@ "composer-runtime-api": ">=2.1", "ext-xml": "*", "symfony/config": "^6.1", - "symfony/dependency-injection": "^6.1", + "symfony/dependency-injection": "^6.2", "symfony/event-dispatcher": "^5.4|^6.0", "symfony/http-kernel": "^6.2", "symfony/http-foundation": "^5.4|^6.0", diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index e72239311e298..bebf228f988c3 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -4,9 +4,11 @@ CHANGELOG 6.2 --- + * Use lazy-loading ghost object proxies out of the box * Add argument `&$asGhostObject` to LazyProxy's `DumperInterface` to allow using ghost objects for lazy loading services * Add `enum` env var processor * Add `shuffle` env var processor + * Deprecate `RealServiceInstantiator` 6.1 --- diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 799a2a48117d3..7664140bcf715 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -39,6 +39,7 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface; +use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\LazyServiceInstantiator; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -86,7 +87,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface private Compiler $compiler; private bool $trackResources; - private ?InstantiatorInterface $proxyInstantiator = null; + private InstantiatorInterface $proxyInstantiator; private ExpressionLanguage $expressionLanguage; /** @@ -994,7 +995,7 @@ private function createService(Definition $definition, array &$inlineServices, b trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); } - if (true === $tryProxy && $definition->isLazy() && !$tryProxy = !($proxy = $this->proxyInstantiator) || $proxy instanceof RealServiceInstantiator) { + if (true === $tryProxy && $definition->isLazy() && !$tryProxy = !($proxy = $this->proxyInstantiator ??= new LazyServiceInstantiator()) || $proxy instanceof RealServiceInstantiator) { $proxy = $proxy->instantiateProxy( $this, $definition, diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 435a28b6b89a2..fc9a0fdb77e3b 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -32,6 +32,7 @@ use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\ExpressionLanguage; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\LazyServiceDumper; use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; use Symfony\Component\DependencyInjection\Loader\FileLoader; use Symfony\Component\DependencyInjection\Parameter; @@ -89,6 +90,7 @@ class PhpDumper extends Dumper private string $serviceLocatorTag; private array $exportedVariables = []; private string $baseClass; + private string $class; private ProxyDumper $proxyDumper; /** @@ -154,6 +156,7 @@ public function dump(array $options = []): string|array $this->inlineFactories = $this->asFiles && $options['inline_factories_parameter'] && $this->container->hasParameter($options['inline_factories_parameter']) && $this->container->getParameter($options['inline_factories_parameter']); $this->inlineRequires = $options['inline_class_loader_parameter'] && ($this->container->hasParameter($options['inline_class_loader_parameter']) ? $this->container->getParameter($options['inline_class_loader_parameter']) : $options['debug']); $this->serviceLocatorTag = $options['service_locator_tag']; + $this->class = $options['class']; if (!str_starts_with($baseClass = $options['base_class'], '\\') && 'Container' !== $baseClass) { $baseClass = sprintf('%s\%s', $options['namespace'] ? '\\'.$options['namespace'] : '', $baseClass); @@ -401,7 +404,7 @@ class %s extends {$options['class']} */ private function getProxyDumper(): ProxyDumper { - return $this->proxyDumper ??= new NullDumper(); + return $this->proxyDumper ??= new LazyServiceDumper($this->class); } private function analyzeReferences() diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php new file mode 100644 index 0000000000000..363a8df9fc875 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\LazyProxy\Instantiator; + +use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\LazyServiceDumper; +use Symfony\Component\VarExporter\LazyGhostObjectInterface; +use Symfony\Component\VarExporter\LazyGhostObjectTrait; + +/** + * @author Nicolas Grekas + */ +final class LazyServiceInstantiator implements InstantiatorInterface +{ + /** + * {@inheritdoc} + */ + public function instantiateProxy(ContainerInterface $container, Definition $definition, string $id, callable $realInstantiator): object + { + $dumper = new LazyServiceDumper(); + + if ($dumper->useProxyManager($definition)) { + return (new RuntimeInstantiator())->instantiateProxy($container, $definition, $id, $realInstantiator); + } + + if (!class_exists($proxyClass = $dumper->getProxyClass($definition), false)) { + eval(sprintf('class %s extends %s implements %s { use %s; }', $proxyClass, $definition->getClass(), LazyGhostObjectInterface::class, LazyGhostObjectTrait::class)); + } + + return $proxyClass::createLazyGhostObject($realInstantiator); + } +} diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php index 38ccca525b1ef..c25673422630f 100644 --- a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php +++ b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php @@ -14,12 +14,16 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; +trigger_deprecation('symfony/dependency-injection', '6.2', 'The "%s" class is deprecated, use "%s" instead.', RealServiceInstantiator::class, LazyServiceInstantiator::class); + /** * {@inheritdoc} * * Noop proxy instantiator - produces the real service instead of a proxy instance. * * @author Marco Pivetta + * + * @deprecated since Symfony 6.2, use LazyServiceInstantiator instead. */ class RealServiceInstantiator implements InstantiatorInterface { diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php new file mode 100644 index 0000000000000..889026889bed9 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php @@ -0,0 +1,153 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\LazyProxy\PhpDumper; + +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\VarExporter\LazyGhostObjectInterface; +use Symfony\Component\VarExporter\LazyGhostObjectTrait; + +/** + * @author Nicolas Grekas + */ +final class LazyServiceDumper implements DumperInterface +{ + public function __construct( + private string $salt = '', + ) { + } + + /** + * {@inheritdoc} + */ + public function isProxyCandidate(Definition $definition, bool &$asGhostObject = null): bool + { + $asGhostObject = false; + + if ($definition->hasTag('proxy')) { + if (!$definition->isLazy()) { + throw new InvalidArgumentException(sprintf('Invalid definition for service of class "%s": setting the "proxy" tag on a service requires it to be "lazy".', $definition->getClass())); + } + + return true; + } + + if (!$definition->isLazy()) { + return false; + } + + if (!($class = $definition->getClass()) || !(class_exists($class) || interface_exists($class, false))) { + return false; + } + + $class = new \ReflectionClass($class); + + if ($class->isFinal()) { + throw new InvalidArgumentException(sprintf('Cannot make service of class "%s" lazy because the class is final.', $definition->getClass())); + } + + if ($asGhostObject = !$class->isAbstract() && !$class->isInterface() && (\stdClass::class === $class->name || !$class->isInternal())) { + while ($class = $class->getParentClass()) { + if (!$asGhostObject = \stdClass::class === $class->name || !$class->isInternal()) { + break; + } + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getProxyFactoryCode(Definition $definition, string $id, string $factoryCode): string + { + if ($dumper = $this->useProxyManager($definition)) { + return $dumper->getProxyFactoryCode($definition, $id, $factoryCode); + } + + $instantiation = 'return'; + + if ($definition->isShared()) { + $instantiation .= sprintf(' $this->%s[%s] =', $definition->isPublic() && !$definition->isPrivate() ? 'services' : 'privates', var_export($id, true)); + } + + $proxyClass = $this->getProxyClass($definition); + + if (preg_match('/^\$this->\w++\(\$proxy\)$/', $factoryCode)) { + $factoryCode = substr_replace($factoryCode, '(...)', -8); + } else { + $factoryCode = sprintf('function ($proxy) { return %s; }', $factoryCode); + } + + return <<createProxy('$proxyClass', function () { + return \\$proxyClass::createLazyGhostObject($factoryCode); + }); + } + + +EOF; + } + + /** + * {@inheritdoc} + */ + public function getProxyCode(Definition $definition): string + { + if ($dumper = $this->useProxyManager($definition)) { + return $dumper->getProxyCode($definition); + } + + $proxyClass = $this->getProxyClass($definition); + + return sprintf(<<getClass(), + LazyGhostObjectInterface::class, + LazyGhostObjectTrait::class + ); + } + + public function getProxyClass(Definition $definition): string + { + $class = (new \ReflectionClass($definition->getClass()))->name; + + return preg_replace('/^.*\\\\/', '', $class).'_'.substr(hash('sha256', $this->salt.'+'.$class), -7); + } + + public function useProxyManager(Definition $definition): ?ProxyDumper + { + if (!$this->isProxyCandidate($definition, $asGhostObject)) { + throw new InvalidArgumentException(sprintf('Cannot instantiate lazy proxy for service of class "%s".', $definition->getClass())); + } + + if ($asGhostObject) { + return null; + } + + if (!class_exists(ProxyDumper::class)) { + throw new LogicException('You cannot use virtual proxies for lazy services as the ProxyManager bridge is not installed. Try running "composer require symfony/proxy-manager-bridge".'); + } + + return new ProxyDumper($this->salt); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 980b393a2d4d7..d4d4af106f786 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -38,6 +38,7 @@ use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; +use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -369,9 +370,13 @@ public function testCreateService() $this->assertInstanceOf(\Bar\FooClass::class, $builder->get('foo2'), '->createService() replaces parameters in the file provided by the service definition'); } + /** + * @group legacy + */ public function testCreateProxyWithRealServiceInstantiator() { $builder = new ContainerBuilder(); + $builder->setProxyInstantiator(new RealServiceInstantiator()); $builder->register('foo1', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php'); $builder->getDefinition('foo1')->setLazy(true); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 78ee57aa4db0a..356f9f1a4ae5c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -36,6 +36,7 @@ use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Reference; @@ -312,7 +313,6 @@ public function testNonSharedLazyDumpAsFiles() ->setLazy(true); $container->compile(); $dumper = new PhpDumper($container); - $dumper->setProxyDumper(new \DummyProxyDumper()); $dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__, 'inline_factories_parameter' => false, 'inline_class_loader_parameter' => false]), true); if ('\\' === \DIRECTORY_SEPARATOR) { @@ -708,7 +708,10 @@ public function testNonSharedLazyDefinitionReferences(bool $asGhostObject) $container->compile(); $dumper = new PhpDumper($container); - $dumper->setProxyDumper(new \DummyProxyDumper($asGhostObject)); + + if (!$asGhostObject) { + $dumper->setProxyDumper(new \DummyProxyDumper()); + } $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_non_shared_lazy'.($asGhostObject ? '_ghost' : '').'.php', $dumper->dump()); } @@ -726,7 +729,6 @@ public function testNonSharedDuplicates() $container->compile(); $dumper = new PhpDumper($container); - $dumper->setProxyDumper(new \DummyProxyDumper()); $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_non_shared_duplicates.php', $dumper->dump()); } @@ -759,12 +761,12 @@ public function testCircularReferenceAllowanceForLazyServices() $container->compile(); $dumper = new PhpDumper($container); - $dumper->setProxyDumper(new \DummyProxyDumper()); $dumper->dump(); $this->addToAssertionCount(1); $dumper = new PhpDumper($container); + $dumper->setProxyDumper(new NullDumper()); $message = 'Circular reference detected for service "foo", path: "foo -> bar -> foo". Try running "composer require symfony/proxy-manager-bridge".'; $this->expectException(ServiceCircularReferenceException::class); @@ -785,7 +787,10 @@ public function testDedupLazyProxy(bool $asGhostObject) $container->compile(); $dumper = new PhpDumper($container); - $dumper->setProxyDumper(new \DummyProxyDumper($asGhostObject)); + + if (!$asGhostObject) { + $dumper->setProxyDumper(new \DummyProxyDumper()); + } $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_dedup_lazy'.($asGhostObject ? '_ghost' : '_proxy').'.php', $dumper->dump()); } @@ -1059,6 +1064,7 @@ public function testAlmostCircular($visibility) $container = include self::$fixturesPath.'/containers/container_almost_circular.php'; $container->compile(); $dumper = new PhpDumper($container); + $dumper->setProxyDumper(new NullDumper()); $container = 'Symfony_DI_PhpDumper_Test_Almost_Circular_'.ucfirst($visibility); $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_almost_circular_'.$visibility.'.php', $dumper->dump(['class' => $container])); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php index 03e4b7bd859ac..3add8dc3bd3d7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php @@ -84,14 +84,9 @@ public function callPassed() class DummyProxyDumper implements ProxyDumper { - public function __construct( - private bool $asGhostObject = false, - ) { - } - public function isProxyCandidate(Definition $definition, bool &$asGhostObject = null): bool { - $asGhostObject = $this->asGhostObject; + $asGhostObject = false; return $definition->isLazy(); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php index b6bac79ecdd71..ee9ea03988199 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php @@ -58,7 +58,11 @@ protected function hydrateProxy($proxy, $instance) */ protected function getBarService($lazyLoad = true) { - // lazy factory for stdClass + if (true === $lazyLoad) { + return $this->services['bar'] = $this->createProxy('stdClass_5a8a5eb', function () { + return \stdClass_5a8a5eb::createLazyGhostObject($this->getBarService(...)); + }); + } return $lazyLoad; } @@ -70,10 +74,17 @@ protected function getBarService($lazyLoad = true) */ protected function getFooService($lazyLoad = true) { - // lazy factory for stdClass + if (true === $lazyLoad) { + return $this->services['foo'] = $this->createProxy('stdClass_5a8a5eb', function () { + return \stdClass_5a8a5eb::createLazyGhostObject($this->getFooService(...)); + }); + } return $lazyLoad; } } -// proxy code for stdClass +class stdClass_5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyGhostObjectInterface +{ + use \Symfony\Component\VarExporter\LazyGhostObjectTrait; +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt index 7d5f90ae649b1..b25f408148836 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt @@ -23,7 +23,11 @@ class getNonSharedFooService extends ProjectServiceContainer return self::do($container); }; - // lazy factory for Bar\FooLazyClass + if (true === $lazyLoad) { + return $container->createProxy('FooLazyClass_f814e3a', function () use ($container) { + return \FooLazyClass_f814e3a::createLazyGhostObject(function ($proxy) use ($container) { return self::do($container, $proxy); }); + }); + } static $include = true; @@ -33,18 +37,21 @@ class getNonSharedFooService extends ProjectServiceContainer $include = false; } - return new \Bar\FooLazyClass(); + return $lazyLoad; } } - [Container%s/proxy.php] => set(\Container%s\ProjectServiceContainer::class, null); -require __DIR__.'/Container%s/proxy.php'; +require __DIR__.'/Container%s/FooLazyClass_f814e3a.php'; require __DIR__.'/Container%s/getNonSharedFooService.php'; $classes = []; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php index c902da1eca68b..da6014eceef12 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php @@ -64,7 +64,7 @@ protected function hydrateProxy($proxy, $instance) */ protected function getBarService() { - return $this->services['bar'] = $lazyLoad; + return $this->services['bar'] = new \stdClass((isset($this->factories['service_container']['foo']) ? $this->factories['service_container']['foo']() : $this->getFooService())); } /** @@ -76,10 +76,17 @@ protected function getFooService($lazyLoad = true) { $this->factories['service_container']['foo'] ??= $this->getFooService(...); - // lazy factory for stdClass + if (true === $lazyLoad) { + return $this->createProxy('stdClass_5a8a5eb', function () { + return \stdClass_5a8a5eb::createLazyGhostObject($this->getFooService(...)); + }); + } return $lazyLoad; } } -// proxy code for stdClass +class stdClass_5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyGhostObjectInterface +{ + use \Symfony\Component\VarExporter\LazyGhostObjectTrait; +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php b/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php index 4abbfdad3717f..6ff2f83c42af3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php @@ -20,6 +20,8 @@ * Tests for {@see \Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator}. * * @author Marco Pivetta + * + * @group legacy */ class RealServiceInstantiatorTest extends TestCase { diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 6732c47fb2d25..763e032b96d68 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -11,8 +11,6 @@ namespace Symfony\Component\HttpKernel; -use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; -use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; use Symfony\Component\Config\Builder\ConfigBuilderGenerator; use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\Loader\DelegatingLoader; @@ -683,9 +681,6 @@ protected function getContainerBuilder(): ContainerBuilder if ($this instanceof CompilerPassInterface) { $container->addCompilerPass($this, PassConfig::TYPE_BEFORE_OPTIMIZATION, -10000); } - if (class_exists(\ProxyManager\Configuration::class) && class_exists(RuntimeInstantiator::class)) { - $container->setProxyInstantiator(new RuntimeInstantiator()); - } return $container; } @@ -701,10 +696,6 @@ protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container // cache the container $dumper = new PhpDumper($container); - if (class_exists(\ProxyManager\Configuration::class) && class_exists(ProxyDumper::class)) { - $dumper->setProxyDumper(new ProxyDumper()); - } - $content = $dumper->dump([ 'class' => $class, 'base_class' => $baseClass, diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index f8e2ceb160820..30bc75c4e05f8 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -28,7 +28,7 @@ "symfony/config": "^6.1", "symfony/console": "^5.4|^6.0", "symfony/css-selector": "^5.4|^6.0", - "symfony/dependency-injection": "^6.1", + "symfony/dependency-injection": "^6.2", "symfony/dom-crawler": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", @@ -51,7 +51,7 @@ "symfony/config": "<6.1", "symfony/console": "<5.4", "symfony/form": "<5.4", - "symfony/dependency-injection": "<6.1", + "symfony/dependency-injection": "<6.2", "symfony/doctrine-bridge": "<5.4", "symfony/http-client": "<5.4", "symfony/mailer": "<5.4",