Skip to content

Commit

Permalink
[DI] Add "defaults" tag to XML services configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
ogizanagi authored and nicolas-grekas committed Jan 7, 2017
1 parent 7b4a18b commit 05f24d5
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 6 deletions.
68 changes: 64 additions & 4 deletions src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
Expand Up @@ -118,28 +118,74 @@ 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 "<defaults>" 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 "<autowire>" tags for tag "<defaults>" 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);

$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));

Expand All @@ -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));
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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')) {
Expand Down
Expand Up @@ -52,8 +52,9 @@
Enclosing element for the definition of all services
]]></xsd:documentation>
</xsd:annotation>
<xsd:choice minOccurs="1" maxOccurs="unbounded">
<xsd:element name="service" type="service" />
<xsd:choice maxOccurs="unbounded">
<xsd:element name="service" type="service" minOccurs="1" />
<xsd:element name="defaults" type="defaults" minOccurs="0" maxOccurs="1" />
</xsd:choice>
</xsd:complexType>

Expand Down Expand Up @@ -89,6 +90,20 @@
<xsd:attribute name="function" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="defaults">
<xsd:annotation>
<xsd:documentation><![CDATA[
Enclosing element for the service definitions' defaults for the current file
]]></xsd:documentation>
</xsd:annotation>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="autowire" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
</xsd:choice>
<xsd:attribute name="public" type="boolean" />
<xsd:attribute name="autowire" type="boolean" />
</xsd:complexType>

<xsd:complexType name="service">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="file" type="xsd:string" minOccurs="0" maxOccurs="1" />
Expand Down
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<defaults public="false" autowire="true">
<tag name="foo" />
</defaults>

<service id="with_defaults" class="Foo" />
<service id="no_defaults" class="Foo" public="true" autowire="false">
<tag name="bar" />
</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" />
</services>
</container>
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<defaults public="false">
<autowire>__construct</autowire>
</defaults>

<service id="with_defaults" class="Foo" />
<service id="no_defaults" class="Foo" public="true">
<autowire>setFoo</autowire>
</service>
<service id="no_defaults_child" class="Foo" parent="no_defaults">
<autowire>setFoo</autowire>
</service>
<service id="with_defaults_child" class="Foo" parent="with_defaults" />
</services>
</container>
Expand Up @@ -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());
}
}

0 comments on commit 05f24d5

Please sign in to comment.