From 7b6f767fbedb1162af805555a6081026243b48a2 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 8 Apr 2020 17:02:25 +0200 Subject: [PATCH] [DI] allow decorators to reference their decorated service using the special `.inner` id --- .../DependencyInjection/CHANGELOG.md | 1 + .../Compiler/DecoratorServicePass.php | 22 ++++++++++++++++++- .../Compiler/DecoratorServicePassTest.php | 15 +++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 14c4104d89..104fdd56b7 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 5.1.0 ----- + * allow decorators to reference their decorated service using the special `.inner` id * added support to autowire public typed properties in php 7.4 * added support for defining method calls, a configurator, and property setters in `InlineServiceConfigurator` * added possibility to define abstract service arguments diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php index 7e76064ce6..55f87b04b9 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php @@ -24,8 +24,15 @@ use Symfony\Component\DependencyInjection\Reference; * @author Fabien Potencier * @author Diego Saint Esteben */ -class DecoratorServicePass implements CompilerPassInterface +class DecoratorServicePass extends AbstractRecursivePass { + private $innerId = '.inner'; + + public function __construct(?string $innerId = '.inner') + { + $this->innerId = $innerId; + } + public function process(ContainerBuilder $container) { $definitions = new \SplPriorityQueue(); @@ -49,6 +56,10 @@ class DecoratorServicePass implements CompilerPassInterface if (!$renamedId) { $renamedId = $id.'.inner'; } + + $this->currentId = $renamedId; + $this->processValue($definition); + $definition->innerServiceId = $renamedId; $definition->decorationOnInvalid = $invalidBehavior; @@ -96,4 +107,13 @@ class DecoratorServicePass implements CompilerPassInterface $container->setAlias($inner, $id)->setPublic($public)->setPrivate($private); } } + + protected function processValue($value, bool $isRoot = false) + { + if ($value instanceof Reference && $this->innerId === (string) $value) { + return new Reference($this->currentId, $value->getInvalidBehavior()); + } + + return parent::processValue($value, $isRoot); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php index ed111d6d2c..bc9ff77b18 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Compiler\DecoratorServicePass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; class DecoratorServicePassTest extends TestCase { @@ -242,6 +243,20 @@ class DecoratorServicePassTest extends TestCase $this->assertEquals(['bar' => ['attr' => 'baz'], 'foobar' => ['attr' => 'bar']], $container->getDefinition('baz')->getTags()); } + public function testGenericInnerReference() + { + $container = new ContainerBuilder(); + $container->register('foo'); + + $container->register('bar') + ->setDecoratedService('foo') + ->setProperty('prop', new Reference('.inner')); + + $this->process($container); + + $this->assertEquals(['prop' => new Reference('bar.inner')], $container->getDefinition('bar')->getProperties()); + } + protected function process(ContainerBuilder $container) { $pass = new DecoratorServicePass();