Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@ class TaggedIteratorArgument extends IteratorArgument
private ?string $defaultIndexMethod;
private ?string $defaultPriorityMethod;
private bool $needsIndexes;
private array $exclude;

/**
* @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
*/
public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false, string $defaultPriorityMethod = null)
public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false, string $defaultPriorityMethod = null, array $exclude = [])
{
parent::__construct([]);

Expand All @@ -44,6 +46,7 @@ public function __construct(string $tag, string $indexAttribute = null, string $
$this->defaultIndexMethod = $defaultIndexMethod ?: ($indexAttribute ? 'getDefault'.str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $indexAttribute))).'Name' : null);
$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;
}

public function getTag()
Expand All @@ -70,4 +73,9 @@ public function getDefaultPriorityMethod(): ?string
{
return $this->defaultPriorityMethod;
}

public function getExclude(): array
{
return $this->exclude;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public function __construct(
public ?string $indexAttribute = null,
public ?string $defaultIndexMethod = null,
public ?string $defaultPriorityMethod = null,
public string|array $exclude = [],
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public function __construct(
public ?string $indexAttribute = null,
public ?string $defaultIndexMethod = null,
public ?string $defaultPriorityMethod = null,
public string|array $exclude = [],
) {
}
}
6 changes: 6 additions & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
CHANGELOG
=========

6.1
---

* Add `$exclude` to `TaggedIterator` and `TaggedLocator` attributes
* Add `$exclude` to `tagged_iterator` and `tagged_locator` configurator

6.0
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,13 +247,13 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
foreach ($parameter->getAttributes() as $attribute) {
if (TaggedIterator::class === $attribute->getName()) {
$attribute = $attribute->newInstance();
$arguments[$index] = new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute, $attribute->defaultIndexMethod, false, $attribute->defaultPriorityMethod);
$arguments[$index] = new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute, $attribute->defaultIndexMethod, false, $attribute->defaultPriorityMethod, (array) $attribute->exclude);
break;
}

if (TaggedLocator::class === $attribute->getName()) {
$attribute = $attribute->newInstance();
$arguments[$index] = new ServiceLocatorArgument(new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute, $attribute->defaultIndexMethod, true, $attribute->defaultPriorityMethod));
$arguments[$index] = new ServiceLocatorArgument(new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute, $attribute->defaultIndexMethod, true, $attribute->defaultPriorityMethod, (array) $attribute->exclude));
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,26 @@ trait PriorityTaggedServiceTrait
*/
private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagName, ContainerBuilder $container): 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();
$tagName = $tagName->getTag();
}

$i = 0;
$services = [];

foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) {
if (\in_array($serviceId, $exclude, true)) {
continue;
}

$defaultPriority = null;
$defaultIndex = null;
$definition = $container->getDefinition($serviceId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,17 @@ function iterator(array $values): IteratorArgument
/**
* Creates a lazy iterator by tag name.
*/
function tagged_iterator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, string $defaultPriorityMethod = null): TaggedIteratorArgument
function tagged_iterator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, string $defaultPriorityMethod = null, string|array $exclude = []): TaggedIteratorArgument
{
return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, false, $defaultPriorityMethod);
return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, false, $defaultPriorityMethod, (array) $exclude);
}

/**
* Creates a service locator by tag name.
*/
function tagged_locator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, string $defaultPriorityMethod = null): ServiceLocatorArgument
function tagged_locator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, string $defaultPriorityMethod = null, string|array $exclude = []): ServiceLocatorArgument
{
return new ServiceLocatorArgument(new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, true, $defaultPriorityMethod));
return new ServiceLocatorArgument(new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, true, $defaultPriorityMethod, (array) $exclude));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\Attribute\CustomMethodAttribute;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Attribute\CustomParameterAttribute;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Attribute\CustomPropertyAttribute;
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfiguredInterface2;
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfiguredService1;
use Symfony\Component\DependencyInjection\Tests\Fixtures\AutoconfiguredService2;
use Symfony\Component\DependencyInjection\Tests\Fixtures\BarTagClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedForDefaultPriorityClass;
Expand All @@ -43,6 +46,7 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\LocatorConsumerWithDefaultIndexMethodAndWithDefaultPriorityMethod;
use Symfony\Component\DependencyInjection\Tests\Fixtures\LocatorConsumerWithDefaultPriorityMethod;
use Symfony\Component\DependencyInjection\Tests\Fixtures\LocatorConsumerWithoutIndex;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TaggedConsumerWithExclude;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TaggedService1;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TaggedService2;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TaggedService3;
Expand Down Expand Up @@ -999,6 +1003,44 @@ static function (ChildDefinition $definition) {
self::assertSame(6, $service->sum);
self::assertTrue($service->hasBeenConfigured);
}

public function testTaggedIteratorAndLocatorWithExclude()
{
$container = new ContainerBuilder();

$container->register(AutoconfiguredService1::class)
->addTag(AutoconfiguredInterface2::class)
->setPublic(true)
;
$container->register(AutoconfiguredService2::class)
->addTag(AutoconfiguredInterface2::class)
->setPublic(true)
;
$container->register(TaggedConsumerWithExclude::class)
->addTag(AutoconfiguredInterface2::class)
->setAutoconfigured(true)
->setAutowired(true)
->setPublic(true)
;

$container->compile();

$this->assertTrue($container->getDefinition(AutoconfiguredService1::class)->hasTag(AutoconfiguredInterface2::class));
$this->assertTrue($container->getDefinition(AutoconfiguredService2::class)->hasTag(AutoconfiguredInterface2::class));
$this->assertTrue($container->getDefinition(TaggedConsumerWithExclude::class)->hasTag(AutoconfiguredInterface2::class));

$s = $container->get(TaggedConsumerWithExclude::class);

$items = iterator_to_array($s->items->getIterator());
$this->assertCount(2, $items);
$this->assertInstanceOf(AutoconfiguredService1::class, $items[0]);
$this->assertInstanceOf(AutoconfiguredService2::class, $items[1]);

$locator = $s->locator;
$this->assertTrue($locator->has(AutoconfiguredService1::class));
$this->assertTrue($locator->has(AutoconfiguredService2::class));
$this->assertFalse($locator->has(TaggedConsumerWithExclude::class));
}
}

class ServiceSubscriberStub implements ServiceSubscriberInterface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,18 @@ public function testTaggedItemAttributes()
$container->register('service3', HelloNamedService2::class)
->setAutoconfigured(true)
->addTag('my_custom_tag');
$container->register('service4', HelloNamedService2::class)
->setAutoconfigured(true)
->addTag('my_custom_tag');
$container->register('service5', HelloNamedService2::class)
->setAutoconfigured(true)
->addTag('my_custom_tag');

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

$priorityTaggedServiceTraitImplementation = new PriorityTaggedServiceTraitImplementation();

$tag = new TaggedIteratorArgument('my_custom_tag', 'foo', 'getFooBar');
$tag = new TaggedIteratorArgument('my_custom_tag', 'foo', 'getFooBar', exclude: ['service4', 'service5']);
$expected = [
'service3' => new TypedReference('service3', HelloNamedService2::class),
'hello' => new TypedReference('service2', HelloNamedService::class),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;

#[AutoconfigureTag]
interface AutoconfiguredInterface2
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

class AutoconfiguredService1 implements AutoconfiguredInterface2
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

class AutoconfiguredService2 implements AutoconfiguredInterface2
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Tests\Fixtures;

use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
use Symfony\Component\DependencyInjection\Attribute\TaggedLocator;

final class TaggedConsumerWithExclude implements AutoconfiguredInterface2
{
public function __construct(
#[TaggedIterator(AutoconfiguredInterface2::class, exclude: self::class)]
public iterable $items,
#[TaggedLocator(AutoconfiguredInterface2::class, exclude: self::class)]
public ContainerInterface $locator,
) {
}
}