Skip to content

Commit db5f9fb

Browse files
committed
[DependencyInjection] added Ability to define a priority method for tagged service
1 parent b56a4b4 commit db5f9fb

19 files changed

+134
-27
lines changed

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ class TaggedIteratorArgument extends IteratorArgument
2424
private $needsIndexes = false;
2525

2626
/**
27-
* @param string $tag The name of the tag identifying the target services
28-
* @param string|null $indexAttribute The name of the attribute that defines the key referencing each service in the tagged collection
29-
* @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
30-
* @param bool $needsIndexes Whether indexes are required and should be generated when computing the map
27+
* @param string $tag The name of the tag identifying the target services
28+
* @param string|null $indexAttribute The name of the attribute that defines the key referencing each service in the tagged collection
29+
* @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
30+
* @param bool $needsIndexes Whether indexes are required and should be generated when computing the map
31+
* @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
3132
*/
32-
public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false)
33+
public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false, string $defaultPriorityMethod = null)
3334
{
3435
parent::__construct([]);
3536

@@ -41,6 +42,7 @@ public function __construct(string $tag, string $indexAttribute = null, string $
4142
$this->indexAttribute = $indexAttribute;
4243
$this->defaultIndexMethod = $defaultIndexMethod ?: ('getDefault'.str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $indexAttribute ?? ''))).'Name');
4344
$this->needsIndexes = $needsIndexes;
45+
$this->defaultPriorityMethod = $defaultPriorityMethod;
4446
}
4547

4648
public function getTag()
@@ -62,4 +64,9 @@ public function needsIndexes(): bool
6264
{
6365
return $this->needsIndexes;
6466
}
67+
68+
public function getDefaultPriorityMethod(): ?string
69+
{
70+
return $this->defaultPriorityMethod;
71+
}
6572
}

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ CHANGELOG
1111
* deprecated passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition`
1212
* added support for binding iterable and tagged services
1313
* made singly-implemented interfaces detection be scoped by file
14+
* added ability to define a static priority method for tagged service
1415

1516
4.3.0
1617
-----

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

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,36 +40,73 @@ trait PriorityTaggedServiceTrait
4040
*/
4141
private function findAndSortTaggedServices($tagName, ContainerBuilder $container): array
4242
{
43-
$indexAttribute = $defaultIndexMethod = $needsIndexes = null;
43+
$indexAttribute = $defaultIndexMethod = $needsIndexes = $defaultPriorityMethod = null;
4444

4545
if ($tagName instanceof TaggedIteratorArgument) {
4646
$indexAttribute = $tagName->getIndexAttribute();
4747
$defaultIndexMethod = $tagName->getDefaultIndexMethod();
4848
$needsIndexes = $tagName->needsIndexes();
49+
$defaultPriorityMethod = $tagName->getDefaultPriorityMethod();
4950
$tagName = $tagName->getTag();
5051
}
5152

5253
$services = [];
5354

5455
foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) {
55-
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
56+
$class = $r = null;
57+
if (isset($attributes[0]['priority'])) {
58+
$priority = $attributes[0]['priority'];
59+
} elseif ($defaultPriorityMethod) {
60+
$class = $container->getDefinition($serviceId)->getClass();
61+
$class = $container->getParameterBag()->resolveValue($class) ?: null;
62+
63+
if (!$r = $container->getReflectionClass($class)) {
64+
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $serviceId));
65+
}
66+
67+
if ($r->hasMethod($defaultPriorityMethod)) {
68+
if (!($rm = $r->getMethod($defaultPriorityMethod))->isStatic()) {
69+
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
70+
}
71+
72+
if (!$rm->isPublic()) {
73+
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
74+
}
75+
76+
$priority = $rm->invoke(null);
77+
78+
if (!\is_int($priority)) {
79+
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return an integer, got %s: tag "%s" on service "%s".', $class, $defaultPriorityMethod, \gettype($priority), $tagName, $serviceId));
80+
}
81+
} else {
82+
$priority = 0;
83+
}
84+
} else {
85+
$priority = 0;
86+
}
5687

5788
if (null === $indexAttribute && !$needsIndexes) {
58-
$services[$priority][] = new Reference($serviceId);
89+
if ($class) {
90+
$services[$priority][] = new TypedReference($serviceId, $class);
91+
} else {
92+
$services[$priority][] = new Reference($serviceId);
93+
}
5994

6095
continue;
6196
}
6297

63-
$class = $container->getDefinition($serviceId)->getClass();
64-
$class = $container->getParameterBag()->resolveValue($class) ?: null;
98+
if (!$class) {
99+
$class = $container->getDefinition($serviceId)->getClass();
100+
$class = $container->getParameterBag()->resolveValue($class) ?: null;
101+
}
65102

66103
if (null !== $indexAttribute && isset($attributes[0][$indexAttribute])) {
67104
$services[$priority][$attributes[0][$indexAttribute]] = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $attributes[0][$indexAttribute]);
68105

69106
continue;
70107
}
71108

72-
if (!$r = $container->getReflectionClass($class)) {
109+
if (!$r && !$r = $container->getReflectionClass($class)) {
73110
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $serviceId));
74111
}
75112

src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ private function convertParameters(array $parameters, string $type, \DOMElement
274274
if (null !== $tag->getDefaultIndexMethod()) {
275275
$element->setAttribute('default-index-method', $tag->getDefaultIndexMethod());
276276
}
277+
if (null !== $tag->getDefaultPriorityMethod()) {
278+
$element->setAttribute('default-priority-method', $tag->getDefaultPriorityMethod());
279+
}
277280
}
278281
} elseif ($value instanceof IteratorArgument) {
279282
$element->setAttribute('type', 'iterator');

src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ private function dumpValue($value)
244244
if (null !== $tag->getDefaultIndexMethod()) {
245245
$content['default_index_method'] = $tag->getDefaultIndexMethod();
246246
}
247+
if (null !== $tag->getDefaultPriorityMethod()) {
248+
$content['default_priority_method'] = $tag->getDefaultPriorityMethod();
249+
}
247250
}
248251

249252
return new TaggedValue($value instanceof TaggedIteratorArgument ? 'tagged_iterator' : 'tagged_locator', $content);

src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ function tagged(string $tag, string $indexAttribute = null, string $defaultIndex
128128
/**
129129
* Creates a lazy iterator by tag name.
130130
*/
131-
function tagged_iterator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null): TaggedIteratorArgument
131+
function tagged_iterator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, string $defaultPriorityMethod = null): TaggedIteratorArgument
132132
{
133-
return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod);
133+
return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, false, $defaultPriorityMethod);
134134
}
135135

136136
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ private function getArgumentsAsPhp(\DOMElement $node, string $name, string $file
513513
throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="%s" has no or empty "tag" attribute in "%s".', $name, $type, $file));
514514
}
515515

516-
$arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag'), $arg->getAttribute('index-by') ?: null, $arg->getAttribute('default-index-method') ?: null, $forLocator);
516+
$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);
517517

518518
if ($forLocator) {
519519
$arguments[$key] = new ServiceLocatorArgument($arguments[$key]);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -721,11 +721,11 @@ private function resolveServices($value, string $file, bool $isParameter = false
721721
$forLocator = 'tagged_locator' === $value->getTag();
722722

723723
if (\is_array($argument) && isset($argument['tag']) && $argument['tag']) {
724-
if ($diff = array_diff(array_keys($argument), ['tag', 'index_by', 'default_index_method'])) {
725-
throw new InvalidArgumentException(sprintf('"!%s" tag contains unsupported key "%s"; supported ones are "tag", "index_by" and "default_index_method".', $value->getTag(), implode('"", "', $diff)));
724+
if ($diff = array_diff(array_keys($argument), ['tag', 'index_by', 'default_index_method', 'default_priority_method'])) {
725+
throw new InvalidArgumentException(sprintf('"!%s" tag contains unsupported key "%s"; supported ones are "tag", "index_by", "default_index_method", and "default_priority_method".', $value->getTag(), implode('"", "', $diff)));
726726
}
727727

728-
$argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator);
728+
$argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator, $argument['default_priority_method'] ?? null);
729729
} elseif (\is_string($argument) && $argument) {
730730
$argument = new TaggedIteratorArgument($argument, null, null, $forLocator);
731731
} 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
@@ -236,6 +236,7 @@
236236
<xsd:attribute name="tag" type="xsd:string" />
237237
<xsd:attribute name="index-by" type="xsd:string" />
238238
<xsd:attribute name="default-index-method" type="xsd:string" />
239+
<xsd:attribute name="default-priority-method" type="xsd:string" />
239240
</xsd:complexType>
240241

241242
<xsd:complexType name="call">

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
2424
use Symfony\Component\DependencyInjection\Tests\Fixtures\BarTagClass;
2525
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedClass;
26+
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedForDefaultPriorityClass;
2627
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooTagClass;
2728

2829
/**
@@ -289,6 +290,30 @@ public function testTaggedServiceWithIndexAttributeAndDefaultMethod()
289290
$this->assertSame(['bar_tab_class_with_defaultmethod' => $container->get(BarTagClass::class), 'foo' => $container->get(FooTagClass::class)], $param);
290291
}
291292

293+
public function testTaggedServiceWithDefaultPriorityMethod()
294+
{
295+
$container = new ContainerBuilder();
296+
$container->register(BarTagClass::class)
297+
->setPublic(true)
298+
->addTag('foo_bar')
299+
;
300+
$container->register(FooTagClass::class)
301+
->setPublic(true)
302+
->addTag('foo_bar', ['foo' => 'foo'])
303+
;
304+
$container->register(FooBarTaggedForDefaultPriorityClass::class)
305+
->addArgument(new TaggedIteratorArgument('foo_bar', null, null, false, 'getPriority'))
306+
->setPublic(true)
307+
;
308+
309+
$container->compile();
310+
311+
$s = $container->get(FooBarTaggedForDefaultPriorityClass::class);
312+
313+
$param = iterator_to_array($s->getParam()->getIterator());
314+
$this->assertSame([$container->get(FooTagClass::class), $container->get(BarTagClass::class)], $param);
315+
}
316+
292317
public function testTaggedServiceLocatorWithIndexAttribute()
293318
{
294319
$container = new ContainerBuilder();

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy