Skip to content

Commit 3987b19

Browse files
committed
Add Inner attribute
1 parent 002e60e commit 3987b19

File tree

10 files changed

+135
-145
lines changed

10 files changed

+135
-145
lines changed

src/Symfony/Component/DependencyInjection/Attribute/Service.php renamed to src/Symfony/Component/DependencyInjection/Attribute/AsDecorator.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,11 @@
1313

1414
use Symfony\Component\DependencyInjection\ContainerInterface;
1515

16-
/**
17-
* An attribute to tell how a service should be configured.
18-
*/
19-
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
20-
class Service
16+
#[\Attribute(\Attribute::TARGET_CLASS)]
17+
class AsDecorator
2118
{
2219
public function __construct(
23-
public ?string $decorates = null,
24-
public int|string|null $decorationArgumentKey = null,
25-
public ?string $decorationInnerName = null,
20+
public string $decorates,
2621
public int $decorationPriority = 0,
2722
public int $decorationOnInvalid = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE,
2823
) {
Lines changed: 17 additions & 0 deletions
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 <fabien@symfony.com>
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 Inner
16+
{
17+
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
use Symfony\Component\Config\Resource\ClassExistenceResource;
1515
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1616
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
17+
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
1718
use Symfony\Component\DependencyInjection\Attribute\Autowire;
19+
use Symfony\Component\DependencyInjection\Attribute\Inner;
1820
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
1921
use Symfony\Component\DependencyInjection\Attribute\TaggedLocator;
2022
use Symfony\Component\DependencyInjection\Attribute\Target;
@@ -271,6 +273,12 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
271273

272274
break;
273275
}
276+
277+
if (Inner::class === $attribute->getName()) {
278+
$arguments[$index] = new Reference( "$class.inner", ContainerInterface::NULL_ON_INVALID_REFERENCE);
279+
280+
break;
281+
}
274282
}
275283

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

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

Lines changed: 1 addition & 1 deletion
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 RegisterDecoratorAttributesPass(),
4647
new AttributeAutoconfigurationPass(),
4748
new ResolveInstanceofConditionalsPass(),
4849
new RegisterEnvVarProcessorsPass(),
@@ -53,7 +54,6 @@ public function __construct()
5354
$this->optimizationPasses = [[
5455
new AutoAliasServicePass(),
5556
new ValidateEnvPlaceholdersPass(),
56-
new ServicePass(),
5757
new ResolveDecoratorStackPass(),
5858
new ResolveChildDefinitionsPass(),
5959
new RegisterServiceSubscribersPass(),
Lines changed: 49 additions & 0 deletions
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 <fabien@symfony.com>
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 autoconfigured
20+
* and don't have the "container.ignore_attributes" tag.
21+
*/
22+
final class RegisterDecoratorAttributesPass 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+
public function accept(Definition $definition): bool
37+
{
38+
return !$definition->hasTag('container.ignore_attributes');
39+
}
40+
41+
public 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->decorationPriority, $attribute->decorationOnInvalid);
47+
}
48+
}
49+
}

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

Lines changed: 0 additions & 52 deletions
This file was deleted.

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
1919
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
2020
use Symfony\Component\DependencyInjection\Compiler\DecoratorServicePass;
21+
use Symfony\Component\DependencyInjection\Compiler\RegisterDecoratorAttributesPass;
2122
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
2223
use Symfony\Component\DependencyInjection\ContainerBuilder;
2324
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -1164,4 +1165,25 @@ public function testAutowireAttribute()
11641165
$this->assertSame('@bar', $service->escapedRawValue);
11651166
$this->assertNull($service->invalid);
11661167
}
1168+
1169+
public function testDecoratorAttribute()
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 RegisterDecoratorAttributesPass())->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/Compiler/ServicePassTest.php

Lines changed: 0 additions & 52 deletions
This file was deleted.

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

Lines changed: 35 additions & 0 deletions
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\Inner;
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, decorationPriority: 10)]
66+
class AsDecoratorBar10 implements AsDecoratorInterface
67+
{
68+
public function __construct(string $arg1, #[Inner] AsDecoratorInterface $inner)
69+
{
70+
}
71+
}
72+
73+
#[AsDecorator(decorates: AsDecoratorFoo::class, decorationPriority: 20)]
74+
class AsDecoratorBar20 implements AsDecoratorInterface
75+
{
76+
public function __construct(string $arg1, #[Inner] AsDecoratorInterface $inner)
77+
{
78+
}
79+
}
80+
81+
#[AsDecorator(decorates: \NonExistent::class, decorationOnInvalid: ContainerInterface::NULL_ON_INVALID_REFERENCE)]
82+
class AsDecoratorBaz implements AsDecoratorInterface
83+
{
84+
public function __construct(#[Inner] AsDecoratorInterface $inner = null)
85+
{
86+
}
87+
}

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

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)