[DependencyInjection] Update dumpers and loaders, add unit tests

This commit is contained in:
Romain Neutron 2014-03-31 17:58:40 +02:00
parent 1eb1f4dd73
commit 140f807346
22 changed files with 316 additions and 13 deletions

View File

@ -11,7 +11,6 @@
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Alias;
@ -29,26 +28,27 @@ class DecoratorServicePass implements CompilerPassInterface
if (!$decorated = $definition->getDecoratedService()) {
continue;
}
$definition->setDecoratedService(null);
list ($decorated, $renamedId) = $decorated;
list ($inner, $renamedId) = $decorated;
if (!$renamedId) {
$renamedId = $id.'.inner';
}
// we create a new alias/service for the service we are replacing
// to be able to reference it in the new one
if ($container->hasAlias($decorated)) {
$alias = $container->getAlias($decorated);
if ($container->hasAlias($inner)) {
$alias = $container->getAlias($inner);
$public = $alias->isPublic();
$container->setAlias($renamedId, new Alias((string) $alias, false));
} else {
$definition = $container->getDefinition($decorated);
$definition = $container->getDefinition($inner);
$public = $definition->isPublic();
$definition->setPublic(false);
$container->setDefinition($renamedId, $definition);
}
$container->setAlias($decorated, new Alias($id, $public));
$container->setAlias($inner, new Alias($id, $public));
}
}
}

View File

@ -104,22 +104,32 @@ class Definition
/**
* Sets the service that this service is decorating.
*
* @param string $id The decorated service id
* @param string $renamedId The new decorated service id
* @param null|string $id The decorated service id, use null to remove decoration
* @param null|string $renamedId The new decorated service id
*
* @return Definition The current instance
*
* @throws InvalidArgumentException In case the decorated service id and the new decorated service id are equals.
*/
public function setDecoratedService($id, $renamedId = null)
{
if ($renamedId && $id == $renamedId) {
throw new \LogicException(sprintf('The decorated service parent name for "%s" must be different than the service name itself.', $id));
throw new \InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id));
}
$this->decoratedService = array($id, $renamedId);
if (null === $id) {
$this->decoratedService = null;
} else {
$this->decoratedService = array($id, $renamedId);
}
return $this;
}
/**
* Gets the service that decorates this service.
*
* @return array An array composed of the decorated service id and the new id for it
* @return null|array An array composed of the decorated service id and the new id for it, null if no service is decorated
*/
public function getDecoratedService()
{

View File

@ -138,6 +138,13 @@ class XmlDumper extends Dumper
if ($definition->isLazy()) {
$service->setAttribute('lazy', 'true');
}
if (null !== $decorated = $definition->getDecoratedService()) {
list ($decorated, $renamedId) = $decorated;
$service->setAttribute('decorates', $decorated);
if (null !== $renamedId) {
$service->setAttribute('decoration-inner-name', $renamedId);
}
}
foreach ($definition->getTags() as $name => $tags) {
foreach ($tags as $attributes) {

View File

@ -139,6 +139,14 @@ class YamlDumper extends Dumper
$code .= sprintf(" scope: %s\n", $scope);
}
if (null !== $decorated = $definition->getDecoratedService()) {
list ($decorated, $renamedId) = $decorated;
$code .= sprintf(" decorates: %s\n", $decorated);
if (null !== $renamedId) {
$code .= sprintf(" decoration-inner-name: %s\n", $renamedId);
}
}
if ($callable = $definition->getConfigurator()) {
if (is_array($callable)) {
if ($callable[0] instanceof Reference) {

View File

@ -197,6 +197,11 @@ class XmlFileLoader extends FileLoader
$definition->addTag((string) $tag['name'], $parameters);
}
if (isset($service['decorates'])) {
$renameId = isset($service['decoration-inner-name']) ? (string) $service['decoration-inner-name'] : null;
$definition->setDecoratedService((string) $service['decorates'], $renameId);
}
$this->container->setDefinition($id, $definition);
}

View File

@ -234,6 +234,11 @@ class YamlFileLoader extends FileLoader
}
}
if (isset($service['decorates'])) {
$renameId = isset($service['decoration-inner-name']) ? $service['decoration-inner-name'] : null;
$definition->setDecoratedService($service['decorates'], $renameId);
}
$this->container->setDefinition($id, $definition);
}

View File

@ -94,6 +94,8 @@
<xsd:attribute name="factory-service" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="parent" type="xsd:string" />
<xsd:attribute name="decorates" type="xsd:string" />
<xsd:attribute name="decoration-inner-name" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="tag">

View File

@ -0,0 +1,81 @@
<?php
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\DecoratorServicePass;
class DecoratorServicePassTest extends \PHPUnit_Framework_TestCase
{
public function testProcessWithoutAlias()
{
$container = new ContainerBuilder();
$fooDefinition = $container
->register('foo')
->setPublic(false)
;
$fooExtendedDefinition = $container
->register('foo.extended')
->setPublic(true)
->setDecoratedService('foo')
;
$barDefinition = $container
->register('bar')
->setPublic(true)
;
$barExtendedDefinition = $container
->register('bar.extended')
->setPublic(true)
->setDecoratedService('bar', 'bar.yoo')
;
$this->process($container);
$this->assertEquals('foo.extended', $container->getAlias('foo'));
$this->assertFalse($container->getAlias('foo')->isPublic());
$this->assertEquals('bar.extended', $container->getAlias('bar'));
$this->assertTrue($container->getAlias('bar')->isPublic());
$this->assertSame($fooDefinition, $container->getDefinition('foo.extended.inner'));
$this->assertFalse($container->getDefinition('foo.extended.inner')->isPublic());
$this->assertSame($barDefinition, $container->getDefinition('bar.yoo'));
$this->assertFalse($container->getDefinition('bar.yoo')->isPublic());
$this->assertNull($fooExtendedDefinition->getDecoratedService());
$this->assertNull($barExtendedDefinition->getDecoratedService());
}
public function testProcessWithAlias()
{
$container = new ContainerBuilder();
$container
->register('foo')
->setPublic(true)
;
$container->setAlias('foo.alias', new Alias('foo', false));
$fooExtendedDefinition = $container
->register('foo.extended')
->setPublic(true)
->setDecoratedService('foo.alias')
;
$this->process($container);
$this->assertEquals('foo.extended', $container->getAlias('foo.alias'));
$this->assertFalse($container->getAlias('foo.alias')->isPublic());
$this->assertEquals('foo', $container->getAlias('foo.extended.inner'));
$this->assertFalse($container->getAlias('foo.extended.inner')->isPublic());
$this->assertNull($fooExtendedDefinition->getDecoratedService());
}
protected function process(ContainerBuilder $container)
{
$repeatedPass = new DecoratorServicePass();
$repeatedPass->process($container);
}
}

View File

@ -62,6 +62,26 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('foo', $def->getClass(), '->getClass() returns the class name');
}
public function testSetGetDecoratedService()
{
$def = new Definition('stdClass');
$this->assertNull($def->getDecoratedService());
$def->setDecoratedService('foo', 'foo.renamed');
$this->assertEquals(array('foo', 'foo.renamed'), $def->getDecoratedService());
$def->setDecoratedService(null);
$this->assertNull($def->getDecoratedService());
$def = new Definition('stdClass');
$def->setDecoratedService('foo');
$this->assertEquals(array('foo', null), $def->getDecoratedService());
$def->setDecoratedService(null);
$this->assertNull($def->getDecoratedService());
$def = new Definition('stdClass');
$this->setExpectedException('InvalidArgumentException', 'The decorated service inner name for "foo" must be different than the service name itself.');
$def->setDecoratedService('foo', 'foo');
}
/**
* @covers Symfony\Component\DependencyInjection\Definition::setArguments
* @covers Symfony\Component\DependencyInjection\Definition::getArguments

View File

@ -66,7 +66,7 @@ class XmlDumperTest extends \PHPUnit_Framework_TestCase
public function testDumpAnonymousServices()
{
include self::$fixturesPath.'/containers/container11.php';
$container = include self::$fixturesPath.'/containers/container11.php';
$dumper = new XmlDumper($container);
$this->assertEquals("<?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\">
@ -87,7 +87,7 @@ class XmlDumperTest extends \PHPUnit_Framework_TestCase
public function testDumpEntities()
{
include self::$fixturesPath.'/containers/container12.php';
$container = include self::$fixturesPath.'/containers/container12.php';
$dumper = new XmlDumper($container);
$this->assertEquals("<?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\">
@ -100,4 +100,35 @@ class XmlDumperTest extends \PHPUnit_Framework_TestCase
</container>
", $dumper->dump());
}
/**
* @dataProvider provideDecoratedServicesData
*/
public function testDumpDecoratedServices($expectedXmlDump, $container)
{
$dumper = new XmlDumper($container);
$this->assertEquals($expectedXmlDump, $dumper->dump());
}
public function provideDecoratedServicesData()
{
$fixturesPath = realpath(__DIR__.'/../Fixtures/');
return array(
array("<?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>
<service id=\"foo\" class=\"FooClass\Foo\" decorates=\"bar\" decoration-inner-name=\"bar.woozy\"/>
</services>
</container>
", include $fixturesPath.'/containers/container15.php'),
array("<?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>
<service id=\"foo\" class=\"FooClass\Foo\" decorates=\"bar\"/>
</services>
</container>
", include $fixturesPath.'/containers/container16.php'),
);
}
}

View File

@ -0,0 +1,11 @@
<?php
use Symfony\Component\DependencyInjection\ContainerBuilder;
$container = new ContainerBuilder();
$container
->register('foo', 'FooClass\\Foo')
->setDecoratedService('bar', 'bar.woozy')
;
return $container;

View File

@ -0,0 +1,11 @@
<?php
use Symfony\Component\DependencyInjection\ContainerBuilder;
$container = new ContainerBuilder();
$container
->register('foo', 'FooClass\\Foo')
->setDecoratedService('bar')
;
return $container;

View File

@ -92,5 +92,16 @@ $container
->register('configured_service', 'stdClass')
->setConfigurator(array(new Reference('configurator_service'), 'configureStdClass'))
;
$container
->register('decorated', 'stdClass')
;
$container
->register('decorator_service', 'stdClass')
->setDecoratedService('decorated')
;
$container
->register('decorator_service_with_name', 'stdClass')
->setDecoratedService('decorated', 'decorated.pif-pouf')
;
return $container;

View File

@ -16,6 +16,9 @@ digraph sc {
node_depends_on_request [label="depends_on_request\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_configurator_service [label="configurator_service\nConfClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_configured_service [label="configured_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_decorated [label="decorated\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_decorator_service [label="decorator_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_decorator_service_with_name [label="decorator_service_with_name\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"];
node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"];
node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"];

View File

@ -27,6 +27,9 @@ class ProjectServiceContainer extends Container
'baz' => 'getBazService',
'configurator_service' => 'getConfiguratorServiceService',
'configured_service' => 'getConfiguredServiceService',
'decorated' => 'getDecoratedService',
'decorator_service' => 'getDecoratorServiceService',
'decorator_service_with_name' => 'getDecoratorServiceWithNameService',
'depends_on_request' => 'getDependsOnRequestService',
'factory_service' => 'getFactoryServiceService',
'foo' => 'getFooService',
@ -96,6 +99,45 @@ class ProjectServiceContainer extends Container
return $instance;
}
/**
* Gets the 'decorated' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return stdClass A stdClass instance.
*/
protected function getDecoratedService()
{
return $this->services['decorated'] = new \stdClass();
}
/**
* Gets the 'decorator_service' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return stdClass A stdClass instance.
*/
protected function getDecoratorServiceService()
{
return $this->services['decorator_service'] = new \stdClass();
}
/**
* Gets the 'decorator_service_with_name' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return stdClass A stdClass instance.
*/
protected function getDecoratorServiceWithNameService()
{
return $this->services['decorator_service_with_name'] = new \stdClass();
}
/**
* Gets the 'depends_on_request' service.
*

View File

@ -37,6 +37,8 @@ class ProjectServiceContainer extends Container
'bar' => 'getBarService',
'baz' => 'getBazService',
'configured_service' => 'getConfiguredServiceService',
'decorator_service' => 'getDecoratorServiceService',
'decorator_service_with_name' => 'getDecoratorServiceWithNameService',
'depends_on_request' => 'getDependsOnRequestService',
'factory_service' => 'getFactoryServiceService',
'foo' => 'getFooService',
@ -49,6 +51,7 @@ class ProjectServiceContainer extends Container
$this->aliases = array(
'alias_for_alias' => 'foo',
'alias_for_foo' => 'foo',
'decorated' => 'decorator_service_with_name',
);
}
@ -108,6 +111,32 @@ class ProjectServiceContainer extends Container
return $instance;
}
/**
* Gets the 'decorator_service' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return stdClass A stdClass instance.
*/
protected function getDecoratorServiceService()
{
return $this->services['decorator_service'] = new \stdClass();
}
/**
* Gets the 'decorator_service_with_name' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return stdClass A stdClass instance.
*/
protected function getDecoratorServiceWithNameService()
{
return $this->services['decorator_service_with_name'] = new \stdClass();
}
/**
* Gets the 'depends_on_request' service.
*

View File

@ -50,5 +50,7 @@
<service id="another_alias_for_foo" alias="foo" public="false" />
<service id="factory_service" factory-method="getInstance" factory-service="baz_factory" />
<service id="request" class="Request" synthetic="true" synchronized="true" lazy="true"/>
<service id="decorator_service" decorates="decorated" />
<service id="decorator_service_with_name" decorates="decorated" decoration-inner-name="decorated.pif-pouf"/>
</services>
</container>

View File

@ -88,6 +88,9 @@
<service id="configured_service" class="stdClass">
<configurator service="configurator_service" method="configureStdClass"/>
</service>
<service id="decorated" class="stdClass"/>
<service id="decorator_service" class="stdClass" decorates="decorated"/>
<service id="decorator_service_with_name" class="stdClass" decorates="decorated" decoration-inner-name="decorated.pif-pouf"/>
<service id="alias_for_foo" alias="foo"/>
<service id="alias_for_alias" alias="foo"/>
</services>

View File

@ -30,3 +30,8 @@ services:
synthetic: true
synchronized: true
lazy: true
decorator_service:
decorates: decorated
decorator_service_with_name:
decorates: decorated
decoration-inner-name: decorated.pif-pouf

View File

@ -79,5 +79,14 @@ services:
configured_service:
class: stdClass
configurator: ['@configurator_service', configureStdClass]
decorated:
class: stdClass
decorator_service:
class: stdClass
decorates: decorated
decorator_service_with_name:
class: stdClass
decorates: decorated
decoration-inner-name: decorated.pif-pouf
alias_for_foo: @foo
alias_for_alias: @foo

View File

@ -229,6 +229,10 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertTrue(isset($aliases['another_alias_for_foo']));
$this->assertEquals('foo', (string) $aliases['another_alias_for_foo']);
$this->assertFalse($aliases['another_alias_for_foo']->isPublic());
$this->assertNull($services['request']->getDecoratedService());
$this->assertEquals(array('decorated', null), $services['decorator_service']->getDecoratedService());
$this->assertEquals(array('decorated', 'decorated.pif-pouf'), $services['decorator_service_with_name']->getDecoratedService());
}
public function testParsesTags()

View File

@ -131,6 +131,10 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertTrue(isset($aliases['another_alias_for_foo']));
$this->assertEquals('foo', (string) $aliases['another_alias_for_foo']);
$this->assertFalse($aliases['another_alias_for_foo']->isPublic());
$this->assertNull($services['request']->getDecoratedService());
$this->assertEquals(array('decorated', null), $services['decorator_service']->getDecoratedService());
$this->assertEquals(array('decorated', 'decorated.pif-pouf'), $services['decorator_service_with_name']->getDecoratedService());
}
public function testExtensions()