Skip to content

Commit

Permalink
[DependencyInjection] Auto exclude referencing service in `TaggedIter…
Browse files Browse the repository at this point in the history
…atorArgument`
  • Loading branch information
chalasr committed Dec 16, 2022
1 parent e7482d7 commit e5570b4
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,18 @@ class TaggedIteratorArgument extends IteratorArgument
private ?string $defaultPriorityMethod;
private bool $needsIndexes;
private array $exclude;
private bool $autoExcludeReferencingService = true;

/**
* @param string $tag The name of the tag identifying the target services
* @param string|null $indexAttribute The name of the attribute that defines the key referencing each service in the tagged collection
* @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
* @param bool $needsIndexes Whether indexes are required and should be generated when computing the map
* @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
* @param array $exclude Services to exclude from the iterator
* @param string $tag The name of the tag identifying the target services
* @param string|null $indexAttribute The name of the attribute that defines the key referencing each service in the tagged collection
* @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
* @param bool $needsIndexes Whether indexes are required and should be generated when computing the map
* @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
* @param array $exclude Services to exclude from the iterator
* @param array $autoExcludeReferencingService Whether to automatically exclude the referencing service from the iterator
*/
public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false, string $defaultPriorityMethod = null, array $exclude = [])
public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false, string $defaultPriorityMethod = null, array $exclude = [], bool $autoExcludeReferencingService = true)
{
parent::__construct([]);

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

public function getTag()
Expand Down Expand Up @@ -78,4 +81,9 @@ public function getExclude(): array
{
return $this->exclude;
}

public function autoExcludeReferencingService(): bool
{
return $this->autoExcludeReferencingService;
}
}
2 changes: 2 additions & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ CHANGELOG
* Add options `inline_factories` and `inline_class_loader` to `PhpDumper::dump()`
* Deprecate `PhpDumper` options `inline_factories_parameter` and `inline_class_loader_parameter`
* Add `RemoveBuildParametersPass`, which removes parameters starting with a dot during compilation
* Add `autoExcludeReferencingService` parameter to `TaggedIteratorArgument` with default value to `true`
to control whether the referencing service should be automatically excluded from the iterator if it holds the tag

6.2
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,16 @@ trait PriorityTaggedServiceTrait
*
* @return Reference[]
*/
private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagName, ContainerBuilder $container): array
private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagName, ContainerBuilder $container, array $exclude = []): array
{
$exclude = [];
$indexAttribute = $defaultIndexMethod = $needsIndexes = $defaultPriorityMethod = null;

if ($tagName instanceof TaggedIteratorArgument) {
$indexAttribute = $tagName->getIndexAttribute();
$defaultIndexMethod = $tagName->getDefaultIndexMethod();
$needsIndexes = $tagName->needsIndexes();
$defaultPriorityMethod = $tagName->getDefaultPriorityMethod() ?? 'getDefaultPriority';
$exclude = $tagName->getExclude();
$exclude = $exclude ?: $tagName->getExclude();
$tagName = $tagName->getTag();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed
return parent::processValue($value, $isRoot);
}

$value->setValues($this->findAndSortTaggedServices($value, $this->container));
$exclude = $value->getExclude();
if ($value->autoExcludeReferencingService() && !\in_array($this->currentId, $exclude, true) && $this->container->getDefinition($this->currentId)->hasTag($value->getTag())) {
$exclude[] = $this->currentId;
}

$value->setValues($this->findAndSortTaggedServices($value, $this->container, $exclude));

return $value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,36 @@ public function testProcessWithIndexes()
$expected->setValues(['1' => new TypedReference('service_a', 'stdClass'), '2' => new TypedReference('service_b', 'stdClass')]);
$this->assertEquals($expected, $properties['foos']);
}

public function testProcesWithAutoExcludeReferencingService()
{
$container = new ContainerBuilder();
$container->register('service_a', 'stdClass')->addTag('foo', ['key' => '1']);
$container->register('service_b', 'stdClass')->addTag('foo', ['key' => '2']);
$container->register('service_c', 'stdClass')->addTag('foo', ['key' => '3'])->setProperty('foos', new TaggedIteratorArgument('foo', 'key'));

(new ResolveTaggedIteratorArgumentPass())->process($container);

$properties = $container->getDefinition('service_c')->getProperties();

$expected = new TaggedIteratorArgument('foo', 'key');
$expected->setValues(['1' => new TypedReference('service_a', 'stdClass'), '2' => new TypedReference('service_b', 'stdClass')]);
$this->assertEquals($expected, $properties['foos']);
}

public function testProcesWithoutAutoExcludeReferencingService()
{
$container = new ContainerBuilder();
$container->register('service_a', 'stdClass')->addTag('foo', ['key' => '1']);
$container->register('service_b', 'stdClass')->addTag('foo', ['key' => '2']);
$container->register('service_c', 'stdClass')->addTag('foo', ['key' => '3'])->setProperty('foos', new TaggedIteratorArgument(tag: 'foo', indexAttribute: 'key', autoExcludeReferencingService: false));

(new ResolveTaggedIteratorArgumentPass())->process($container);

$properties = $container->getDefinition('service_c')->getProperties();

$expected = new TaggedIteratorArgument(tag: 'foo', indexAttribute: 'key', autoExcludeReferencingService: false);
$expected->setValues(['1' => new TypedReference('service_a', 'stdClass'), '2' => new TypedReference('service_b', 'stdClass'), '3' => new TypedReference('service_c', 'stdClass')]);
$this->assertEquals($expected, $properties['foos']);
}
}

0 comments on commit e5570b4

Please sign in to comment.