From 9afa5c916089ceee69af61dbef6442067332ec53 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Tue, 18 Oct 2022 23:12:56 +0200 Subject: [PATCH] [DI] Add support for tagged iterators/locators `exclude` option to xml and yaml --- .../DependencyInjection/CHANGELOG.md | 1 + .../DependencyInjection/Dumper/XmlDumper.php | 9 +++++++ .../DependencyInjection/Dumper/YamlDumper.php | 6 +++++ .../Loader/XmlFileLoader.php | 10 +++++++- .../Loader/YamlFileLoader.php | 6 ++--- .../schema/dic/services/services-1.0.xsd | 2 ++ .../Tests/Dumper/XmlDumperTest.php | 24 +++++++++++++++++++ .../Tests/Dumper/YamlDumperTest.php | 12 ++++++++++ .../xml/services_with_tagged_arguments.xml | 24 +++++++++++++++++++ .../yaml/services_with_tagged_argument.yml | 20 ++++++++++++++++ .../Tests/Loader/XmlFileLoaderTest.php | 8 +++++++ .../Tests/Loader/YamlFileLoaderTest.php | 8 +++++++ 12 files changed, 126 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 1b90158a98442..b4bed7f7eddc8 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -12,6 +12,7 @@ CHANGELOG * Change the signature of `ContainerAwareInterface::setContainer()` to `setContainer(?ContainerInterface)` * Deprecate calling `ContainerAwareTrait::setContainer()` without arguments * Deprecate using numeric parameter names + * Add support for tagged iterators/locators `exclude` option to the xml and yaml loaders/dumpers 6.1 --- diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index d384c4c08927c..3677617ba552e 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -287,6 +287,15 @@ private function convertParameters(array $parameters, string $type, \DOMElement $element->setAttribute('default-priority-method', $tag->getDefaultPriorityMethod()); } } + if ($excludes = $tag->getExclude()) { + if (1 === \count($excludes)) { + $element->setAttribute('exclude', $excludes[0]); + } else { + foreach ($excludes as $exclude) { + $element->appendChild($this->document->createElement('exclude', $exclude)); + } + } + } } elseif ($value instanceof IteratorArgument) { $element->setAttribute('type', 'iterator'); $this->convertParameters($value->getValues(), $type, $element, 'key'); diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index dd1fa0628945e..17d684f2be276 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -266,6 +266,12 @@ private function dumpValue(mixed $value): mixed $content['default_priority_method'] = $tag->getDefaultPriorityMethod(); } } + if ($excludes = $tag->getExclude()) { + if (!\is_array($content)) { + $content = ['tag' => $content]; + } + $content['exclude'] = 1 === \count($excludes) ? $excludes[0] : $excludes; + } return new TaggedValue($value instanceof TaggedIteratorArgument ? 'tagged_iterator' : 'tagged_locator', $content); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index c0fafa4fc54ee..47f627598cce6 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -538,7 +538,15 @@ 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, $arg->getAttribute('default-priority-method') ?: null); + $excludes = array_column($this->getChildren($arg, 'exclude'), 'nodeValue'); + if ($arg->hasAttribute('exclude')) { + if (\count($excludes) > 0) { + throw new InvalidArgumentException('You cannot use both the attribute "exclude" and tags at the same time.'); + } + $excludes = [$arg->getAttribute('exclude')]; + } + + $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, $excludes); 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 b7fdeae28c4ca..36e40de304a3c 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -832,11 +832,11 @@ private function resolveServices(mixed $value, string $file, bool $isParameter = $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', '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))); + if ($diff = array_diff(array_keys($argument), $supportedKeys = ['tag', 'index_by', 'default_index_method', 'default_priority_method', 'exclude'])) { + throw new InvalidArgumentException(sprintf('"!%s" tag contains unsupported key "%s"; supported ones are "%s".', $value->getTag(), implode('", "', $diff), implode('", "', $supportedKeys))); } - $argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator, $argument['default_priority_method'] ?? null); + $argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator, $argument['default_priority_method'] ?? null, (array) ($argument['exclude'] ?? 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 ec642212c2a92..27d866c95f5a2 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 @@ -284,6 +284,7 @@ + @@ -294,6 +295,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php index 78bb529499290..9a4f52c252ce8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php @@ -203,16 +203,40 @@ public function testDumpLoad() public function testTaggedArguments() { $taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar', false, 'getPriority'); + $taggedIterator2 = new TaggedIteratorArgument('foo_tag', null, null, false, null, ['baz']); + $taggedIterator3 = new TaggedIteratorArgument('foo_tag', null, null, false, null, ['baz', 'qux']); + $container = new ContainerBuilder(); + $container->register('foo', 'Foo')->addTag('foo_tag'); + $container->register('baz', 'Baz')->addTag('foo_tag'); + $container->register('qux', 'Qux')->addTag('foo_tag'); + $container->register('foo_tagged_iterator', 'Bar') ->setPublic(true) ->addArgument($taggedIterator) ; + $container->register('foo2_tagged_iterator', 'Bar') + ->setPublic(true) + ->addArgument($taggedIterator2) + ; + $container->register('foo3_tagged_iterator', 'Bar') + ->setPublic(true) + ->addArgument($taggedIterator3) + ; + $container->register('foo_tagged_locator', 'Bar') ->setPublic(true) ->addArgument(new ServiceLocatorArgument($taggedIterator)) ; + $container->register('foo2_tagged_locator', 'Bar') + ->setPublic(true) + ->addArgument(new ServiceLocatorArgument($taggedIterator2)) + ; + $container->register('foo3_tagged_locator', 'Bar') + ->setPublic(true) + ->addArgument(new ServiceLocatorArgument($taggedIterator3)) + ; $dumper = new XmlDumper($container); $this->assertStringEqualsFile(self::$fixturesPath.'/xml/services_with_tagged_arguments.xml', $dumper->dump()); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php index a49b3c5e5b22d..07099b4cca457 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php @@ -112,10 +112,22 @@ public function testInlineServices() public function testTaggedArguments() { $taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', false, 'getPriority'); + $taggedIterator2 = new TaggedIteratorArgument('foo', null, null, false, null, ['baz']); + $taggedIterator3 = new TaggedIteratorArgument('foo', null, null, false, null, ['baz', 'qux']); + $container = new ContainerBuilder(); + $container->register('foo_service', 'Foo')->addTag('foo'); + $container->register('baz_service', 'Baz')->addTag('foo'); + $container->register('qux_service', 'Qux')->addTag('foo'); + $container->register('foo_service_tagged_iterator', 'Bar')->addArgument($taggedIterator); + $container->register('foo2_service_tagged_iterator', 'Bar')->addArgument($taggedIterator2); + $container->register('foo3_service_tagged_iterator', 'Bar')->addArgument($taggedIterator3); + $container->register('foo_service_tagged_locator', 'Bar')->addArgument(new ServiceLocatorArgument($taggedIterator)); + $container->register('foo2_service_tagged_locator', 'Bar')->addArgument(new ServiceLocatorArgument($taggedIterator2)); + $container->register('foo3_service_tagged_locator', 'Bar')->addArgument(new ServiceLocatorArgument($taggedIterator3)); $container->register('bar_service_tagged_locator', 'Bar')->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('foo'))); $dumper = new YamlDumper($container); 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 e4c62800245ce..e4043c7b17c46 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 @@ -5,11 +5,35 @@ + + + + + + + + + + + + baz + qux + + + + + + + + baz + qux + + 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 d3c1e591daf60..2c2cbc720f457 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 @@ -8,12 +8,32 @@ services: class: Foo tags: - foo + baz_service: + class: Baz + tags: + - foo + qux_service: + class: Qux + tags: + - foo foo_service_tagged_iterator: class: Bar arguments: [!tagged_iterator { tag: foo, index_by: barfoo, default_index_method: foobar, default_priority_method: getPriority }] + foo2_service_tagged_iterator: + class: Bar + arguments: [!tagged_iterator { tag: foo, exclude: baz }] + foo3_service_tagged_iterator: + class: Bar + arguments: [!tagged_iterator { tag: foo, exclude: [baz, qux] }] foo_service_tagged_locator: class: Bar arguments: [!tagged_locator { tag: foo, index_by: barfoo, default_index_method: foobar, default_priority_method: getPriority }] + foo2_service_tagged_locator: + class: Bar + arguments: [!tagged_locator { tag: foo, exclude: baz }] + foo3_service_tagged_locator: + class: Bar + arguments: [!tagged_locator { tag: foo, exclude: [baz, qux] }] 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 34f2b4ec0e1c6..5c4eddb46a24c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -404,9 +404,17 @@ public function testParseTaggedArgumentsWithIndexBy() $taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar', false, 'getPriority'); $this->assertEquals($taggedIterator, $container->getDefinition('foo_tagged_iterator')->getArgument(0)); + $taggedIterator2 = new TaggedIteratorArgument('foo_tag', null, null, false, null, ['baz']); + $this->assertEquals($taggedIterator2, $container->getDefinition('foo2_tagged_iterator')->getArgument(0)); + $taggedIterator3 = new TaggedIteratorArgument('foo_tag', null, null, false, null, ['baz', 'qux']); + $this->assertEquals($taggedIterator3, $container->getDefinition('foo3_tagged_iterator')->getArgument(0)); $taggedIterator = new TaggedIteratorArgument('foo_tag', 'barfoo', 'foobar', true, 'getPriority'); $this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('foo_tagged_locator')->getArgument(0)); + $taggedIterator2 = new TaggedIteratorArgument('foo_tag', 'foo_tag', 'getDefaultFooTagName', true, 'getDefaultFooTagPriority', ['baz']); + $this->assertEquals(new ServiceLocatorArgument($taggedIterator2), $container->getDefinition('foo2_tagged_locator')->getArgument(0)); + $taggedIterator3 = new TaggedIteratorArgument('foo_tag', 'foo_tag', 'getDefaultFooTagName', true, 'getDefaultFooTagPriority', ['baz', 'qux']); + $this->assertEquals(new ServiceLocatorArgument($taggedIterator3), $container->getDefinition('foo3_tagged_locator')->getArgument(0)); } public function testParseServiceClosure() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 1d417db8758af..722a7f81f1f66 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -392,9 +392,17 @@ public function testTaggedArgumentsWithIndex() $taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', false, 'getPriority'); $this->assertEquals($taggedIterator, $container->getDefinition('foo_service_tagged_iterator')->getArgument(0)); + $taggedIterator2 = new TaggedIteratorArgument('foo', null, null, false, null, ['baz']); + $this->assertEquals($taggedIterator2, $container->getDefinition('foo2_service_tagged_iterator')->getArgument(0)); + $taggedIterator3 = new TaggedIteratorArgument('foo', null, null, false, null, ['baz', 'qux']); + $this->assertEquals($taggedIterator3, $container->getDefinition('foo3_service_tagged_iterator')->getArgument(0)); $taggedIterator = new TaggedIteratorArgument('foo', 'barfoo', 'foobar', true, 'getPriority'); $this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('foo_service_tagged_locator')->getArgument(0)); + $taggedIterator2 = new TaggedIteratorArgument('foo', 'foo', 'getDefaultFooName', true, 'getDefaultFooPriority', ['baz']); + $this->assertEquals(new ServiceLocatorArgument($taggedIterator2), $container->getDefinition('foo2_service_tagged_locator')->getArgument(0)); + $taggedIterator3 = new TaggedIteratorArgument('foo', 'foo', 'getDefaultFooName', true, 'getDefaultFooPriority', ['baz', 'qux']); + $this->assertEquals(new ServiceLocatorArgument($taggedIterator3), $container->getDefinition('foo3_service_tagged_locator')->getArgument(0)); $taggedIterator = new TaggedIteratorArgument('foo', null, null, true); $this->assertEquals(new ServiceLocatorArgument($taggedIterator), $container->getDefinition('bar_service_tagged_locator')->getArgument(0));