diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index c3c4ef5fd654..1beaaf0c53ce 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -48,27 +48,7 @@ public function process(ContainerBuilder $container) $this->formatter = $this->compiler->getLoggingFormatter(); $this->graph = $this->compiler->getServiceReferenceGraph(); - foreach ($container->getDefinitions() as $id => $definition) { - $this->currentId = $id; - - $definition->setArguments( - $this->inlineArguments($container, $definition->getArguments()) - ); - - $definition->setMethodCalls( - $this->inlineArguments($container, $definition->getMethodCalls()) - ); - - $definition->setProperties( - $this->inlineArguments($container, $definition->getProperties()) - ); - - $configurator = $this->inlineArguments($container, array($definition->getConfigurator())); - $definition->setConfigurator($configurator[0]); - - $factory = $this->inlineArguments($container, array($definition->getFactory())); - $definition->setFactory($factory[0]); - } + $container->setDefinitions($this->inlineArguments($container, $container->getDefinitions(), true)); } /** @@ -76,12 +56,16 @@ public function process(ContainerBuilder $container) * * @param ContainerBuilder $container The ContainerBuilder * @param array $arguments An array of arguments + * @param bool $isRoot If we are processing the root definitions or not * * @return array */ - private function inlineArguments(ContainerBuilder $container, array $arguments) + private function inlineArguments(ContainerBuilder $container, array $arguments, $isRoot = false) { foreach ($arguments as $k => $argument) { + if ($isRoot) { + $this->currentId = $k; + } if (is_array($argument)) { $arguments[$k] = $this->inlineArguments($container, $argument); } elseif ($argument instanceof Reference) { @@ -102,6 +86,12 @@ private function inlineArguments(ContainerBuilder $container, array $arguments) $argument->setArguments($this->inlineArguments($container, $argument->getArguments())); $argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls())); $argument->setProperties($this->inlineArguments($container, $argument->getProperties())); + + $configurator = $this->inlineArguments($container, array($argument->getConfigurator())); + $argument->setConfigurator($configurator[0]); + + $factory = $this->inlineArguments($container, array($argument->getFactory())); + $argument->setFactory($factory[0]); } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php index daaee8159d48..2d5711cdf2e4 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -21,12 +21,13 @@ * merged Definition instance. * * @author Johannes M. Schmitt + * @author Nicolas Grekas */ class ResolveDefinitionTemplatesPass implements CompilerPassInterface { - private $container; private $compiler; private $formatter; + private $currentId; /** * Process the ContainerBuilder to replace DefinitionDecorator instances with their real Definition instances. @@ -35,44 +36,80 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { - $this->container = $container; $this->compiler = $container->getCompiler(); $this->formatter = $this->compiler->getLoggingFormatter(); - foreach ($container->getDefinitions() as $id => $definition) { - // yes, we are specifically fetching the definition from the - // container to ensure we are not operating on stale data - $definition = $container->getDefinition($id); - if (!$definition instanceof DefinitionDecorator || $definition->isAbstract()) { - continue; - } + $container->setDefinitions($this->resolveArguments($container, $container->getDefinitions(), true)); + } - $this->resolveDefinition($id, $definition); + /** + * Resolves definition decorator arguments. + * + * @param ContainerBuilder $container The ContainerBuilder + * @param array $arguments An array of arguments + * @param bool $isRoot If we are processing the root definitions or not + * + * @return array + */ + private function resolveArguments(ContainerBuilder $container, array $arguments, $isRoot = false) + { + foreach ($arguments as $k => $argument) { + if ($isRoot) { + // yes, we are specifically fetching the definition from the + // container to ensure we are not operating on stale data + $arguments[$k] = $argument = $container->getDefinition($k); + $this->currentId = $k; + } + if (is_array($argument)) { + $arguments[$k] = $this->resolveArguments($container, $argument); + } elseif ($argument instanceof Definition) { + if ($argument instanceof DefinitionDecorator) { + $arguments[$k] = $argument = $this->resolveDefinition($container, $argument); + if ($isRoot) { + $container->setDefinition($k, $argument); + } + } + $argument->setArguments($this->resolveArguments($container, $argument->getArguments())); + $argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls())); + $argument->setProperties($this->resolveArguments($container, $argument->getProperties())); + + $configurator = $this->resolveArguments($container, array($argument->getConfigurator())); + $argument->setConfigurator($configurator[0]); + + $factory = $this->resolveArguments($container, array($argument->getFactory())); + $argument->setFactory($factory[0]); + } } + + return $arguments; } /** * Resolves the definition. * - * @param string $id The definition identifier + * @param ContainerBuilder $container The ContainerBuilder * @param DefinitionDecorator $definition * * @return Definition * * @throws \RuntimeException When the definition is invalid */ - private function resolveDefinition($id, DefinitionDecorator $definition) + private function resolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition) { - if (!$this->container->hasDefinition($parent = $definition->getParent())) { - throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $id)); + if (!$container->hasDefinition($parent = $definition->getParent())) { + throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $this->currentId)); } - $parentDef = $this->container->getDefinition($parent); + $parentDef = $container->getDefinition($parent); if ($parentDef instanceof DefinitionDecorator) { - $parentDef = $this->resolveDefinition($parent, $parentDef); + $id = $this->currentId; + $this->currentId = $parent; + $parentDef = $this->resolveDefinition($container, $parentDef); + $container->setDefinition($parent, $parentDef); + $this->currentId = $id; } - $this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $id, $parent)); + $this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $this->currentId, $parent)); $def = new Definition(); // merge in parent definition @@ -156,9 +193,6 @@ private function resolveDefinition($id, DefinitionDecorator $definition) $def->setScope($definition->getScope(false), false); $def->setTags($definition->getTags()); - // set new definition on container - $this->container->setDefinition($id, $def); - return $def; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php index 0060b35e9a59..1487d94f5147 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php @@ -176,6 +176,42 @@ public function testSetLazyOnServiceIsParent() $this->assertTrue($container->getDefinition('child1')->isLazy()); } + public function testDeepDefinitionsResolving() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'parentClass'); + $container->register('sibling', 'siblingClass') + ->setConfigurator(new DefinitionDecorator('parent'), 'foo') + ->setFactory(array(new DefinitionDecorator('parent'), 'foo')) + ->addArgument(new DefinitionDecorator('parent')) + ->setProperty('prop', new DefinitionDecorator('parent')) + ->addMethodCall('meth', array(new DefinitionDecorator('parent'))) + ; + + $this->process($container); + + $configurator = $container->getDefinition('sibling')->getConfigurator(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($configurator)); + $this->assertSame('parentClass', $configurator->getClass()); + + $factory = $container->getDefinition('sibling')->getFactory(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($factory[0])); + $this->assertSame('parentClass', $factory[0]->getClass()); + + $argument = $container->getDefinition('sibling')->getArgument(0); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($argument)); + $this->assertSame('parentClass', $argument->getClass()); + + $properties = $container->getDefinition('sibling')->getProperties(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($properties['prop'])); + $this->assertSame('parentClass', $properties['prop']->getClass()); + + $methodCalls = $container->getDefinition('sibling')->getMethodCalls(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($methodCalls[0][1][0])); + $this->assertSame('parentClass', $methodCalls[0][1][0]->getClass()); + } + protected function process(ContainerBuilder $container) { $pass = new ResolveDefinitionTemplatesPass();