diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index c13da0b280..02cb2d50ec 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * deprecated the concept of scopes * added `Definition::setShared()` and `Definition::isShared()` * added ResettableContainerInterface to be able to reset the container to release memory on shutdown + * added a way to define the priority of service decoration 2.7.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php index ef0a19c6a7..f80d705a9b 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php @@ -19,18 +19,28 @@ use Symfony\Component\DependencyInjection\Alias; * * @author Christophe Coevoet * @author Fabien Potencier + * @author Diego Saint Esteben */ class DecoratorServicePass implements CompilerPassInterface { public function process(ContainerBuilder $container) { + $definitions = new \SplPriorityQueue(); + $order = PHP_INT_MAX; + foreach ($container->getDefinitions() as $id => $definition) { if (!$decorated = $definition->getDecoratedService()) { continue; } + $definitions->insert(array($id, $definition), array($decorated[2], --$order)); + } + + foreach ($definitions as $arr) { + list($id, $definition) = $arr; + list($inner, $renamedId) = $definition->getDecoratedService(); + $definition->setDecoratedService(null); - list($inner, $renamedId) = $decorated; if (!$renamedId) { $renamedId = $id.'.inner'; } diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index 4e99cca77a..49d705cf7d 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -150,12 +150,13 @@ class Definition * * @param null|string $id The decorated service id, use null to remove decoration * @param null|string $renamedId The new decorated service id + * @param int $priority The priority of decoration * * @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) + public function setDecoratedService($id, $renamedId = null, $priority = 0) { if ($renamedId && $id == $renamedId) { throw new \InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id)); @@ -164,7 +165,7 @@ class Definition if (null === $id) { $this->decoratedService = null; } else { - $this->decoratedService = array($id, $renamedId); + $this->decoratedService = array($id, $renamedId, (int) $priority); } return $this; @@ -173,7 +174,7 @@ class Definition /** * Gets the service that decorates this service. * - * @return null|array An array composed of the decorated service id and the new id for it, null if no service is decorated + * @return null|array An array composed of the decorated service id, the new id for it and the priority of decoration, null if no service is decorated */ public function getDecoratedService() { diff --git a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php index f5a1485a5f..7764079d51 100644 --- a/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php +++ b/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php @@ -173,11 +173,11 @@ class DefinitionDecorator extends Definition /** * {@inheritdoc} */ - public function setDecoratedService($id, $renamedId = null) + public function setDecoratedService($id, $renamedId = null, $priority = 0) { $this->changes['decorated_service'] = true; - return parent::setDecoratedService($id, $renamedId); + return parent::setDecoratedService($id, $renamedId, $priority); } /** diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 4c49ceb3a8..c405c4ce90 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -149,11 +149,14 @@ class XmlDumper extends Dumper $service->setAttribute('lazy', 'true'); } if (null !== $decorated = $definition->getDecoratedService()) { - list($decorated, $renamedId) = $decorated; + list($decorated, $renamedId, $priority) = $decorated; $service->setAttribute('decorates', $decorated); if (null !== $renamedId) { $service->setAttribute('decoration-inner-name', $renamedId); } + if (0 !== $priority) { + $service->setAttribute('decoration-priority', $priority); + } } foreach ($definition->getTags() as $name => $tags) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 5cd6455bf6..3fac53e01c 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -141,11 +141,14 @@ class YamlDumper extends Dumper } if (null !== $decorated = $definition->getDecoratedService()) { - list($decorated, $renamedId) = $decorated; + list($decorated, $renamedId, $priority) = $decorated; $code .= sprintf(" decorates: %s\n", $decorated); if (null !== $renamedId) { $code .= sprintf(" decoration_inner_name: %s\n", $renamedId); } + if (0 !== $priority) { + $code .= sprintf(" decoration_priority: %s\n", $priority); + } } if ($callable = $definition->getFactory()) { diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 15b6e60366..de2ea9852a 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -245,7 +245,8 @@ class XmlFileLoader extends FileLoader if ($value = $service->getAttribute('decorates')) { $renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null; - $definition->setDecoratedService($value, $renameId); + $priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0; + $definition->setDecoratedService($value, $renameId, $priority); } return $definition; diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 3b4c5cda80..ce04d10afa 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -290,7 +290,8 @@ class YamlFileLoader extends FileLoader if (isset($service['decorates'])) { $renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null; - $definition->setDecoratedService($service['decorates'], $renameId); + $priority = isset($service['decoration_priority']) ? $service['decoration_priority'] : 0; + $definition->setDecoratedService($service['decorates'], $renameId, $priority); } $this->container->setDefinition($id, $definition); 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 11f869c04a..15bdd791dc 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 @@ -101,6 +101,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php index e17961ac99..952f7a0bd8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php @@ -73,6 +73,48 @@ class DecoratorServicePassTest extends \PHPUnit_Framework_TestCase $this->assertNull($fooExtendedDefinition->getDecoratedService()); } + public function testProcessWithPriority() + { + $container = new ContainerBuilder(); + $fooDefinition = $container + ->register('foo') + ->setPublic(false) + ; + $barDefinition = $container + ->register('bar') + ->setPublic(true) + ->setDecoratedService('foo') + ; + $bazDefinition = $container + ->register('baz') + ->setPublic(true) + ->setDecoratedService('foo', null, 5) + ; + $quxDefinition = $container + ->register('qux') + ->setPublic(true) + ->setDecoratedService('foo', null, 3) + ; + + $this->process($container); + + $this->assertEquals('bar', $container->getAlias('foo')); + $this->assertFalse($container->getAlias('foo')->isPublic()); + + $this->assertSame($fooDefinition, $container->getDefinition('baz.inner')); + $this->assertFalse($container->getDefinition('baz.inner')->isPublic()); + + $this->assertEquals('qux', $container->getAlias('bar.inner')); + $this->assertFalse($container->getAlias('bar.inner')->isPublic()); + + $this->assertEquals('baz', $container->getAlias('qux.inner')); + $this->assertFalse($container->getAlias('qux.inner')->isPublic()); + + $this->assertNull($barDefinition->getDecoratedService()); + $this->assertNull($bazDefinition->getDecoratedService()); + $this->assertNull($quxDefinition->getDecoratedService()); + } + protected function process(ContainerBuilder $container) { $repeatedPass = new DecoratorServicePass(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php index 9f1cc87b1d..2180483e2b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php @@ -241,7 +241,7 @@ class ResolveDefinitionTemplatesPassTest extends \PHPUnit_Framework_TestCase ->setDecoratedService('foo', 'foo_inner') ; - $this->assertEquals(array('foo', 'foo_inner'), $container->getDefinition('child1')->getDecoratedService()); + $this->assertEquals(array('foo', 'foo_inner', 0), $container->getDefinition('child1')->getDecoratedService()); } protected function process(ContainerBuilder $container) diff --git a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php index d67f1d7224..e8df122953 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -56,16 +56,23 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase public function testSetGetDecoratedService() { + $def = new Definition('stdClass'); + $this->assertNull($def->getDecoratedService()); + $def->setDecoratedService('foo', 'foo.renamed', 5); + $this->assertEquals(array('foo', 'foo.renamed', 5), $def->getDecoratedService()); + $def->setDecoratedService(null); + $this->assertNull($def->getDecoratedService()); + $def = new Definition('stdClass'); $this->assertNull($def->getDecoratedService()); $def->setDecoratedService('foo', 'foo.renamed'); - $this->assertEquals(array('foo', 'foo.renamed'), $def->getDecoratedService()); + $this->assertEquals(array('foo', 'foo.renamed', 0), $def->getDecoratedService()); $def->setDecoratedService(null); $this->assertNull($def->getDecoratedService()); $def = new Definition('stdClass'); $def->setDecoratedService('foo'); - $this->assertEquals(array('foo', null), $def->getDecoratedService()); + $this->assertEquals(array('foo', null, 0), $def->getDecoratedService()); $def->setDecoratedService(null); $this->assertNull($def->getDecoratedService()); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml index a1a320bd82..4595c668c7 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml @@ -47,6 +47,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml index 2186620045..6a377c91d3 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml @@ -26,6 +26,10 @@ services: decorator_service_with_name: decorates: decorated decoration_inner_name: decorated.pif-pouf + decorator_service_with_name_and_priority: + decorates: decorated + decoration_inner_name: decorated.pif-pouf + decoration_priority: 5 new_factory1: { class: FooBarClass, factory: factory} new_factory2: { class: FooBarClass, factory: [@baz, getClass]} new_factory3: { class: FooBarClass, factory: [BazClass, getInstance]} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php index 581dc675fb..b717113bb4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -242,8 +242,9 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foo', (string) $aliases['another_alias_for_foo']); $this->assertFalse($aliases['another_alias_for_foo']->isPublic()); - $this->assertEquals(array('decorated', null), $services['decorator_service']->getDecoratedService()); - $this->assertEquals(array('decorated', 'decorated.pif-pouf'), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', null, 0), $services['decorator_service']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 0), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService()); } public function testParsesTags() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php index 058f5b66df..45f1cb496d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -171,8 +171,9 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foo', (string) $aliases['another_alias_for_foo']); $this->assertFalse($aliases['another_alias_for_foo']->isPublic()); - $this->assertEquals(array('decorated', null), $services['decorator_service']->getDecoratedService()); - $this->assertEquals(array('decorated', 'decorated.pif-pouf'), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', null, 0), $services['decorator_service']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 0), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService()); } public function testLoadFactoryShortSyntax()