10000 [DependencyInjection] Auto exclude referencing service in `TaggedIter… · symfony/symfony@0cbb176 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0cbb176

Browse files
committed
[DependencyInjection] Auto exclude referencing service in TaggedIteratorArgument
1 parent e7482d7 commit 0cbb176

File tree

8 files changed

+61
-14
lines changed

8 files changed

+61
-14
lines changed

src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,18 @@ class TaggedIteratorArgument extends IteratorArgument
2424
private ?string $defaultPriorityMethod;
2525
private bool $needsIndexes;
2626
private array $exclude;
27+
private bool $autoExcludeReferencingService = true;
2728

2829
/**
29-
* @param string $tag The name of the tag identifying the target services
30-
* @param string|null $indexAttribute The name of the attribute that defines the key referencing each service in the tagged collection
31-
* @param string|null $defaultIndexMethod The static method that should be called to get each service's key when their tag doesn't define the previous attribute
32-
* @param bool $needsIndexes Whether indexes are required and should be generated when computing the map
33-
* @param string|null $defaultPriorityMethod The static method that should be called to get each service's priority when their tag doesn't define the "priority" attribute
34-
* @param array $exclude Services to exclude from the iterator
30+
* @param string $tag The name of the tag identifying the target services
31+
* @param string|null $indexAttribute The name of the attribute that defines the key referencing each service in the tagged collection
32+
* @param string|null $defaultIndexMethod The static method that should be called to get each service's key when their tag doesn't define the previous attribute
33+
* @param bool $needsIndexes Whether indexes are required and should be generated when computing the map
34+
* @param string|null $defaultPriorityMethod The static method that should be called to get each service's priority when their tag doesn't define the "priority" attribute
35+
* @param array $exclude Services to exclude from the iterator
36+
* @param array $autoExcludeReferencingService Whether to automatically exclude the referencing service from the iterator
3537
*/
36-
public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false, string $defaultPriorityMethod = null, array $exclude = [])
38+
public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false, string $defaultPriorityMethod = null, array $exclude = [], bool $autoExcludeReferencingService = true)
3739
{
3840
parent::__construct([]);
3941

@@ -47,6 +49,7 @@ public function __construct(string $tag, string $indexAttribute = null, string $
4749
$this->needsIndexes = $needsIndexes;
4850
$this->defaultPriorityMethod = $defaultPriorityMethod ?: ($indexAttribute ? 'getDefault'.str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $indexAttribute))).'Priority' : null);
4951
$this->exclude = $exclude;
52+
$this->autoExcludeReferencingService = $autoExcludeReferencingService;
5053
}
5154

5255
public function getTag()
@@ -78,4 +81,9 @@ public function getExclude(): array
7881
{
7982
return $this->exclude;
8083
}
84+
85+
public function autoExcludeReferencingService(): bool
86+
{
87+
return $this->autoExcludeReferencingService;
88+
}
8189
}

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ CHANGELOG
77
* Add options `inline_factories` and `inline_class_loader` to `PhpDumper::dump()`
88
* Deprecate `PhpDumper` options `inline_factories_parameter` and `inline_class_loader_parameter`
99
* Add `RemoveBuildParametersPass`, which removes parameters starting with a dot during compilation
10+
* Add `autoExcludeReferencingService` parameter to `TaggedIteratorArgument` with default value to `true`
11+
to control whether the referencing service should be automatically excluded from the iterator if it holds the tag
1012

1113
6.2
1214
---

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,16 @@ trait PriorityTaggedServiceTrait
3737
*
3838
* @return Reference[]
3939
*/
40-
private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagName, ContainerBuilder $container): array
40+
private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagName, ContainerBuilder $container, array $exclude = []): array
4141
{
42-
$exclude = [];
4342
$indexAttribute = $defaultIndexMethod = $needsIndexes = $defaultPriorityMethod = null;
4443

4544
if ($tagName instanceof TaggedIteratorArgument) {
4645
$indexAttribute = $tagName->getIndexAttribute();
4746
$defaultIndexMethod = $tagName->getDefaultIndexMethod();
4847
$needsIndexes = $tagName->needsIndexes();
4948
$defaultPriorityMethod = $tagName->getDefaultPriorityMethod() ?? 'getDefaultPriority';
50-
$exclude = $tagName->getExclude();
49+
$exclude = array_merge($exclude, $tagName->getExclude());
5150
$tagName = $tagName->getTag();
5251
}
5352

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
2828
return parent::processValue($value, $isRoot);
2929
}
3030

31-
$value->setValues($this->findAndSortTaggedServices($value, $this->container));
31+
$exclude = $value->getExclude();
32+
if ($value->autoExcludeReferencingService() && !\in_array($this->currentId, $exclude, true) && $this->container->getDefinition($this->currentId)->hasTag($value->getTag())) {
33+
$exclude[] = $this->currentId;
34+
}
35+
36+
$value->setValues($this->findAndSortTaggedServices($value, $this->container, $exclude));
3237

3338
return $value;
3439
}

src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ private function getArgumentsAsPhp(\DOMElement $node, string $name, string $file
545545
$excludes = [$arg->getAttribute('exclude')];
546546
}
547547

548-
$arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag'), $arg->getAttribute('index-by') ?: null, $arg->getAttribute('default-index-method') ?: null, $forLocator, $arg->getAttribute('default-priority-method') ?: null, $excludes);
548+
$arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag'), $arg->getAttribute('index-by') ?: null, $arg->getAttribute('default-index-method') ?: null, $forLocator, $arg->getAttribute('default-priority-method') ?: null, $excludes, $arg->getAttribute('auto-exclude-referencing-service') ?: true);
549549

550550
if ($forLocator) {
551551
$arguments[$key] = new ServiceLocatorArgument($arguments[$key]);

src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -820,11 +820,11 @@ private function resolveServices(mixed $value, string $file, bool $isParameter =
820820
$forLocator = 'tagged_locator' === $value->getTag();
821821

822822
if (\is_array($argument) && isset($argument['tag']) && $argument['tag']) {
823-
if ($diff = array_diff(array_keys($argument), $supportedKeys = ['tag', 'index_by', 'default_index_method', 'default_priority_method', 'exclude'])) {
823+
if ($diff = array_diff(array_keys($argument), $supportedKeys = ['tag', 'index_by', 'default_index_method', 'default_priority_method', 'exclude', 'auto_exclude_referencing_service'])) {
824824
throw new InvalidArgumentException(sprintf('"!%s" tag contains unsupported key "%s"; supported ones are "%s".', $value->getTag(), implode('", "', $diff), implode('", "', $supportedKeys)));
825825
}
826826

827-
$argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator, $argument['default_priority_method'] ?? null, (array) ($argument['exclude'] ?? null));
827+
$argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator, $argument['default_priority_method'] ?? null, (array) ($argument['exclude'] ?? null), $argument['auto_exclude_referencing_service'] ?? true);
828828
} elseif (\is_string($argument) && $argument) {
829829
$argument = new TaggedIteratorArgument($argument, null, null, $forLocator);
830830
} else {

src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@
302302
<xsd:attribute name="default-index-method" type="xsd:string" />
303303
<xsd:attribute name="default-priority-method" type="xsd:string" />
304304
<xsd:attribute name="exclude" type="xsd:string" />
305+
<xsd:attribute name="auto-exclude-refencing-service" type="xsd:boolean" />
305306
</xsd:complexType>
306307

307308
<xsd:complexType name="call">

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,36 @@ public function testProcessWithIndexes()
5454
$expected->setValues(['1' => new TypedReference('service_a', 'stdClass'), '2' => new TypedReference('service_b', 'stdClass')]);
5555
$this->assertEquals($expected, $properties['foos']);
5656
}
57+
58+
public function testProcesWithAutoExcludeReferencingService()
59+
{
60+
$container = new ContainerBuilder();
61+
$container->register('service_a', 'stdClass')->addTag('foo', ['key' => '1']);
62+
$container->register('service_b', 'stdClass')->addTag('foo', ['key' => '2']);
63+
$container->register('service_c', 'stdClass')->addTag('foo', ['key' => '3'])->setProperty('foos', new TaggedIteratorArgument('foo', 'key'));
64+
65+
(new ResolveTaggedIteratorArgumentPass())->process($container);
66+
67+
$properties = $container->getDefinition('service_c')->getProperties();
68+
69+
$expected = new TaggedIteratorArgument('foo', 'key');
70+
$expected->setValues(['1' => new TypedReference('service_a', 'stdClass'), '2' => new TypedReference('service_b', 'stdClass')]);
71+
$this->assertEquals($expected, $properties['foos']);
72+
}
73+
74+
public function testProcesWithoutAutoExcludeReferencingService()
75+
{
76+
$container = new ContainerBuilder();
77+
$container->register('service_a', 'stdClass')->addTag('foo', ['key' => '1']);
78+
$container->register('service_b', 'stdClass')->addTag('foo', ['key' => '2']);
79+
$container->register('service_c', 'stdClass')->addTag('foo', ['key' => '3'])->setProperty('foos', new TaggedIteratorArgument(tag: 'foo', indexAttribute: 'key', autoExcludeReferencingService: false));
80+
81+
(new ResolveTaggedIteratorArgumentPass())->process($container);
82+
83+
$properties = $container->getDefinition('service_c')->getProperties();
84+
85+
$expected = new TaggedIteratorArgument(tag: 'foo', indexAttribute: 'key', autoExcludeReferencingService: false);
86+
$expected->setValues(['1' => new TypedReference('service_a', 'stdClass'), '2' => new TypedReference('service_b', 'stdClass'), '3' => new TypedReference('service_c', 'stdClass')]);
87+
$this->assertEquals($expected, $properties['foos']);
88+
}
5789
}

0 commit comments

Comments
 (0)
0