From 7b4a18b04476a6cc606d083ca2d53b4cd28bd7fa Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 27 Dec 2016 18:36:50 +0100 Subject: [PATCH 1/3] [DI] Add "_defaults" key to Yaml services configuration --- .../Loader/YamlFileLoader.php | 83 +++++++++++++++---- .../Tests/Fixtures/yaml/services28.yml | 30 +++++++ .../Tests/Loader/YamlFileLoaderTest.php | 25 ++++++ 3 files changed, 122 insertions(+), 16 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 0cbe6f51299d3..6bf3aa05ce9be 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -148,9 +148,56 @@ private function parseDefinitions($content, $file) if (!is_array($content['services'])) { throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file)); } + if (isset($content['services']['_defaults'])) { + if (!is_array($defaults = $content['services']['_defaults'])) { + throw new InvalidArgumentException(sprintf('Service defaults must be an array, "%s" given in "%s".', gettype($defaults), $file)); + } + if (isset($defaults['alias']) || isset($defaults['class']) || isset($defaults['factory'])) { + @trigger_error('Giving a service the "_defaults" name is deprecated since Symfony 3.3 and will be forbidden in 4.0. Rename your service.', E_USER_DEPRECATED); + $defaults = array(); + } else { + $defaultKeys = array('public', 'tags', 'autowire'); + unset($content['services']['_defaults']); + + foreach ($defaults as $key => $default) { + if (!in_array($key, $defaultKeys)) { + throw new InvalidArgumentException(sprintf('The configuration key "%s" cannot be used to define a default value in "%s". Allowed keys are "%s".', $key, $file, implode('", "', $defaultKeys))); + } + } + if (isset($defaults['tags'])) { + if (!is_array($tags = $defaults['tags'])) { + throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in %s. Check your YAML syntax.', $file)); + } + + foreach ($tags as $tag) { + if (!is_array($tag)) { + $tag = array('name' => $tag); + } + + if (!isset($tag['name'])) { + throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in %s.', $file)); + } + $name = $tag['name']; + unset($tag['name']); + + if (!is_string($name) || '' === $name) { + 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)); + } + } + } + } + } + } else { + $defaults = array(); + } foreach ($content['services'] as $id => $service) { - $this->parseDefinition($id, $service, $file); + $this->parseDefinition($id, $service, $file, $defaults); } } @@ -160,10 +207,11 @@ private function parseDefinitions($content, $file) * @param string $id * @param array $service * @param string $file + * @param array $defaults * * @throws InvalidArgumentException When tags are invalid */ - private function parseDefinition($id, $service, $file) + private function parseDefinition($id, $service, $file, array $defaults) { if (is_string($service) && 0 === strpos($service, '@')) { $this->container->setAlias($id, substr($service, 1)); @@ -178,7 +226,7 @@ private function parseDefinition($id, $service, $file) static::checkDefinition($id, $service, $file); if (isset($service['alias'])) { - $public = !array_key_exists('public', $service) || (bool) $service['public']; + $public = array_key_exists('public', $service) ? (bool) $service['public'] : !empty($defaults['public']); $this->container->setAlias($id, new Alias($service['alias'], $public)); foreach ($service as $key => $value) { @@ -192,6 +240,7 @@ private function parseDefinition($id, $service, $file) if (isset($service['parent'])) { $definition = new ChildDefinition($service['parent']); + $defaults = array(); } else { $definition = new Definition(); } @@ -212,8 +261,9 @@ private function parseDefinition($id, $service, $file) $definition->setLazy($service['lazy']); } - if (isset($service['public'])) { - $definition->setPublic($service['public']); + $public = isset($service['public']) ? $service['public'] : (isset($defaults['public']) ? $defaults['public'] : null); + if (null !== $public) { + $definition->setPublic($public); } if (isset($service['abstract'])) { @@ -262,12 +312,13 @@ private function parseDefinition($id, $service, $file) } } - if (isset($service['tags'])) { - if (!is_array($service['tags'])) { + $tags = isset($service['tags']) ? $service['tags'] : (isset($defaults['tags']) ? $defaults['tags'] : null); + if (null !== $tags) { + if (!is_array($tags)) { throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); } - foreach ($service['tags'] as $tag) { + foreach ($tags as $tag) { if (!is_array($tag)) { $tag = array('name' => $tag); } @@ -275,14 +326,13 @@ private function parseDefinition($id, $service, $file) if (!isset($tag['name'])) { throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file)); } + $name = $tag['name']; + unset($tag['name']); - if (!is_string($tag['name']) || '' === $tag['name']) { + if (!is_string($name) || '' === $name) { throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', $id, $file)); } - $name = $tag['name']; - unset($tag['name']); - 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)); @@ -303,11 +353,12 @@ private function parseDefinition($id, $service, $file) $definition->setDecoratedService($service['decorates'], $renameId, $priority); } - if (isset($service['autowire'])) { - if (is_array($service['autowire'])) { - $definition->setAutowiredMethods($service['autowire']); + $autowire = isset($service['autowire']) ? $service['autowire'] : (isset($defaults['autowire']) ? $defaults['autowire'] : null); + if (null !== $autowire) { + if (is_array($autowire)) { + $definition->setAutowiredMethods($autowire); } else { - $definition->setAutowired($service['autowire']); + $definition->setAutowired($autowire); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml new file mode 100644 index 0000000000000..c03e4e9cafba4 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml @@ -0,0 +1,30 @@ +services: + _defaults: + public: false + autowire: true + tags: + - name: foo + + with_defaults: + class: Foo + + with_null: + class: Foo + public: ~ + autowire: ~ + tags: ~ + + no_defaults: + class: Foo + public: true + autowire: false + tags: [] + + no_defaults_child: + parent: no_defaults + autowire: ~ + tags: + - name: bar + + with_defaults_child: + parent: with_defaults diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index d0390ce314f57..efa915b0f0d57 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -352,6 +352,31 @@ public function testClassFromId() $this->assertEquals(CaseSensitiveClass::class, $container->getDefinition(CaseSensitiveClass::class)->getClass()); } + public function testDefaults() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services28.yml'); + + $this->assertFalse($container->getDefinition('with_defaults')->isPublic()); + $this->assertFalse($container->getDefinition('with_null')->isPublic()); + $this->assertTrue($container->getDefinition('no_defaults')->isPublic()); + $this->assertTrue($container->getDefinition('no_defaults_child')->isPublic()); + $this->assertArrayNotHasKey('public', $container->getDefinition('no_defaults_child')->getChanges()); + + $this->assertSame(array('foo' => array(array())), $container->getDefinition('with_defaults')->getTags()); + $this->assertSame(array('foo' => array(array())), $container->getDefinition('with_null')->getTags()); + $this->assertSame(array(), $container->getDefinition('no_defaults')->getTags()); + $this->assertSame(array('bar' => array(array())), $container->getDefinition('no_defaults_child')->getTags()); + $this->assertSame(array(), $container->getDefinition('with_defaults_child')->getTags()); + + $this->assertTrue($container->getDefinition('with_defaults')->isAutowired()); + $this->assertTrue($container->getDefinition('with_null')->isAutowired()); + $this->assertFalse($container->getDefinition('no_defaults')->isAutowired()); + $this->assertFalse($container->getDefinition('no_defaults_child')->isAutowired()); + $this->assertArrayNotHasKey('autowire', $container->getDefinition('no_defaults_child')->getChanges()); + } + /** * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException * @expectedExceptionMessage The value of the "decorates" option for the "bar" service must be the id of the service without the "@" prefix (replace "@foo" with "foo"). From 05f24d5a6dace0b7994d16057fa4417f1b81fe69 Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Wed, 28 Dec 2016 11:18:50 +0100 Subject: [PATCH 2/3] [DI] Add "defaults" tag to XML services configuration --- .../Loader/XmlFileLoader.php | 68 +++++++++++++++++-- .../schema/dic/services/services-1.0.xsd | 19 +++++- .../Tests/Fixtures/xml/services29.xml | 17 +++++ .../Tests/Fixtures/xml/services30.xml | 17 +++++ .../Tests/Loader/XmlFileLoaderTest.php | 34 ++++++++++ 5 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services30.xml diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index ab06a0c0af18c..e77952061961a 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -118,21 +118,65 @@ private function parseDefinitions(\DOMDocument $xml, $file) } foreach ($services as $service) { - if (null !== $definition = $this->parseDefinition($service, $file)) { + if (null !== $definition = $this->parseDefinition($service, $file, $this->getServiceDefaults($xml, $file))) { $this->container->setDefinition((string) $service->getAttribute('id'), $definition); } } } + /** + * Get service defaults. + * + * @return array + */ + private function getServiceDefaults(\DOMDocument $xml, $file) + { + $xpath = new \DOMXPath($xml); + $xpath->registerNamespace('container', self::NS); + + if (null === $defaultsNode = $xpath->query('//container:services/container:defaults')->item(0)) { + return array(); + } + $defaults = array( + 'tags' => $this->getChildren($defaultsNode, 'tag'), + 'autowire' => $this->getChildren($defaultsNode, 'autowire'), + ); + + foreach ($defaults['tags'] as $tag) { + if ('' === $tag->getAttribute('name')) { + throw new InvalidArgumentException(sprintf('The tag name for tag "" in %s must be a non-empty string.', $file)); + } + } + if ($defaultsNode->hasAttribute('public')) { + $defaults['public'] = XmlUtils::phpize($defaultsNode->getAttribute('public')); + } + if (!$defaultsNode->hasAttribute('autowire')) { + foreach ($defaults['autowire'] as $k => $v) { + $defaults['autowire'][$k] = $v->textContent; + } + + return $defaults; + } + if ($defaults['autowire']) { + throw new InvalidArgumentException(sprintf('The "autowire" attribute cannot be used together with "" tags for tag "" in %s.', $file)); + } + if (XmlUtils::phpize($defaultsNode->getAttribute('autowire'))) { + $defaults['autowire'][] = '__construct'; + } + + return $defaults; + } + /** * Parses an individual Definition. * * @param \DOMElement $service * @param string $file + * @param array $defaults * * @return Definition|null */ - private function parseDefinition(\DOMElement $service, $file) + private function parseDefinition(\DOMElement $service, $file, array $defaults = array()) { if ($alias = $service->getAttribute('alias')) { $this->validateAlias($service, $file); @@ -140,6 +184,8 @@ private function parseDefinition(\DOMElement $service, $file) $public = true; if ($publicAttr = $service->getAttribute('public')) { $public = XmlUtils::phpize($publicAttr); + } elseif (isset($defaults['public'])) { + $public = $defaults['public']; } $this->container->setAlias((string) $service->getAttribute('id'), new Alias($alias, $public)); @@ -148,11 +194,18 @@ private function parseDefinition(\DOMElement $service, $file) if ($parent = $service->getAttribute('parent')) { $definition = new ChildDefinition($parent); + $defaults = array(); } else { $definition = new Definition(); } - foreach (array('class', 'shared', 'public', 'synthetic', 'lazy', 'abstract') as $key) { + if ($publicAttr = $service->getAttribute('public')) { + $definition->setPublic(XmlUtils::phpize($publicAttr)); + } elseif (isset($defaults['public'])) { + $definition->setPublic($defaults['public']); + } + + foreach (array('class', 'shared', 'synthetic', 'lazy', 'abstract') as $key) { if ($value = $service->getAttribute($key)) { $method = 'set'.$key; $definition->$method(XmlUtils::phpize($value)); @@ -216,7 +269,12 @@ private function parseDefinition(\DOMElement $service, $file) $definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument')); } - foreach ($this->getChildren($service, 'tag') as $tag) { + $tags = $this->getChildren($service, 'tag'); + if (!$tags && !empty($defaults['tags'])) { + $tags = $defaults['tags']; + } + + foreach ($tags as $tag) { $parameters = array(); foreach ($tag->attributes as $name => $node) { if ('name' === $name) { @@ -252,6 +310,8 @@ private function parseDefinition(\DOMElement $service, $file) } $definition->setAutowiredMethods($autowireTags); + } elseif (!$service->hasAttribute('autowire') && !empty($defaults['autowire'])) { + $definition->setAutowiredMethods($defaults['autowire']); } if ($value = $service->getAttribute('decorates')) { 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 1e99d80ffe219..e3414776cf130 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 @@ -52,8 +52,9 @@ Enclosing element for the definition of all services ]]> - - + + + @@ -89,6 +90,20 @@ + + + + + + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml new file mode 100644 index 0000000000000..3755ba9b14a91 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services30.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services30.xml new file mode 100644 index 0000000000000..030f432b8bd26 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services30.xml @@ -0,0 +1,17 @@ + + + + + __construct + + + + + setFoo + + + setFoo + + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 7fda551371a27..c25fdfb68df47 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -617,4 +617,38 @@ public function testArgumentWithKeyOutsideCollection() $this->assertSame(array('type' => 'foo', 'bar'), $container->getDefinition('foo')->getArguments()); } + + public function testDefaults() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services29.xml'); + + $this->assertFalse($container->getDefinition('with_defaults')->isPublic()); + $this->assertTrue($container->getDefinition('no_defaults')->isPublic()); + $this->assertTrue($container->getDefinition('no_defaults_child')->isPublic()); + $this->assertArrayNotHasKey('public', $container->getDefinition('no_defaults_child')->getChanges()); + + $this->assertSame(array('foo' => array(array())), $container->getDefinition('with_defaults')->getTags()); + $this->assertSame(array('bar' => array(array())), $container->getDefinition('no_defaults')->getTags()); + $this->assertSame(array('bar' => array(array())), $container->getDefinition('no_defaults_child')->getTags()); + $this->assertSame(array(), $container->getDefinition('with_defaults_child')->getTags()); + + $this->assertTrue($container->getDefinition('with_defaults')->isAutowired()); + $this->assertFalse($container->getDefinition('no_defaults')->isAutowired()); + $this->assertFalse($container->getDefinition('no_defaults_child')->isAutowired()); + $this->assertArrayNotHasKey('autowire', $container->getDefinition('no_defaults_child')->getChanges()); + } + + public function testDefaultsWithAutowiredMethods() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services30.xml'); + + $this->assertSame(array('__construct'), $container->getDefinition('with_defaults')->getAutowiredMethods()); + $this->assertSame(array('setFoo'), $container->getDefinition('no_defaults')->getAutowiredMethods()); + $this->assertSame(array('setFoo'), $container->getDefinition('no_defaults_child')->getAutowiredMethods()); + $this->assertSame(array(), $container->getDefinition('with_defaults_child')->getAutowiredMethods()); + } } From beec1cff8f65d4ac86764807c7e03625183faa12 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 28 Dec 2016 14:17:18 +0100 Subject: [PATCH 3/3] [DI] Allow definitions to inherit tags from parent context --- .../DependencyInjection/CHANGELOG.md | 12 +++++---- .../DependencyInjection/ChildDefinition.php | 25 +++++++++++++++++++ .../ResolveDefinitionTemplatesPass.php | 9 +++++++ .../Loader/XmlFileLoader.php | 20 +++++++++++++-- .../Loader/YamlFileLoader.php | 21 ++++++++++++++-- .../schema/dic/services/services-1.0.xsd | 2 ++ .../Tests/Fixtures/xml/services29.xml | 7 +++--- .../Tests/Fixtures/yaml/services28.yml | 8 ++++-- .../Tests/Loader/XmlFileLoaderTest.php | 16 +++++++----- .../Tests/Loader/YamlFileLoaderTest.php | 18 +++++++------ 10 files changed, 111 insertions(+), 27 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 577bd28ac637b..b329162636cc2 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -4,11 +4,13 @@ CHANGELOG 3.3.0 ----- - * Add "iterator" argument type for lazy iteration over a set of values and services - - * Using the `PhpDumper` with an uncompiled `ContainerBuilder` is deprecated and - will not be supported anymore in 4.0. - + * added "iterator" argument type for lazy iteration over a set of values and services + * added "closure-proxy" argument type for turning services' methods into lazy callables + * added file-wide configurable defaults for service attributes "public", "tags", + "autowire" and a new "inherit-tags" + * made the "class" attribute optional, using the "id" as fallback + * using the `PhpDumper` with an uncompiled `ContainerBuilder` is deprecated and + will not be supported anymore in 4.0 * deprecated the `DefinitionDecorator` class in favor of `ChildDefinition` 3.2.0 diff --git a/src/Symfony/Component/DependencyInjection/ChildDefinition.php b/src/Symfony/Component/DependencyInjection/ChildDefinition.php index 4275b8adddbdc..cc85e55cf7786 100644 --- a/src/Symfony/Component/DependencyInjection/ChildDefinition.php +++ b/src/Symfony/Component/DependencyInjection/ChildDefinition.php @@ -22,6 +22,7 @@ class ChildDefinition extends Definition { private $parent; + private $inheritTags = false; private $changes = array(); /** @@ -54,6 +55,30 @@ public function getChanges() return $this->changes; } + /** + * Sets whether tags should be inherited from the parent or not. + * + * @param bool $boolean + * + * @return $this + */ + public function setInheritTags($boolean) + { + $this->inheritTags = (bool) $boolean; + + return $this; + } + + /** + * Returns whether tags should be inherited from the parent or not. + * + * @return bool + */ + public function getInheritTags() + { + return $this->inheritTags; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php index 3f854d3b8fb13..f8b1ab6464a34 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -216,6 +216,15 @@ private function doResolveDefinition(ContainerBuilder $container, ChildDefinitio $def->setShared($definition->isShared()); $def->setTags($definition->getTags()); + // append parent tags when inheriting is enabled + if ($definition->getInheritTags()) { + foreach ($parentDef->getTags() as $k => $v) { + foreach ($v as $v) { + $def->addTag($k, $v); + } + } + } + return $def; } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index e77952061961a..adb75d3b6a6cd 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -150,6 +150,9 @@ private function getServiceDefaults(\DOMDocument $xml, $file) if ($defaultsNode->hasAttribute('public')) { $defaults['public'] = XmlUtils::phpize($defaultsNode->getAttribute('public')); } + if ($defaultsNode->hasAttribute('inherit-tags')) { + $defaults['inherit-tags'] = XmlUtils::phpize($defaultsNode->getAttribute('inherit-tags')); + } if (!$defaultsNode->hasAttribute('autowire')) { foreach ($defaults['autowire'] as $k => $v) { $defaults['autowire'][$k] = $v->textContent; @@ -194,6 +197,12 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults = if ($parent = $service->getAttribute('parent')) { $definition = new ChildDefinition($parent); + + if ($value = $service->getAttribute('inherit-tags')) { + $definition->setInheritTags(XmlUtils::phpize($value)); + } elseif (isset($defaults['inherit-tags'])) { + $definition->setInheritTags($defaults['inherit-tags']); + } $defaults = array(); } else { $definition = new Definition(); @@ -270,8 +279,15 @@ private function parseDefinition(\DOMElement $service, $file, array $defaults = } $tags = $this->getChildren($service, 'tag'); - if (!$tags && !empty($defaults['tags'])) { - $tags = $defaults['tags']; + + if (empty($defaults['tags'])) { + // no-op + } elseif (!$value = $service->getAttribute('inherit-tags')) { + if (!$tags) { + $tags = $defaults['tags']; + } + } elseif (XmlUtils::phpize($value)) { + $tags = array_merge($tags, $defaults['tags']); } foreach ($tags as $tag) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 6bf3aa05ce9be..89e82f0c086b6 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -52,6 +52,7 @@ class YamlFileLoader extends FileLoader 'configurator' => 'configurator', 'calls' => 'calls', 'tags' => 'tags', + 'inherit_tags' => 'inherit_tags', 'decorates' => 'decorates', 'decoration_inner_name' => 'decoration_inner_name', 'decoration_priority' => 'decoration_priority', @@ -156,7 +157,7 @@ private function parseDefinitions($content, $file) @trigger_error('Giving a service the "_defaults" name is deprecated since Symfony 3.3 and will be forbidden in 4.0. Rename your service.', E_USER_DEPRECATED); $defaults = array(); } else { - $defaultKeys = array('public', 'tags', 'autowire'); + $defaultKeys = array('public', 'tags', 'inherit_tags', 'autowire'); unset($content['services']['_defaults']); foreach ($defaults as $key => $default) { @@ -240,6 +241,11 @@ private function parseDefinition($id, $service, $file, array $defaults) if (isset($service['parent'])) { $definition = new ChildDefinition($service['parent']); + + $inheritTag = isset($service['inherit_tags']) ? $service['inherit_tags'] : (isset($defaults['inherit_tags']) ? $defaults['inherit_tags'] : null); + if (null !== $inheritTag) { + $definition->setInheritTags($inheritTag); + } $defaults = array(); } else { $definition = new Definition(); @@ -312,7 +318,18 @@ private function parseDefinition($id, $service, $file, array $defaults) } } - $tags = isset($service['tags']) ? $service['tags'] : (isset($defaults['tags']) ? $defaults['tags'] : null); + $tags = isset($service['tags']) ? $service['tags'] : array(); + + if (!isset($defaults['tags'])) { + // no-op + } elseif (!isset($service['inherit_tags'])) { + if (!isset($service['tags'])) { + $tags = $defaults['tags']; + } + } elseif ($service['inherit_tags']) { + $tags = array_merge($tags, $defaults['tags']); + } + if (null !== $tags) { if (!is_array($tags)) { throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); 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 e3414776cf130..f9e9e700060ad 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 @@ -102,6 +102,7 @@ + @@ -130,6 +131,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml index 3755ba9b14a91..4ab511ed92dbf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services29.xml @@ -6,12 +6,13 @@ - - + - + + + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml index c03e4e9cafba4..5ba7ea98f20f7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services28.yml @@ -10,9 +10,9 @@ services: with_null: class: Foo - public: ~ + public: true autowire: ~ - tags: ~ + inherit_tags: false no_defaults: class: Foo @@ -28,3 +28,7 @@ services: with_defaults_child: parent: with_defaults + public: true + inherit_tags: true + tags: + - name: baz diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index c25fdfb68df47..659d82e2f7d11 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -625,19 +625,23 @@ public function testDefaults() $loader->load('services29.xml'); $this->assertFalse($container->getDefinition('with_defaults')->isPublic()); + $this->assertSame(array('foo' => array(array())), $container->getDefinition('with_defaults')->getTags()); + $this->assertTrue($container->getDefinition('with_defaults')->isAutowired()); + + $this->assertArrayNotHasKey('public', $container->getDefinition('no_defaults_child')->getChanges()); + $this->assertArrayNotHasKey('autowire', $container->getDefinition('no_defaults_child')->getChanges()); + + $container->compile(); + $this->assertTrue($container->getDefinition('no_defaults')->isPublic()); $this->assertTrue($container->getDefinition('no_defaults_child')->isPublic()); - $this->assertArrayNotHasKey('public', $container->getDefinition('no_defaults_child')->getChanges()); - $this->assertSame(array('foo' => array(array())), $container->getDefinition('with_defaults')->getTags()); - $this->assertSame(array('bar' => array(array())), $container->getDefinition('no_defaults')->getTags()); + $this->assertSame(array(), $container->getDefinition('no_defaults')->getTags()); $this->assertSame(array('bar' => array(array())), $container->getDefinition('no_defaults_child')->getTags()); - $this->assertSame(array(), $container->getDefinition('with_defaults_child')->getTags()); + $this->assertSame(array('baz' => array(array()), 'foo' => array(array())), $container->getDefinition('with_defaults_child')->getTags()); - $this->assertTrue($container->getDefinition('with_defaults')->isAutowired()); $this->assertFalse($container->getDefinition('no_defaults')->isAutowired()); $this->assertFalse($container->getDefinition('no_defaults_child')->isAutowired()); - $this->assertArrayNotHasKey('autowire', $container->getDefinition('no_defaults_child')->getChanges()); } public function testDefaultsWithAutowiredMethods() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index efa915b0f0d57..3d42ac34421cf 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -359,22 +359,26 @@ public function testDefaults() $loader->load('services28.yml'); $this->assertFalse($container->getDefinition('with_defaults')->isPublic()); - $this->assertFalse($container->getDefinition('with_null')->isPublic()); + $this->assertSame(array('foo' => array(array())), $container->getDefinition('with_defaults')->getTags()); + $this->assertTrue($container->getDefinition('with_defaults')->isAutowired()); + + $this->assertArrayNotHasKey('public', $container->getDefinition('no_defaults_child')->getChanges()); + $this->assertArrayNotHasKey('autowire', $container->getDefinition('no_defaults_child')->getChanges()); + + $container->compile(); + + $this->assertTrue($container->getDefinition('with_null')->isPublic()); $this->assertTrue($container->getDefinition('no_defaults')->isPublic()); $this->assertTrue($container->getDefinition('no_defaults_child')->isPublic()); - $this->assertArrayNotHasKey('public', $container->getDefinition('no_defaults_child')->getChanges()); - $this->assertSame(array('foo' => array(array())), $container->getDefinition('with_defaults')->getTags()); - $this->assertSame(array('foo' => array(array())), $container->getDefinition('with_null')->getTags()); + $this->assertSame(array(), $container->getDefinition('with_null')->getTags()); $this->assertSame(array(), $container->getDefinition('no_defaults')->getTags()); $this->assertSame(array('bar' => array(array())), $container->getDefinition('no_defaults_child')->getTags()); - $this->assertSame(array(), $container->getDefinition('with_defaults_child')->getTags()); + $this->assertSame(array('baz' => array(array()), 'foo' => array(array())), $container->getDefinition('with_defaults_child')->getTags()); - $this->assertTrue($container->getDefinition('with_defaults')->isAutowired()); $this->assertTrue($container->getDefinition('with_null')->isAutowired()); $this->assertFalse($container->getDefinition('no_defaults')->isAutowired()); $this->assertFalse($container->getDefinition('no_defaults_child')->isAutowired()); - $this->assertArrayNotHasKey('autowire', $container->getDefinition('no_defaults_child')->getChanges()); } /** 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