8000 [DependencyInjection] Add `#[AutowireIterator]` attribute and improve… · symfony/symfony@ed3efda · GitHub
[go: up one dir, main page]

Skip to content

Commit ed3efda

Browse files
[DependencyInjection] Add #[AutowireIterator] attribute and improve #[AutowireLocator]
1 parent 7be1c03 commit ed3efda

File tree

8 files changed

+112
-45
lines changed

8 files changed

+112
-45
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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+
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
15+
use Symfony\Component\DependencyInjection\ContainerInterface;
16+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
17+
use Symfony\Component\DependencyInjection\TypedReference;
18+
use Symfony\Contracts\Service\Attribute\SubscribedService;
19+
use Symfony\Contracts\Service\ServiceSubscriberInterface;
20+
21+
#[\Attribute(\Attribute::TARGET_PARAMETER)]
22+
class AutowireIterator extends Autowire
23+
{
24+
/**
25+
* @see ServiceSubscriberInterface::getSubscribedServices()
26+
*
27+
* @param array<string|SubscribedService>|string $services An explicit list of services or a tag name
28+
*/
29+
public function __construct(
30+
array|string $services,
31+
string $indexAttribute = null,
32+
string $defaultIndexMethod = null,
33+
string $defaultPriorityMethod = null,
34+
string|array $exclude = [],
35+
bool $excludeSelf = true,
36+
) {
37+
if (\is_string($services)) {
38+
parent::__construct(new TaggedIteratorArgument($services, $indexAttribute, $defaultIndexMethod, false, $defaultPriorityMethod, (array) $exclude, $excludeSelf));
39+
40+
return;
41+
}
42+
43+
$references = [];
44+
45+
foreach ($services as $key => $type) {
46+
$attributes = [];
47+
48+
if ($type instanceof SubscribedService) {
49+
$key = $type->key;
50+
$attributes = $type->attributes;
51+
$type = ($type->nullable ? '?' : '').($type->type ?? throw new InvalidArgumentException(sprintf('When "%s" is used, a type must be set.', SubscribedService::class)));
52+
}
53+
54+
if (!\is_string($type) || !preg_match('/(?(DEFINE)(?<cn>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+))(?(DEFINE)(?<fqcn>(?&cn)(?:\\\\(?&cn))*+))^\??(?&fqcn)(?:(?:\|(?&fqcn))*+|(?:&(?&fqcn))*+)$/', $type)) {
55+
throw new InvalidArgumentException(sprintf('"%s" is not a PHP type for key "%s".', \is_string($type) ? $type : get_debug_type($type), $key));
56+
}
57+
if ($optionalBehavior = '?' === $type[0]) {
58+
$type = substr($type, 1);
59+
$optionalBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
60+
}
61+
if (\is_int($name = $key)) {
62+
$key = $type;
63+
$name = null;
64+
}
65+
66+
$references[$key] = new TypedReference($type, $type, $optionalBehavior ?: ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $name, $attributes);
67+
}
68+
69+
parent::__construct($references);
70+
}
71+
}

src/Symfony/Component/DependencyInjection/Attribute/AutowireLocator.php

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,32 @@
1212
namespace Symfony\Component\DependencyInjection\Attribute;
1313

1414
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
15-
use Symfony\Component\DependencyInjection\ContainerInterface;
16-
use Symfony\Component\DependencyInjection\Reference;
15+
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
16+
use Symfony\Contracts\Service\Attribute\SubscribedService;
17+
use Symfony\Contracts\Service\ServiceSubscriberInterface;
1718

1819
#[\Attribute(\Attribute::TARGET_PARAMETER)]
1920
class AutowireLocator extends Autowire
2021
{
21-
public function __construct(string ...$serviceIds)
22-
{
23-
$values = [];
24-
25-
foreach ($serviceIds as $key => $serviceId) {
26-
if ($nullable = str_starts_with($serviceId, '?')) {
27-
$serviceId = substr($serviceId, 1);
28-
}
29-
30-
if (is_numeric($key)) {
31-
$key = $serviceId;
32-
}
33-
34-
$values[$key] = new Reference(
35-
$serviceId,
36-
$nullable ? ContainerInterface::IGNORE_ON_INVALID_REFERENCE : ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE,
37-
);
22+
/**
23+
* @see ServiceSubscriberInterface::getSubscribedServices()
24+
*
25+
* @param array<string|SubscribedService>|string $services An explicit list of services or a tag name
26+
*/
27+
public function __construct(
28+
array|string $services,
29+
string $indexAttribute = null,
30+
string $defaultIndexMethod = null,
31+
string $defaultPriorityMethod = null,
32+
string|array $exclude = [],
33+
bool $excludeSelf = true,
34+
) {
35+
$iterator = (new AutowireIterator($services, $indexAttribute, $defaultIndexMethod, $defaultPriorityMethod, (array) $exclude, $excludeSelf))->value;
36+
37+
if ($iterator instanceof TaggedIteratorArgument) {
38+
$iterator = new TaggedIteratorArgument($iterator->getTag(), $iterator->getIndexAttribute(), $iterator->getDefaultIndexMethod(), true, $iterator->getDefaultPriorityMethod(), $iterator->getExclude(), $iterator->excludeSelf());
3839
}
3940

40-
parent::__construct(new ServiceLocatorArgument($values));
41+
parent::__construct(new ServiceLocatorArgument($iterator));
4142
}
4243
}

src/Symfony/Component/DependencyInjection/Attribute/TaggedIterator.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Attribute;
1313

14-
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
15-
1614
#[\Attribute(\Attribute::TARGET_PARAMETER)]
17-
class TaggedIterator extends Autowire
15+
class TaggedIterator extends AutowireIterator
1816
{
1917
public function __construct(
2018
public string $tag,
@@ -24,6 +22,6 @@ public function __construct(
2422
public string|array $exclude = [],
2523
public bool $excludeSelf = true,
2624
) {
27-
parent::__construct(new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, false, $defaultPriorityMethod, (array) $exclude, $excludeSelf));
25+
parent::__construct($tag, $indexAttribute, $defaultIndexMethod, $defaultPriorityMethod, $exclude, $excludeSelf);
2826
}
2927
}

src/Symfony/Component/DependencyInjection/Attribute/TaggedLocator.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,8 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Attribute;
1313

14-
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
15-
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
16-
1714
#[\Attribute(\Attribute::TARGET_PARAMETER)]
18-
class TaggedLocator extends Autowire
15+
class TaggedLocator extends AutowireLocator
1916
{
2017
public function __construct(
2118
public string $tag,
@@ -25,6 +22,6 @@ public function __construct(
2522
public string|array $exclude = [],
2623
public bool $excludeSelf = true,
2724
) {
28-
parent::__construct(new ServiceLocatorArgument(new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, true, $defaultPriorityMethod, (array) $exclude, $excludeSelf)));
25+
parent::__construct($tag, $indexAttribute, $defaultIndexMethod, $defaultPriorityMethod, $exclude, $excludeSelf);
2926
}
3027
}

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ CHANGELOG
77
* Allow using `#[Target]` with no arguments to state that a parameter must match a named autowiring alias
88
* Deprecate `ContainerAwareInterface` and `ContainerAwareTrait`, use dependency injection instead
99
* Add `defined` env var processor that returns `true` for defined and neither null nor empty env vars
10-
* Add `#[AutowireLocator]` attribute
10+
* Add `#[AutowireLocator]` and `#[AutowireIterator]` attributes
1111

1212
6.3
1313
---

src/Symfony/Component/DependencyInjection/Tests/Attribute/AutowireLocatorTest.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,33 @@
1515
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1616
use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;
1717
use Symfony\Component\DependencyInjection\ContainerInterface;
18-
use Symfony\Component\DependencyInjection\Reference;
18+
use Symfony\Component\DependencyInjection\TypedReference;
1919

2020
class AutowireLocatorTest extends TestCase
2121
{
2222
public function testSimpleLocator()
2323
{
24-
$locator = new AutowireLocator('foo', 'bar');
24+
$locator = new AutowireLocator(['foo', 'bar']);
2525

2626
$this->assertEquals(
27-
new ServiceLocatorArgument(['foo' => new Reference('foo'), 'bar' => new Reference('bar')]),
27+
new ServiceLocatorArgument(['foo' => new TypedReference('foo', 'foo'), 'bar' => new TypedReference('bar', 'bar')]),
2828
$locator->value,
2929
);
3030
}
3131

3232
public function testComplexLocator()
3333
{
34-
$< F438 /span>locator = new AutowireLocator(
34+
$locator = new AutowireLocator([
3535
'?qux',
36-
foo: 'bar',
37-
bar: '?baz',
38-
);
36+
'foo' => 'bar',
37+
'bar' => '?baz',
38+
]);
3939

4040
$this->assertEquals(
4141
new ServiceLocatorArgument([
42-
'qux' => new Reference('qux', ContainerInterface::IGNORE_ON_INVALID_REFERENCE),
43-
'foo' => new Reference('bar'),
44-
'bar' => new Reference('baz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE),
42+
'qux' => new TypedReference('qux', 'qux', ContainerInterface::IGNORE_ON_INVALID_REFERENCE),
43+
'foo' => new TypedReference('bar', 'bar', name: 'foo'),
44+
'bar' => new TypedReference('baz', 'baz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE, 'bar'),
4545
]),
4646
$locator->value,
4747
);

src/Symfony/Component/DependencyInjection/Tests/Fixtures/AutowireLocatorConsumer.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
final class AutowireLocatorConsumer
1818
{
1919
public function __construct(
20-
#[AutowireLocator(
20+
#[AutowireLocator([
2121
BarTagClass::class,
22-
with_key: FooTagClass::class,
23-
nullable: '?invalid',
24-
)]
22+
'with_key' => FooTagClass::class,
23+
'nullable' => '?invalid',
24+
])]
2525
public readonly ContainerInterface $locator,
2626
) {
2727
}

src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ class WithTaggedIteratorAndTaggedLocator
676676
public function fooAction(
677677
#[TaggedIterator('foobar')] iterable $iterator,
678678
#[TaggedLocator('foobar')] ServiceLocator $locator,
679-
#[AutowireLocator('bar', 'baz')] ContainerInterface $container,
679+
#[AutowireLocator(['bar', 'baz'])] ContainerInterface $container,
680680
) {
681681
}
682682
}

0 commit comments

Comments
 (0)
0