Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 84d35a2

Browse files
feature #45834 [DependencyInjection] add AsDecorator class attribute and InnerService parameter attribute (Jean-Beru)
This PR was squashed before being merged into the 6.1 branch. Discussion ---------- [DependencyInjection] add AsDecorator class attribute and InnerService parameter attribute | Q | A | ------------- | --- | Branch? | 6.1 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | | License | MIT | Doc PR | WIP The aim f this PR is to allow decorator declaration with PHP8 attributes by using `AsDecorator` attribute on class and, optionally, `InnerService` parameter attribute to inject decorated service. To use it : ```php interface FooInterface { public function myMethod(): string; } final class Foo implements FooInterface { public function myMethod(): string { return 'foo'; } } #[AsDecorator( decorates: Foo::class, priority: 5, onInvalid: ContainerInterface::NULL_ON_INVALID_REFERENCE, )] final class Bar implements FooInterface { private string $arg1; private ?FooInterface $foo; public function __construct(string $arg1, #[InnerService] FooInterface $foo = null) { $this->arg1 = $arg1; $this->foo = $foo; } public function myMethod(): string { if (null === $this->foo) { return 'bar'; } return $this->foo->myMethod().' bar '; } } ``` Commits ------- d8a4680 [DependencyInjection] add AsDecorator class attribute and InnerService parameter attribute
2 parents 55f6d9d + d8a4680 commit 84d35a2

File tree

7 files changed

+156
-0
lines changed

7 files changed

+156
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Attribute;
13+
14+
use Symfony\Component\DependencyInjection\ContainerInterface;
15+
16+
#[\Attribute(\Attribute::TARGET_CLASS)]
17+
class AsDecorator
18+
{
19+
public function __construct(
20+
public string $decorates,
21+
public int $priority = 0,
22+
public int $onInvalid = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE,
23+
) {
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Attribute;
13+
14+
#[\Attribute(\Attribute::TARGET_PARAMETER)]
15+
class InnerService
16+
{
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Definition;
17+
18+
/**
19+
* Reads #[AsDecorator] attributes on definitions that are autowired
20+
* and don't have the "container.ignore_attributes" tag.
21+
*/
22+
final class AutowireAsDecoratorPass implements CompilerPassInterface
23+
{
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
public function process(ContainerBuilder $container)
28+
{
29+
foreach ($container->getDefinitions() as $definition) {
30+
if ($this->accept($definition) && $reflectionClass = $container->getReflectionClass($definition->getClass(), false)) {
31+
$this->processClass($definition, $reflectionClass);
32+
}
33+
}
34+
}
35+
36+
private function accept(Definition $definition): bool
37+
{
38+
return !$definition->hasTag('container.ignore_attributes') && $definition->isAutowired();
39+
}
40+
41+
private function processClass(Definition $definition, \ReflectionClass $reflectionClass)
42+
{
43+
foreach ($reflectionClass->getAttributes(AsDecorator::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
44+
$attribute = $attribute->newInstance();
45+
46+
$definition->setDecoratedService($attribute->decorates, null, $attribute->priority, $attribute->onInvalid);
47+
}
48+
}
49+
}

src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1616
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
1717
use Symfony\Component\DependencyInjection\Attribute\Autowire;
18+
use Symfony\Component\DependencyInjection\Attribute\InnerService;
1819
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
1920
use Symfony\Component\DependencyInjection\Attribute\TaggedLocator;
2021
use Symfony\Component\DependencyInjection\Attribute\Target;
@@ -271,6 +272,12 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
271272

272273
break;
273274
}
275+
276+
if (InnerService::class === $attribute->getName()) {
277+
$arguments[$index] = new Reference($this->currentId.'.inner', ContainerInterface::NULL_ON_INVALID_REFERENCE);
278+
279+
break;
280+
}
274281
}
275282

276283
if ('' !== ($arguments[$index] ?? '')) {

src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public function __construct()
4343
100 => [
4444
new ResolveClassPass(),
4545
new RegisterAutoconfigureAttributesPass(),
46+
new AutowireAsDecoratorPass(),
4647
new AttributeAutoconfigurationPass(),
4748
new ResolveInstanceofConditionalsPass(),
4849
new RegisterEnvVarProcessorsPass(),

src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php

+22
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Psr\Log\LoggerInterface;
1616
use Psr\Log\NullLogger;
1717
use Symfony\Component\Config\FileLocator;
18+
use Symfony\Component\DependencyInjection\Compiler\AutowireAsDecoratorPass;
1819
use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
1920
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
2021
use Symfony\Component\DependencyInjection\Compiler\DecoratorServicePass;
@@ -1164,4 +1165,25 @@ public function testAutowireAttribute()
11641165
$this->assertSame('@bar', $service->escapedRawValue);
11651166
$this->assertNull($service->invalid);
11661167
}
1168+
1169+
public function testAsDecoratorAttribute()
1170+
{
1171+
$container = new ContainerBuilder();
1172+
1173+
$container->register(AsDecoratorFoo::class);
1174+
$container->register(AsDecoratorBar10::class)->setAutowired(true)->setArgument(0, 'arg1');
1175+
$container->register(AsDecoratorBar20::class)->setAutowired(true)->setArgument(0, 'arg1');
1176+
$container->register(AsDecoratorBaz::class)->setAutowired(true);
1177+
1178+
(new ResolveClassPass())->process($container);
1179+
(new AutowireAsDecoratorPass())->process($container);
1180+
(new DecoratorServicePass())->process($container);
1181+
(new AutowirePass())->process($container);
1182+
1183+
$this->assertSame(AsDecoratorBar10::class.'.inner', (string) $container->getDefinition(AsDecoratorBar10::class)->getArgument(1));
1184+
1185+
$this->assertSame(AsDecoratorBar20::class.'.inner', (string) $container->getDefinition(AsDecoratorBar20::class)->getArgument(1));
1186+
$this->assertSame(AsDecoratorBaz::class.'.inner', (string) $container->getDefinition(AsDecoratorBaz::class)->getArgument(0));
1187+
$this->assertSame(2, $container->getDefinition(AsDecoratorBaz::class)->getArgument(0)->getInvalidBehavior());
1188+
}
11671189
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_80.php

+35
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
44

5+
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
56
use Symfony\Component\DependencyInjection\Attribute\Autowire;
7+
use Symfony\Component\DependencyInjection\Attribute\InnerService;
8+
use Symfony\Component\DependencyInjection\ContainerInterface;
69
use Symfony\Contracts\Service\Attribute\Required;
710

811
class AutowireSetter
@@ -50,3 +53,35 @@ public function __construct(
5053
) {
5154
}
5255
}
56+
57+
interface AsDecoratorInterface
58+
{
59+
}
60+
61+
class AsDecoratorFoo implements AsDecoratorInterface
62+
{
63+
}
64+
65+
#[AsDecorator(decorates: AsDecoratorFoo::class, priority: 10)]
66+
class AsDecoratorBar10 implements AsDecoratorInterface
67+
{
68+
public function __construct(string $arg1, #[InnerService] AsDecoratorInterface $inner)
69+
{
70+
}
71+
}
72+
73+
#[AsDecorator(decorates: AsDecoratorFoo::class, priority: 20)]
74+
class AsDecoratorBar20 implements AsDecoratorInterface
75+
{
76+
public function __construct(string $arg1, #[InnerService] AsDecoratorInterface $inner)
77+
{
78+
}
79+
}
80+
81+
#[AsDecorator(decorates: \NonExistent::class, onInvalid: ContainerInterface::NULL_ON_INVALID_REFERENCE)]
82+
class AsDecoratorBaz implements AsDecoratorInterface
83+
{
84+
public function __construct(#[InnerService] AsDecoratorInterface $inner = null)
85+
{
86+
}
87+
}

0 commit comments

Comments
 (0)