From 8494c7cdbaa493c26eb7ba335002e4c8852a9317 Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Tue, 13 Oct 2020 08:11:13 +0200 Subject: [PATCH 1/4] Allow array attributes for service tags --- .../Configurator/DefaultsConfigurator.php | 17 ++++++--- .../Loader/Configurator/Traits/TagTrait.php | 17 ++++++--- .../Loader/XmlFileLoader.php | 35 ++++++++++++++++--- .../Loader/YamlFileLoader.php | 23 ++++++------ .../schema/dic/services/services-1.0.xsd | 10 ++++++ 5 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php index 49a92e5ce3a76..df9e6dde0f645 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php @@ -48,11 +48,7 @@ final public function tag(string $name, array $attributes = []): self throw new InvalidArgumentException('The tag name in "_defaults" must be a non-empty string.'); } - foreach ($attributes as $attribute => $value) { - if (null !== $value && !is_scalar($value)) { - throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type.', $name, $attribute)); - } - } + $this->recursiveValidateAttributes($name, $attributes); $this->definition->addTag($name, $attributes); @@ -66,4 +62,15 @@ final public function instanceof(string $fqcn): InstanceofConfigurator { return $this->parent->instanceof($fqcn); } + + private function recursiveValidateAttributes(string $name, array $attributes, string $prefix = ''): void + { + foreach ($attributes as $attribute => $value) { + if (\is_array($value)) { + $this->recursiveValidateAttributes($name, $attributes, $attribute.'.'); + } elseif (!is_scalar($value) && null !== $value) { + throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type or an array of scalar-type.', $name, $prefix.$attribute)); + } + } + } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php index f4d5f002cf87d..fd2aa818657b5 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php @@ -26,14 +26,21 @@ final public function tag(string $name, array $attributes = []): self throw new InvalidArgumentException(sprintf('The tag name for service "%s" must be a non-empty string.', $this->id)); } - foreach ($attributes as $attribute => $value) { - if (!is_scalar($value) && null !== $value) { - throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $this->id, $name, $attribute)); - } - } + $this->recursiveValidateAttributes($name, $attributes); $this->definition->addTag($name, $attributes); return $this; } + + private function recursiveValidateAttributes(string $name, array $attributes, string $prefix = ''): void + { + foreach ($attributes as $attribute => $value) { + if (\is_array($value)) { + $this->recursiveValidateAttributes($name, $attributes, $attribute.'.'); + } elseif (!is_scalar($value) && null !== $value) { + throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type or an array of scalar-types for service "%s", tag "%s", attribute "%s".', $this->id, $name, $prefix.$attribute)); + } + } + } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 75b24dceba22a..43cfa2edf0aa9 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -335,8 +335,12 @@ private function parseDefinition(\DOMElement $service, string $file, Definition $tags = $this->getChildren($service, 'tag'); foreach ($tags as $tag) { - $parameters = []; $tagName = $tag->nodeValue; + if ('' === $tagName && '' === $tagName = $tag->getAttribute('name')) { + throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', (string) $service->getAttribute('id'), $file)); + } + + $parameters = $this->getRecursiveTagAttributes($tag, sprintf('The attribute name of tag "%s" for service "%s" in %s must be a non-empty string.', $tagName, (string) $service->getAttribute('id'), $file)); foreach ($tag->attributes as $name => $node) { if ('name' === $name && '' === $tagName) { continue; @@ -349,10 +353,6 @@ private function parseDefinition(\DOMElement $service, string $file, Definition $parameters[$name] = XmlUtils::phpize($node->nodeValue); } - if ('' === $tagName && '' === $tagName = $tag->getAttribute('name')) { - throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', (string) $service->getAttribute('id'), $file)); - } - $definition->addTag($tagName, $parameters); } @@ -585,6 +585,31 @@ private function getChildren(\DOMNode $node, string $name): array return $children; } + private function getRecursiveTagAttributes(\DOMNode $node, string $missingName): array + { + $parameters = []; + $children = $this->getChildren($node, 'attribute'); + + foreach ($children as $childNode) { + $name = $childNode->getAttribute('name'); + if ('' === $name) { + throw new InvalidArgumentException($missingName); + } + + if ($this->getChildren($childNode, 'attribute')) { + $parameters[$childNode->getAttribute('name')] = $this->getRecursiveTagAttributes($childNode, $missingName); + } else { + if (false !== strpos($name, '-') && false === strpos($name, '_') && !\array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { + $parameters[$normalizedName] = XmlUtils::phpize($childNode->nodeValue); + } + // keep not normalized key + $parameters[$name] = XmlUtils::phpize($childNode->nodeValue); + } + } + + return $parameters; + } + /** * Validates a documents XML schema. * diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index bfd06d9e92b18..fcc2ff0666e5f 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -302,11 +302,7 @@ private function parseDefaults(array &$content, string $file): array throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in "%s".', $file)); } - foreach ($tag as $attribute => $value) { - if (!is_scalar($value) && null !== $value) { - throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in "%s". Check your YAML syntax.', $name, $attribute, $file)); - } - } + $this->recursiveValidateAttributes(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in "%s". Check your YAML syntax.', $name, '%s', $file), $tag); } } @@ -614,11 +610,7 @@ private function parseDefinition(string $id, $service, string $file, array $defa throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $id, $file)); } - foreach ($tag as $attribute => $value) { - if (!is_scalar($value) && null !== $value) { - throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in "%s". Check your YAML syntax.', $id, $name, $attribute, $file)); - } - } + $this->recursiveValidateAttributes(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in "%s". Check your YAML syntax.', $id, $name, '%s', $file), $tag); $definition->addTag($name, $tag); } @@ -960,4 +952,15 @@ private function checkDefinition(string $id, array $definition, string $file) } } } + + private function recursiveValidateAttributes(string $message, array $attributes, string $prefix = ''): void + { + foreach ($attributes as $attribute => $value) { + if (\is_array($value)) { + $this->recursiveValidateAttributes($message, $attributes, $attribute.'.'); + } elseif (!is_scalar($value) && null !== $value) { + throw new InvalidArgumentException(sprintf($message, $prefix.$attribute)); + } + } + } } 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 3c30002542e94..a39d8dfef5a0c 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 @@ -209,6 +209,9 @@ + + + @@ -226,6 +229,13 @@ + + + + + + + From c9ee2ddca995ffcbafabc4f672a542a05a052f08 Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Wed, 14 Jul 2021 17:39:47 +0200 Subject: [PATCH 2/4] Rename methods and arguments --- .../Loader/Configurator/DefaultsConfigurator.php | 8 ++++---- .../Loader/Configurator/Traits/TagTrait.php | 8 ++++---- .../DependencyInjection/Loader/XmlFileLoader.php | 6 +++--- .../DependencyInjection/Loader/YamlFileLoader.php | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php index df9e6dde0f645..4c5efbc7bf834 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php @@ -48,7 +48,7 @@ final public function tag(string $name, array $attributes = []): self throw new InvalidArgumentException('The tag name in "_defaults" must be a non-empty string.'); } - $this->recursiveValidateAttributes($name, $attributes); + $this->validateAttributes($name, $attributes); $this->definition->addTag($name, $attributes); @@ -63,13 +63,13 @@ final public function instanceof(string $fqcn): InstanceofConfigurator return $this->parent->instanceof($fqcn); } - private function recursiveValidateAttributes(string $name, array $attributes, string $prefix = ''): void + private function validateAttributes(string $tagName, array $attributes, string $prefix = ''): void { foreach ($attributes as $attribute => $value) { if (\is_array($value)) { - $this->recursiveValidateAttributes($name, $attributes, $attribute.'.'); + $this->validateAttributes($tagName, $attributes, $attribute.'.'); } elseif (!is_scalar($value) && null !== $value) { - throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type or an array of scalar-type.', $name, $prefix.$attribute)); + throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type or an array of scalar-type.', $tagName, $prefix.$attribute)); } } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php index fd2aa818657b5..bf8d7aafbb57e 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php @@ -26,20 +26,20 @@ final public function tag(string $name, array $attributes = []): self throw new InvalidArgumentException(sprintf('The tag name for service "%s" must be a non-empty string.', $this->id)); } - $this->recursiveValidateAttributes($name, $attributes); + $this->validateAttributes($name, $attributes); $this->definition->addTag($name, $attributes); return $this; } - private function recursiveValidateAttributes(string $name, array $attributes, string $prefix = ''): void + private function validateAttributes(string $tagName, array $attributes, string $prefix = ''): void { foreach ($attributes as $attribute => $value) { if (\is_array($value)) { - $this->recursiveValidateAttributes($name, $attributes, $attribute.'.'); + $this->validateAttributes($tagName, $attributes, $attribute.'.'); } elseif (!is_scalar($value) && null !== $value) { - throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type or an array of scalar-types for service "%s", tag "%s", attribute "%s".', $this->id, $name, $prefix.$attribute)); + throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type or an array of scalar-types for service "%s", tag "%s", attribute "%s".', $this->id, $tagName, $prefix.$attribute)); } } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 43cfa2edf0aa9..3575c0d10a39d 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -340,7 +340,7 @@ private function parseDefinition(\DOMElement $service, string $file, Definition throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', (string) $service->getAttribute('id'), $file)); } - $parameters = $this->getRecursiveTagAttributes($tag, sprintf('The attribute name of tag "%s" for service "%s" in %s must be a non-empty string.', $tagName, (string) $service->getAttribute('id'), $file)); + $parameters = $this->getTagAttributes($tag, sprintf('The attribute name of tag "%s" for service "%s" in %s must be a non-empty string.', $tagName, (string) $service->getAttribute('id'), $file)); foreach ($tag->attributes as $name => $node) { if ('name' === $name && '' === $tagName) { continue; @@ -585,7 +585,7 @@ private function getChildren(\DOMNode $node, string $name): array return $children; } - private function getRecursiveTagAttributes(\DOMNode $node, string $missingName): array + private function getTagAttributes(\DOMNode $node, string $missingName): array { $parameters = []; $children = $this->getChildren($node, 'attribute'); @@ -597,7 +597,7 @@ private function getRecursiveTagAttributes(\DOMNode $node, string $missingName): } if ($this->getChildren($childNode, 'attribute')) { - $parameters[$childNode->getAttribute('name')] = $this->getRecursiveTagAttributes($childNode, $missingName); + $parameters[$childNode->getAttribute('name')] = $this->getTagAttributes($childNode, $missingName); } else { if (false !== strpos($name, '-') && false === strpos($name, '_') && !\array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { $parameters[$normalizedName] = XmlUtils::phpize($childNode->nodeValue); diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index fcc2ff0666e5f..e9d6de2287357 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -302,7 +302,7 @@ private function parseDefaults(array &$content, string $file): array throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in "%s".', $file)); } - $this->recursiveValidateAttributes(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in "%s". Check your YAML syntax.', $name, '%s', $file), $tag); + $this->validateAttributes(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in "%s". Check your YAML syntax.', $name, '%s', $file), $tag); } } @@ -610,7 +610,7 @@ private function parseDefinition(string $id, $service, string $file, array $defa throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', $id, $file)); } - $this->recursiveValidateAttributes(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in "%s". Check your YAML syntax.', $id, $name, '%s', $file), $tag); + $this->validateAttributes(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in "%s". Check your YAML syntax.', $id, $name, '%s', $file), $tag); $definition->addTag($name, $tag); } @@ -953,11 +953,11 @@ private function checkDefinition(string $id, array $definition, string $file) } } - private function recursiveValidateAttributes(string $message, array $attributes, string $prefix = ''): void + private function validateAttributes(string $message, array $attributes, string $prefix = ''): void { foreach ($attributes as $attribute => $value) { if (\is_array($value)) { - $this->recursiveValidateAttributes($message, $attributes, $attribute.'.'); + $this->validateAttributes($message, $attributes, $attribute.'.'); } elseif (!is_scalar($value) && null !== $value) { throw new InvalidArgumentException(sprintf($message, $prefix.$attribute)); } From 3d7d4f8b7b86e3295aae7d76668ff9a08a42eebd Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Wed, 14 Jul 2021 18:25:04 +0200 Subject: [PATCH 3/4] Fixed recursion and added tests for YamlFileLoader --- .../Loader/YamlFileLoader.php | 2 +- .../{badtag3.yml => tag_array_arguments.yml} | 2 +- .../Tests/Loader/YamlFileLoaderTest.php | 16 +++++++--------- 3 files changed, 9 insertions(+), 11 deletions(-) rename src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/{badtag3.yml => tag_array_arguments.yml} (74%) diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index e9d6de2287357..39fc2e410602b 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -957,7 +957,7 @@ private function validateAttributes(string $message, array $attributes, string $ { foreach ($attributes as $attribute => $value) { if (\is_array($value)) { - $this->validateAttributes($message, $attributes, $attribute.'.'); + $this->validateAttributes($message, $value, $attribute.'.'); } elseif (!is_scalar($value) && null !== $value) { throw new InvalidArgumentException(sprintf($message, $prefix.$attribute)); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag3.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/tag_array_arguments.yml similarity index 74% rename from src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag3.yml rename to src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/tag_array_arguments.yml index 72ec4e8f0bc6f..46c488bca1e9b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag3.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/tag_array_arguments.yml @@ -2,5 +2,5 @@ services: foo_service: class: FooClass tags: - # tag-attribute is not a scalar + # tag-attribute is an array - { name: foo, bar: { foo: foo, bar: bar } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 55b0a8acb53c0..5a624e3d9a45d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -433,16 +433,14 @@ public function testNameOnlyTagsAreAllowedAsString() $this->assertCount(1, $container->getDefinition('foo_service')->getTag('foo')); } - public function testTagWithAttributeArrayThrowsException() + public function testTagWithAttributeArray() { - $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); - try { - $loader->load('badtag3.yml'); - $this->fail('->load() should throw an exception when a tag-attribute is not a scalar'); - } catch (\Exception $e) { - $this->assertInstanceOf(InvalidArgumentException::class, $e, '->load() throws an InvalidArgumentException if a tag-attribute is not a scalar'); - $this->assertStringStartsWith('A "tags" attribute must be of a scalar-type for service "foo_service", tag "foo", attribute "bar"', $e->getMessage(), '->load() throws an InvalidArgumentException if a tag-attribute is not a scalar'); - } + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('tag_array_arguments.yml'); + + $definition = $container->getDefinition('foo_service'); + $this->assertEquals(['foo' => [['bar' => ['foo' => 'foo', 'bar' => 'bar']]]], $definition->getTags()); } public function testLoadYamlOnlyWithKeys() From aeb2ce1a29fa438ac4af93e8c635705d61826214 Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Thu, 15 Jul 2021 09:23:31 +0200 Subject: [PATCH 4/4] Fixed XMLFileLoader and XSD and added test --- .../DependencyInjection/Loader/XmlFileLoader.php | 8 ++++---- .../Loader/schema/dic/services/services-1.0.xsd | 12 ++++-------- .../Fixtures/xml/services_with_array_tags.xml | 14 ++++++++++++++ .../Tests/Loader/XmlFileLoaderTest.php | 9 +++++++++ 4 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_array_tags.xml diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 3575c0d10a39d..2e92ab7190314 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -335,14 +335,14 @@ private function parseDefinition(\DOMElement $service, string $file, Definition $tags = $this->getChildren($service, 'tag'); foreach ($tags as $tag) { - $tagName = $tag->nodeValue; - if ('' === $tagName && '' === $tagName = $tag->getAttribute('name')) { + $tagName = $tag->hasChildNodes() || '' === $tag->nodeValue ? $tag->getAttribute('name') : $tag->nodeValue; + if ('' === $tagName) { throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', (string) $service->getAttribute('id'), $file)); } $parameters = $this->getTagAttributes($tag, sprintf('The attribute name of tag "%s" for service "%s" in %s must be a non-empty string.', $tagName, (string) $service->getAttribute('id'), $file)); foreach ($tag->attributes as $name => $node) { - if ('name' === $name && '' === $tagName) { + if ('name' === $name) { continue; } @@ -597,7 +597,7 @@ private function getTagAttributes(\DOMNode $node, string $missingName): array } if ($this->getChildren($childNode, 'attribute')) { - $parameters[$childNode->getAttribute('name')] = $this->getTagAttributes($childNode, $missingName); + $parameters[$name] = $this->getTagAttributes($childNode, $missingName); } else { if (false !== strpos($name, '-') && false === strpos($name, '_') && !\array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { $parameters[$normalizedName] = XmlUtils::phpize($childNode->nodeValue); 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 a39d8dfef5a0c..20f34b07b4c5e 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 @@ -208,15 +208,11 @@ - + - + - - - - - + @@ -229,7 +225,7 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_array_tags.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_array_tags.xml new file mode 100644 index 0000000000000..e6af07990101b --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_with_array_tags.xml @@ -0,0 +1,14 @@ + + + + + + bar + + bar + foo + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 93babd01043fe..137a695e7b0bf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -430,6 +430,15 @@ public function testParseServiceClosure() $this->assertEquals(new ServiceClosureArgument(new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), $container->getDefinition('foo')->getArgument(0)); } + public function testParseServiceTagsWithArrayAttributes() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_with_array_tags.xml'); + + $this->assertEquals(['foo_tag' => [['foo' => 'bar', 'bar' => ['foo' => 'bar', 'bar' => 'foo']]]], $container->getDefinition('foo')->getTags()); + } + public function testParseTagsWithoutNameThrowsException() { $this->expectException(InvalidArgumentException::class);