[DI] Allow definitions to inherit tags from parent context

This commit is contained in:
Nicolas Grekas 2016-12-28 14:17:18 +01:00
parent 05f24d5a6d
commit beec1cff8f
10 changed files with 111 additions and 27 deletions

View File

@ -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

View File

@ -22,6 +22,7 @@ use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
class ChildDefinition extends Definition
{
private $parent;
private $inheritTags = false;
private $changes = array();
/**
@ -54,6 +55,30 @@ class ChildDefinition extends Definition
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}
*/

View File

@ -216,6 +216,15 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface
$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;
}
}

View File

@ -150,6 +150,9 @@ class XmlFileLoader extends FileLoader
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 @@ class XmlFileLoader extends FileLoader
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 @@ class XmlFileLoader extends FileLoader
}
$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) {

View File

@ -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 @@ class YamlFileLoader extends FileLoader
@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 @@ class YamlFileLoader extends FileLoader
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 @@ class YamlFileLoader extends FileLoader
}
}
$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));

View File

@ -102,6 +102,7 @@
</xsd:choice>
<xsd:attribute name="public" type="boolean" />
<xsd:attribute name="autowire" type="boolean" />
<xsd:attribute name="inherit-tags" type="boolean" />
</xsd:complexType>
<xsd:complexType name="service">
@ -130,6 +131,7 @@
<xsd:attribute name="decoration-inner-name" type="xsd:string" />
<xsd:attribute name="decoration-priority" type="xsd:integer" />
<xsd:attribute name="autowire" type="boolean" />
<xsd:attribute name="inherit-tags" type="boolean" />
</xsd:complexType>
<xsd:complexType name="tag">

View File

@ -6,12 +6,13 @@
</defaults>
<service id="with_defaults" class="Foo" />
<service id="no_defaults" class="Foo" public="true" autowire="false">
<tag name="bar" />
<service id="no_defaults" class="Foo" public="true" autowire="false" inherit-tags="false">
</service>
<service id="no_defaults_child" class="Foo" parent="no_defaults">
<tag name="bar" />
</service>
<service id="with_defaults_child" class="Foo" parent="with_defaults" />
<service id="with_defaults_child" class="Foo" parent="with_defaults" public="true" inherit-tags="true">
<tag name="baz" />
</service>
</services>
</container>

View File

@ -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

View File

@ -625,19 +625,23 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
$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()

View File

@ -359,22 +359,26 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
$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());
}
/**