Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5dac5c0

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

20 files changed

+132
-28
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 ?: ('getDefault'.str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $indexAttribute ?? ''))).'Priority');
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: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,36 +40,69 @@ 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)) && $r->hasMethod($defaultPriorityMethod)) {
64+
if (!($rm = $r->getMethod($defaultPriorityMethod))->isStatic()) {
65+
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
66+
}
67+
68+
if (!$rm->isPublic()) {
69+
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId));
70+
}
71+
72+
$priority = $rm->invoke(null);
73+
74+
if (!\is_int($priority)) {
75+
throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return an integer, got %s: tag "%s" on service "%s".', $class, $defaultPriorityMethod, \gettype($priority), $tagName, $serviceId));
76+
}
77+
} else {
78+
$priority = 0;
79+
}
80+
} else {
81+
$priority = 0;
82+
}
5683

5784
if (null === $indexAttribute && !$needsIndexes) {
58-
$services[$priority][] = new Reference($serviceId);
85+
if ($class) {
86+
$services[$priority][] = new TypedReference($serviceId, $class);
87+
} else {
88+
$services[$priority][] = new Reference($serviceId);
89+
}
5990

6091
continue;
6192
}
6293

63-
$class = $container->getDefinition($serviceId)->getClass();
64-
$class = $container->getParameterBag()->resolveValue($class) ?: null;
94+
if (!$class) {
95+
$class = $container->getDefinition($serviceId)->getClass();
96+
$class = $container->getParameterBag()->resolveValue($class) ?: null;
97+
}
6598

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

69102
continue;
70103
}
71104

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

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();

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass;
1717
use Symfony\Component\DependencyInjection\ContainerBuilder;
1818
use Symfony\Component\DependencyInjection\Reference;
19+
use Symfony\Component\DependencyInjection\TypedReference;
1920

2021
/**
2122
* @author Roland Franssen <[email protected]>
@@ -34,7 +35,7 @@ public function testProcess()
3435

3536
$properties = $container->getDefinition('d')->getProperties();
3637
$expected = new TaggedIteratorArgument('foo');
37-
$expected->setValues([new Reference('b'), new Reference('c'), new Reference('a')]);
38+
$expected->setValues([new Reference('b'), new Reference('c'), new TypedReference('a', \stdClass::class)]);
3839
$this->assertEquals($expected, $properties['foos']);
3940
}
4041
}

src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ public function testDumpLoad()
204204

205205
public function testTaggedArguments()
206206
{
207-
$taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar');
207+
$taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar', false, 'getPriority');
208208
$container = new ContainerBuilder();
209209
$container->register('foo', 'Foo')->addTag('foo_tag');
210210
$container->register('foo_tagged_iterator', 'Bar')

src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public function testInlineServices()
9999

100100
public function testTaggedArguments()
101101
{
102-
$taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar');
102+
$taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', false, 'getPriority');
103103
$container = new ContainerBuilder();
104104
$container->register('foo_service', 'Foo')->addTag('foo');
105105
$container->register('foo_service_tagged_iterator', 'Bar')->addArgument($taggedIterator);

src/Symfony/Component/DependencyInjection/Tests/Fixtures/BarTagClass.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,9 @@ public static function getFooBar()
1313
{
1414
return 'bar_tab_class_with_defaultmethod';
1515
}
16+
17+
public static function getPriority(): int
18+
{
19+
return 0;
20+
}
1621
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
4+
5+
class FooBarTaggedForDefaultPriorityClass
6+
{
7+
private $param;
8+
9+
public function __construct($param = [])
10+
{
11+
$this->param = $param;
12+
}
13+
14+
public function getParam()
15+
{
16+
return $this->param;
17+
}
18+
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooTagClass.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,11 @@ public static function getDefaultFooName()
88
{
99
return 'foo_tag_class';
1010
}
11+
12+
public static function getPriority(): int
13+
{
14+
// Should be more than BarTagClass. More because this class is after
15+
// BarTagClass (order by name). So we want to ensure it will be before it
16+
return 20;
17+
}
1118
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_tagged_arguments.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
<tag name="foo_tag"/>
77
</service>
88
<service id="foo_tagged_iterator" class="Bar" public="true">
9-
<argument type="tagged_iterator" tag="foo_tag" index-by="barfoo" default-index-method="foobar"/>
9+
<argument type="tagged_iterator" tag="foo_tag" index-by="barfoo" default-index-method="foobar" default-priority-method="getPriority"/>
1010
</service>
1111
<service id="foo_tagged_locator" class="Bar" public="true">
12-
<argument type="tagged_locator" tag="foo_tag" index-by="barfoo" default-index-method="foobar"/>
12+
<argument type="tagged_locator" tag="foo_tag" index-by="barfoo" default-index-method="foobar" default-priority-method="getPriority"/>
1313
</service>
1414
<service id="Psr\Container\ContainerInterface" alias="service_container" public="false"/>
1515
<service id="Symfony\Component\DependencyInjection\ContainerInterface" alias="service_container" public="false"/>

src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_tagged_argument.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ services:
1010
- { name: foo }
1111
foo_service_tagged_iterator:
1212
class: Bar
13-
arguments: [!tagged_iterator { tag: foo, index_by: barfoo, default_index_method: foobar }]
13+
arguments: [!tagged_iterator { tag: foo, index_by: barfoo, default_index_method: foobar, default_priority_method: getPriority }]
1414
foo_service_tagged_locator:
1515
class: Bar
16-
arguments: [!tagged_locator { tag: foo, index_by: barfoo, default_index_method: foobar }]
16+
arguments: [!tagged_locator { tag: foo, index_by: barfoo, default_index_method: foobar, default_priority_method: getPriority }]
1717
bar_service_tagged_locator:
1818
class: Bar
1919
arguments: [!tagged_locator foo]

src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,10 @@ public function testParseTaggedArgumentsWithIndexBy()
327327
$this->assertCount(1, $container->getDefinition('foo_tagged_iterator')->getArguments());
328328
$this->assertCount(1, $container->getDefinition('foo_tagged_locator')->getArguments());
329329

330-
$taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar');
330+
$taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar', false, 'getPriority');
331331
$this->assertEquals($taggedIterator, $container->getDefinition('foo_tagged_iterator')->getArgument(0));
332332

333-
$taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar', true);
333+
$taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar', true, 'getPriority');
334334
$this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('foo_tagged_locator')->getArgument(0));
335335
}
336336

src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,10 +306,10 @@ public function testTaggedArgumentsWithIndex()
306306
$this->assertCount(1, $container->getDefinition('foo_service_tagged_iterator')->getArguments());
307307
$this->assertCount(1, $container->getDefinition('foo_service_tagged_locator')->getArguments());
308308

309-
$taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar');
309+
$taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', false, 'getPriority');
310310
$this->assertEquals($taggedIterator, $container->getDefinition('foo_service_tagged_iterator')->getArgument(0));
311311

312-
$taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', true);
312+
$taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', true, 'getPriority');
313313
$this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('foo_service_tagged_locator')->getArgument(0));
314314

315315
if (is_subclass_of('Symfony\Component\Yaml\Exception\ExceptionInterface', 'Throwable')) {

0 commit comments

Comments
 (0)