8000 [DI] ignore extra tags added by autoconfiguration in PriorityTaggedSe… · symfony/symfony@09770aa · GitHub
[go: up one dir, main page]

Skip to content

Commit 09770aa

Browse files
[DI] ignore extra tags added by autoconfiguration in PriorityTaggedServiceTrait
1 parent d246e94 commit 09770aa

File tree

2 files changed

+125
-58
lines changed

2 files changed

+125
-58
lines changed

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

Lines changed: 79 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
use Symfony\Component\DependencyInjection\ContainerBuilder;
1616
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1717
use Symfony\Component\DependencyInjection\Reference;
18-
use Symfony\Component\DependencyInjection\TypedReference;
1918

2019
/**
2120
* Trait that allows a generic method to find and sort service by priority option in the tag.
@@ -50,11 +49,10 @@ private function findAndSortTaggedServices($tagName, ContainerBuilder $container
5049
$tagName = $tagName->getTag();
5150
}
5251

52+
$i = 0;
5353
$services = [];
5454

5555
foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) {
56-
$class = $r = null;
57-
5856
$defaultPriority = null;
5957
$defaultIndex = null;
6058

@@ -64,78 +62,101 @@ private function findAndSortTaggedServices($tagName, ContainerBuilder $container
6462
if (isset($attribute['priority'])) {
6563
$priority = $attribute['priority'];
6664
} elseif (null === $defaultPriority && $defaultPriorityMethod) {
67-
$class = $container->getDefinition($serviceId)->getClass();
68-
$class = $container->getParameterBag()->resolveValue($class) ?: null;
69-
70-
if (($r = ($r ?? $container->getReflectionClass($class))) && $r->hasMethod($defaultPriorityMethod)) {
71-
if (!($rm = $r->getMethod($defaultPriorityMethod))->isStatic()) {
72-
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
73-
}
74-
75-
if (!$rm->isPublic()) {
76-
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
77-
}
78-
79-
$defaultPriority = $rm->invoke(null);
80-
81-
if (!\is_int($defaultPriority)) {
82-
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return an integer, got %s: tag "%s" on service "%s".', $class, $defaultPriorityMethod, \gettype($priority), $tagName, $serviceId));
83-
}
84-
}
65+
$defaultPriority = PriorityTaggedServiceUtil::getDefaultPriority($container, $serviceId, $defaultPriorityMethod, $tagName);
8566
}
67+
$priority = $priority ?? $defaultPriority ?? $defaultPriority = 0;
8668

87-
$priority = $priority ?? $defaultPriority ?? 0;
69+
if (null === $indexAttribute && !$needsIndexes) {
70+
$services[] = [$priority, ++$i, null, $serviceId];
71+
continue 2;
72+
}
8873

8974
if (null !== $indexAttribute && isset($attribute[$indexAttribute])) {
9075
$index = $attribute[$indexAttribute];
91-
} elseif (null === $defaultIndex && null === $indexAttribute && !$needsIndexes) {
92-
// With partially associative array, insertion to get next key is simpler.
93-
$services[$priority][] = null;
94-
end($services[$priority]);
95-
$defaultIndex = key($services[$priority]);
9676
} elseif (null === $defaultIndex && $defaultIndexMethod) {
97-
$class = $container->getDefinition($serviceId)->getClass();
98-
$class = $container->getParameterBag()->resolveValue($class) ?: null;
77+
$defaultIndex = PriorityTaggedServiceUtil::getDefaultIndex($container, $serviceId, $defaultIndexMethod, $tagName, $indexAttribute);
78+
}
79+
$index = $index ?? $defaultIndex ?? $defaultIndex = $serviceId;
9980

100-
if (($r = ($r ?? $container->getReflectionClass($class))) && $r->hasMethod($defaultIndexMethod)) {
101-
if (!($rm = $r->getMethod($defaultIndexMethod))->isStatic()) {
102-
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
103-
}
81+
$services[] = [$priority, ++$i, $index, $serviceId];
82+
}
83+
}
10484

105-
if (!$rm->isPublic()) {
106-
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
107-
}
85+
uasort($services, static function ($a, $b) { return $b[0] <=> $a[0] ?: $a[1] <=> $b[1]; });
10886

109-
$defaultIndex = $rm->invoke(null);
87+
$refs = [];
88+
foreach ($services as [, , $index, $serviceId]) {
89+
if (null === $index) {
90+
$refs[] = new Reference($serviceId);
91+
} else {
92+
$refs[$index] = new Reference($serviceId);
93+
}
94+
}
11095

111-
if (!\is_string($defaultIndex)) {
112-
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return a string, got %s: tag "%s" on service "%s" is missing "%s" attribute.', $class, $defaultIndexMethod, \gettype($defaultIndex), $tagName, $serviceId, $indexAttribute));
113-
}
114-
}
96+
return $refs;
97+
}
98+
}
11599

116-
$defaultIndex = $defaultIndex ?? $serviceId;
117-
}
100+
/**
101+
* @internal
102+
*/
103+
class PriorityTaggedServiceUtil
104+
{
105+
/**
106+
* Gets the index defined by the default index method.
107+
*/
108+
public static function getDefaultIndex(ContainerBuilder $container, string $serviceId, string $defaultIndexMethod, string $tagName, string $indexAttribute): ?string
109+
{
110+
$class = $container->getDefinition($serviceId)->getClass();
111+
$class = $container->getParameterBag()->resolveValue($class) ?: null;
118112

119-
$index = $index ?? $defaultIndex;
113+
if (!($r = $container->getReflectionClass($class)) || !$r->hasMethod($defaultIndexMethod)) {
114+
return null;
115+
}
120116

121-
$reference = null;
122-
if (!$class || 'stdClass' === $class) {
123-
$reference = new Reference($serviceId);
124-
} elseif ($index === $serviceId) {
125-
$reference = new TypedReference($serviceId, $class);
126-
} else {
127-
$reference = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, \is_string($index) ? $index : null);
128-
}
117+
if (!($rm = $r->getMethod($defaultIndexMethod))->isStatic()) {
118+
throw new InvalidArgumentException(sprintf('Either method "%s::%s()" should be static or tag "%s" on service "%s" is missing attribute "%s".', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
119+
}
129120

130-
$services[$priority][$index] = $reference;
131-
}
121+
if (!$rm->isPublic()) {
122+
throw new InvalidArgumentException(sprintf('Either method "%s::%s()" should be public or tag "%s" on service "%s" is missing attribute "%s".', $class, $defaultIndexMethod, $tagName, $serviceId, $indexAttribute));
123+
}
124+
125+
$defaultIndex = $rm->invoke(null);
126+
127+
if (!\is_string($defaultIndex)) {
128+
throw new InvalidArgumentException(sprintf('Either method "%s::%s()" should return a string (got %s) or tag "%s" on service "%s" is missing attribute "%s".', $class, $defaultIndexMethod, \gettype($defaultIndex), $tagName, $serviceId, $indexAttribute));
129+
}
130+
131+
return $defaultIndex;
132+
}
133+
134+
/**
135+
* Gets the priority defined by the default priority method.
136+
*/
137+
public static function getDefaultPriority(ContainerBuilder $container, string $serviceId, string $defaultPriorityMethod, string $tagName): ?int
138+
{
139+
$class = $container->getDefinition($serviceId)->getClass();
140+
$class = $container->getParameterBag()->resolveValue($class) ?: null;
141+
142+
if (!($r = $container->getReflectionClass($class)) || !$r->hasMethod($defaultPriorityMethod)) {
143+
return null;
132144
}
133145

134-
if ($services) {
135-
krsort($services);
136-
$services = array_merge(...$services);
146+
if (!($rm = $r->getMethod($defaultPriorityMethod))->isStatic()) {
147+
throw new InvalidArgumentException(sprintf('Either method "%s::%s()" should be static or tag "%s" on service "%s" is missing attribute "priority".', $class, $defaultPriorityMethod, $tagName, $serviceId));
148+
}
149+
150+
if (!$rm->isPublic()) {
151+
throw new InvalidArgumentException(sprintf('Either method "%s::%s()" should be public or tag "%s" on service "%s" is missing attribute "priority".', $class, $defaultPriorityMethod, $tagName, $serviceId));
152+
}
153+
154+
$defaultPriority = $rm->invoke(null);
155+
156+
if (!\is_int($defaultPriority)) {
157+
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return an integer (got %s) or tag "%s" on service "%s" is missing attribute "priority".', $class, $defaultPriorityMethod, \gettype($defaultPriority), $tagName, $serviceId));
137158
}
138159

139-
return $services;
160+
return $defaultPriority;
140161
}
141162
}

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
1516
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
1718
use Symfony\Component\DependencyInjection\Reference;
19+
use Symfony\Component\DependencyInjection\Tests\Fixtures\BarTagClass;
1820

1921
class PriorityTaggedServiceTraitTest extends TestCase
2022
{
@@ -85,6 +87,50 @@ public function testWithEmptyArray()
8587
$priorityTaggedServiceTraitImplementation = new PriorityTaggedServiceTraitImplementation();
8688
$this->assertEquals([], $priorityTaggedServiceTraitImplementation->test('my_custom_tag', $container));
8789
}
90+
91+
public function testOnlyTheFirstNonIndexedTagIsListed()
92+
{
93+
$container = new ContainerBuilder();
94+
$container->register('service1')->addTag('my_custom_tag');
95+
96+
$definition = $container->register('service2', BarTagClass::class);
97+
$definition->addTag('my_custom_tag', ['priority' => 100]);
98+
$definition->addTag('my_custom_tag', []);
99+
100+
$priorityTaggedServiceTraitImplementation = new PriorityTaggedServiceTraitImplementation();
101+
102+
$expected = [
103+
new Reference('service2'),
104+
new Reference('service1'),
105+
];
106+
$this->assertEquals($expected, $priorityTaggedServiceTraitImplementation->test('my_custom_tag', $container));
107+
}
108+
109+
public function testOnlyTheIndexedTagsAreListed()
110+
{
111+
$container = new ContainerBuilder();
112+
$container->register('service1')->addTag('my_custom_tag', ['foo' => 'bar']);
113+
114+
$definition = $container->register('service2', BarTagClass::class);
115+
$definition->addTag('my_custom_tag', ['priority' => 100]);
116+
$definition->addTag('my_custom_tag', ['foo' => 'a']);
117+
$definition->addTag('my_custom_tag', ['foo' => 'b', 'priority' => 100]);
118+
$definition->addTag('my_custom_tag', ['foo' => 'b']);
119+
$definition->addTag('my_custom_tag', []);
120+
121+
$priorityTaggedServiceTraitImplementation = new PriorityTaggedServiceTraitImplementation();
122+
123+
$tag = new TaggedIteratorArgument('my_custom_tag', 'foo');
124+
$expected = [
125+
'bar_tag_class' => new Reference('service2'),
126+
'b' => new Reference('service2'),
127+
'bar' => new Reference('service1'),
128+
'a' => new Reference('service2'),
129+
];
130+
$services = $priorityTaggedServiceTraitImplementation->test($tag, $container);
131+
$this->assertSame(array_keys($expected), array_keys($services));
132+
$this->assertEquals($expected, $priorityTaggedServiceTraitImplementation->test($tag, $container));
133+
}
88134
}
89135

90136
class PriorityTaggedServiceTraitImplementation

0 commit comments

Comments
 (0)
0