diff --git a/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php b/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php index c88b174169054..1a2518812fa22 100644 --- a/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php +++ b/src/Symfony/Component/DependencyInjection/Argument/TaggedIteratorArgument.php @@ -24,12 +24,13 @@ class TaggedIteratorArgument extends IteratorArgument private $needsIndexes = false; /** - * @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 $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 */ - public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false) + public function __construct(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, bool $needsIndexes = false, string $defaultPriorityMethod = null) { parent::__construct([]); @@ -41,6 +42,7 @@ public function __construct(string $tag, string $indexAttribute = null, string $ $this->indexAttribute = $indexAttribute; $this->defaultIndexMethod = $defaultIndexMethod ?: ('getDefault'.str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $indexAttribute ?? ''))).'Name'); $this->needsIndexes = $needsIndexes; + $this->defaultPriorityMethod = $defaultPriorityMethod ?: ('getDefault'.str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $indexAttribute ?? ''))).'Priority'); } public function getTag() @@ -62,4 +64,9 @@ public function needsIndexes(): bool { return $this->needsIndexes; } + + public function getDefaultPriorityMethod(): ?string + { + return $this->defaultPriorityMethod; + } } diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 22db0a1c49cfc..7171cb882d919 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -11,6 +11,7 @@ CHANGELOG * deprecated passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition` * added support for binding iterable and tagged services * made singly-implemented interfaces detection be scoped by file + * added ability to define a static priority method for tagged service 4.3.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php index a86c6a742976b..9b3760b49758d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php @@ -40,19 +40,43 @@ trait PriorityTaggedServiceTrait */ private function findAndSortTaggedServices($tagName, ContainerBuilder $container): array { - $indexAttribute = $defaultIndexMethod = $needsIndexes = null; + $indexAttribute = $defaultIndexMethod = $needsIndexes = $defaultPriorityMethod = null; if ($tagName instanceof TaggedIteratorArgument) { $indexAttribute = $tagName->getIndexAttribute(); $defaultIndexMethod = $tagName->getDefaultIndexMethod(); $needsIndexes = $tagName->needsIndexes(); + $defaultPriorityMethod = $tagName->getDefaultPriorityMethod(); $tagName = $tagName->getTag(); } $services = []; foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) { - $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; + $class = $r = null; + $priority = 0; + if (isset($attributes[0]['priority'])) { + $priority = $attributes[0]['priority']; + } elseif ($defaultPriorityMethod) { + $class = $container->getDefinition($serviceId)->getClass(); + $class = $container->getParameterBag()->resolveValue($class) ?: null; + + if (($r = $container->getReflectionClass($class)) && $r->hasMethod($defaultPriorityMethod)) { + if (!($rm = $r->getMethod($defaultPriorityMethod))->isStatic()) { + throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be static: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId)); + } + + if (!$rm->isPublic()) { + throw new InvalidArgumentException(sprintf('Method "%s::%s()" should be public: tag "%s" on service "%s".', $class, $defaultPriorityMethod, $tagName, $serviceId)); + } + + $priority = $rm->invoke(null); + + if (!\is_int($priority)) { + throw new InvalidArgumentException(sprintf('Method "%s::%s()" should return an integer, got %s: tag "%s" on service "%s".', $class, $defaultPriorityMethod, \gettype($priority), $tagName, $serviceId)); + } + } + } if (null === $indexAttribute && !$needsIndexes) { $services[$priority][] = new Reference($serviceId); @@ -60,8 +84,10 @@ private function findAndSortTaggedServices($tagName, ContainerBuilder $container continue; } - $class = $container->getDefinition($serviceId)->getClass(); - $class = $container->getParameterBag()->resolveValue($class) ?: null; + if (!$class) { + $class = $container->getDefinition($serviceId)->getClass(); + $class = $container->getParameterBag()->resolveValue($class) ?: null; + } if (null !== $indexAttribute && isset($attributes[0][$indexAttribute])) { $services[$priority][$attributes[0][$indexAttribute]] = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $attributes[0][$indexAttribute]); @@ -69,7 +95,7 @@ private function findAndSortTaggedServices($tagName, ContainerBuilder $container continue; } - if (!$r = $container->getReflectionClass($class)) { + if (!$r && !$r = $container->getReflectionClass($class)) { throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $serviceId)); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 43a22f65ee933..8060170612efa 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -274,6 +274,9 @@ private function convertParameters(array $parameters, string $type, \DOMElement if (null !== $tag->getDefaultIndexMethod()) { $element->setAttribute('default-index-method', $tag->getDefaultIndexMethod()); } + if (null !== $tag->getDefaultPriorityMethod()) { + $element->setAttribute('default-priority-method', $tag->getDefaultPriorityMethod()); + } } } elseif ($value instanceof IteratorArgument) { $element->setAttribute('type', 'iterator'); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 24f55969b5047..4eb68677f679a 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -244,6 +244,9 @@ private function dumpValue($value) if (null !== $tag->getDefaultIndexMethod()) { $content['default_index_method'] = $tag->getDefaultIndexMethod(); } + if (null !== $tag->getDefaultPriorityMethod()) { + $content['default_priority_method'] = $tag->getDefaultPriorityMethod(); + } } return new TaggedValue($value instanceof TaggedIteratorArgument ? 'tagged_iterator' : 'tagged_locator', $content); diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php index 9ad4960f8967e..2fd74c0e3310f 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php @@ -128,9 +128,9 @@ function tagged(string $tag, string $indexAttribute = null, string $defaultIndex /** * Creates a lazy iterator by tag name. */ -function tagged_iterator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null): TaggedIteratorArgument +function tagged_iterator(string $tag, string $indexAttribute = null, string $defaultIndexMethod = null, string $defaultPriorityMethod = null): TaggedIteratorArgument { - return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod); + return new TaggedIteratorArgument($tag, $indexAttribute, $defaultIndexMethod, false, $defaultPriorityMethod); } /** diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index e9f73a945d1bf..25886d2f50f5b 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -513,7 +513,7 @@ private function getArgumentsAsPhp(\DOMElement $node, string $name, string $file throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="%s" has no or empty "tag" attribute in "%s".', $name, $type, $file)); } - $arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag'), $arg->getAttribute('index-by') ?: null, $arg->getAttribute('default-index-method') ?: null, $forLocator); + $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); if ($forLocator) { $arguments[$key] = new ServiceLocatorArgument($arguments[$key]); diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index ea7eab1649905..8a47fae60bc18 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -721,11 +721,11 @@ private function resolveServices($value, string $file, bool $isParameter = false $forLocator = 'tagged_locator' === $value->getTag(); if (\is_array($argument) && isset($argument['tag']) && $argument['tag']) { - if ($diff = array_diff(array_keys($argument), ['tag', 'index_by', 'default_index_method'])) { - throw new InvalidArgumentException(sprintf('"!%s" tag contains unsupported key "%s"; supported ones are "tag", "index_by" and "default_index_method".', $value->getTag(), implode('"", "', $diff))); + if ($diff = array_diff(array_keys($argument), ['tag', 'index_by', 'default_index_method', 'default_priority_method'])) { + 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))); } - $argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator); + $argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator, $argument['default_priority_method'] ?? null); } elseif (\is_string($argument) && $argument) { $argument = new TaggedIteratorArgument($argument, null, null, $forLocator); } else { diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index 2da07fde4e2ee..b491196af75f5 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -236,6 +236,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php index 73c284f806a1d..747e0496ee9a4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php @@ -23,6 +23,7 @@ use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; use Symfony\Component\DependencyInjection\Tests\Fixtures\BarTagClass; use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedClass; +use Symfony\Component\DependencyInjection\Tests\Fixtures\FooBarTaggedForDefaultPriorityClass; use Symfony\Component\DependencyInjection\Tests\Fixtures\FooTagClass; /** @@ -289,6 +290,30 @@ public function testTaggedServiceWithIndexAttributeAndDefaultMethod() $this->assertSame(['bar_tab_class_with_defaultmethod' => $container->get(BarTagClass::class), 'foo' => $container->get(FooTagClass::class)], $param); } + public function testTaggedServiceWithDefaultPriorityMethod() + { + $container = new ContainerBuilder(); + $container->register(BarTagClass::class) + ->setPublic(true) + ->addTag('foo_bar') + ; + $container->register(FooTagClass::class) + ->setPublic(true) + ->addTag('foo_bar', ['foo' => 'foo']) + ; + $container->register(FooBarTaggedForDefaultPriorityClass::class) + ->addArgument(new TaggedIteratorArgument('foo_bar', null, null, false, 'getPriority')) + ->setPublic(true) + ; + + $container->compile(); + + $s = $container->get(FooBarTaggedForDefaultPriorityClass::class); + + $param = iterator_to_array($s->getParam()->getIterator()); + $this->assertSame([$container->get(FooTagClass::class), $container->get(BarTagClass::class)], $param); + } + public function testTaggedServiceLocatorWithIndexAttribute() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php index e6eaf8f5a9708..17f839fa56276 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php @@ -204,7 +204,7 @@ public function testDumpLoad() public function testTaggedArguments() { - $taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar'); + $taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar', false, 'getPriority'); $container = new ContainerBuilder(); $container->register('foo', 'Foo')->addTag('foo_tag'); $container->register('foo_tagged_iterator', 'Bar') diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php index 9b1ff3245f118..93b9e85783e62 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php @@ -99,7 +99,7 @@ public function testInlineServices() public function testTaggedArguments() { - $taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar'); + $taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', false, 'getPriority'); $container = new ContainerBuilder(); $container->register('foo_service', 'Foo')->addTag('foo'); $container->register('foo_service_tagged_iterator', 'Bar')->addArgument($taggedIterator); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/BarTagClass.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/BarTagClass.php index 9e065f6b102a9..158faa35247cd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/BarTagClass.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/BarTagClass.php @@ -13,4 +13,9 @@ public static function getFooBar() { return 'bar_tab_class_with_defaultmethod'; } + + public static function getPriority(): int + { + return 0; + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooBarTaggedForDefaultPriorityClass.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooBarTaggedForDefaultPriorityClass.php new file mode 100644 index 0000000000000..4dafc0a46589d --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooBarTaggedForDefaultPriorityClass.php @@ -0,0 +1,18 @@ +param = $param; + } + + public function getParam() + { + return $this->param; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooTagClass.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooTagClass.php index c1279b9a9feeb..75bbc2c21d9cc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooTagClass.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/FooTagClass.php @@ -8,4 +8,11 @@ public static function getDefaultFooName() { return 'foo_tag_class'; } + + public static function getPriority(): int + { + // Should be more than BarTagClass. More because this class is after + // BarTagClass (order by name). So we want to ensure it will be before it + return 20; + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_tagged_arguments.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_tagged_arguments.xml index fcf27a824963f..6992f8432430f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_tagged_arguments.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_tagged_arguments.xml @@ -6,10 +6,10 @@ - + - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_tagged_argument.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_tagged_argument.yml index 8c2cd59aaf8b3..2b76522827de5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_tagged_argument.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_with_tagged_argument.yml @@ -10,10 +10,10 @@ services: - { name: foo } foo_service_tagged_iterator: class: Bar - arguments: [!tagged_iterator { tag: foo, index_by: barfoo, default_index_method: foobar }] + arguments: [!tagged_iterator { tag: foo, index_by: barfoo, default_index_method: foobar, default_priority_method: getPriority }] foo_service_tagged_locator: class: Bar - arguments: [!tagged_locator { tag: foo, index_by: barfoo, default_index_method: foobar }] + arguments: [!tagged_locator { tag: foo, index_by: barfoo, default_index_method: foobar, default_priority_method: getPriority }] bar_service_tagged_locator: class: Bar arguments: [!tagged_locator foo] diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index ed6b74bbbdbdc..b5ca7c919cf52 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -327,10 +327,10 @@ public function testParseTaggedArgumentsWithIndexBy() $this->assertCount(1, $container->getDefinition('foo_tagged_iterator')->getArguments()); $this->assertCount(1, $container->getDefinition('foo_tagged_locator')->getArguments()); - $taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar'); + $taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar', false, 'getPriority'); $this->assertEquals($taggedIterator, $container->getDefinition('foo_tagged_iterator')->getArgument(0)); - $taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar', true); + $taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar', true, 'getPriority'); $this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('foo_tagged_locator')->getArgument(0)); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index fda3f0ddd2d5f..2e116132a35dd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -306,10 +306,10 @@ public function testTaggedArgumentsWithIndex() $this->assertCount(1, $container->getDefinition('foo_service_tagged_iterator')->getArguments()); $this->assertCount(1, $container->getDefinition('foo_service_tagged_locator')->getArguments()); - $taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar'); + $taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', false, 'getPriority'); $this->assertEquals($taggedIterator, $container->getDefinition('foo_service_tagged_iterator')->getArgument(0)); - $taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', true); + $taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', true, 'getPriority'); $this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('foo_service_tagged_locator')->getArgument(0)); if (is_subclass_of('Symfony\Component\Yaml\Exception\ExceptionInterface', 'Throwable')) { 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