Skip to content

Commit

Permalink
feature #36389 [DI] allow decorators to reference their decorated ser…
Browse files Browse the repository at this point in the history
…vice using the special `.inner` id (nicolas-grekas)

This PR was merged into the 5.1-dev branch.

Discussion
----------

[DI] allow decorators to reference their decorated service using the special `.inner` id

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | -
| License       | MIT
| Doc PR        | -

Right now, when one wants to decorate a service, one needs to reference the decorated service using `foo.inner`, where `foo` is the id of the decorating service.

There are two issues with this IMHO:
- it's weird to have to repeat the name of declaring service inside of it;
- it doesn't play well with child definitions.

In this PR, I propose to use `.inner` to fix both issues.

Before:
```yaml
services:
  decorating_service:
    arguments: [@decorating_service.inner]
    decorates: decorated_service
```

After:
```yaml
services:
  decorating_service:
    arguments: [@.inner]
    decorates: decorated_service
```

Commits
-------

7b6f767 [DI] allow decorators to reference their decorated service using the special `.inner` id
  • Loading branch information
fabpot committed Apr 12, 2020
2 parents c5bf92d + 7b6f767 commit 9a6695c
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Expand Up @@ -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
Expand Down
Expand Up @@ -24,8 +24,15 @@
* @author Fabien Potencier <fabien@symfony.com>
* @author Diego Saint Esteben <diego@saintesteben.me>
*/
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();
Expand All @@ -49,6 +56,10 @@ public function process(ContainerBuilder $container)
if (!$renamedId) {
$renamedId = $id.'.inner';
}

$this->currentId = $renamedId;
$this->processValue($definition);

$definition->innerServiceId = $renamedId;
$definition->decorationOnInvalid = $invalidBehavior;

Expand Down Expand Up @@ -96,4 +107,13 @@ public function process(ContainerBuilder $container)
$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);
}
}
Expand Up @@ -16,6 +16,7 @@
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
{
Expand Down Expand Up @@ -242,6 +243,20 @@ public function testProcessLeavesServiceLocatorTagOnOriginalDefinition()
$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();
Expand Down

0 comments on commit 9a6695c

Please sign in to comment.