Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\DependencyInjection\Compiler; Chris@0: Chris@0: use Symfony\Component\DependencyInjection\Definition; Chris@0: use Symfony\Component\DependencyInjection\DefinitionDecorator; Chris@0: use Symfony\Component\DependencyInjection\ContainerBuilder; Chris@0: use Symfony\Component\DependencyInjection\Exception\ExceptionInterface; Chris@0: use Symfony\Component\DependencyInjection\Exception\RuntimeException; Chris@0: Chris@0: /** Chris@0: * This replaces all DefinitionDecorator instances with their equivalent fully Chris@0: * merged Definition instance. Chris@0: * Chris@0: * @author Johannes M. Schmitt Chris@0: * @author Nicolas Grekas Chris@0: */ Chris@0: class ResolveDefinitionTemplatesPass implements CompilerPassInterface Chris@0: { Chris@0: private $compiler; Chris@0: private $formatter; Chris@0: private $currentId; Chris@0: Chris@0: /** Chris@0: * Process the ContainerBuilder to replace DefinitionDecorator instances with their real Definition instances. Chris@0: * Chris@0: * @param ContainerBuilder $container Chris@0: */ Chris@0: public function process(ContainerBuilder $container) Chris@0: { Chris@0: $this->compiler = $container->getCompiler(); Chris@0: $this->formatter = $this->compiler->getLoggingFormatter(); Chris@0: Chris@0: $container->setDefinitions($this->resolveArguments($container, $container->getDefinitions(), true)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Resolves definition decorator arguments. Chris@0: * Chris@0: * @param ContainerBuilder $container The ContainerBuilder Chris@0: * @param array $arguments An array of arguments Chris@0: * @param bool $isRoot If we are processing the root definitions or not Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: private function resolveArguments(ContainerBuilder $container, array $arguments, $isRoot = false) Chris@0: { Chris@0: foreach ($arguments as $k => $argument) { Chris@0: if ($isRoot) { Chris@0: // yes, we are specifically fetching the definition from the Chris@0: // container to ensure we are not operating on stale data Chris@0: $arguments[$k] = $argument = $container->getDefinition($k); Chris@0: $this->currentId = $k; Chris@0: } Chris@0: if (is_array($argument)) { Chris@0: $arguments[$k] = $this->resolveArguments($container, $argument); Chris@0: } elseif ($argument instanceof Definition) { Chris@0: if ($argument instanceof DefinitionDecorator) { Chris@0: $arguments[$k] = $argument = $this->resolveDefinition($container, $argument); Chris@0: if ($isRoot) { Chris@0: $container->setDefinition($k, $argument); Chris@0: } Chris@0: } Chris@0: $argument->setArguments($this->resolveArguments($container, $argument->getArguments())); Chris@0: $argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls())); Chris@0: $argument->setProperties($this->resolveArguments($container, $argument->getProperties())); Chris@0: Chris@0: $configurator = $this->resolveArguments($container, array($argument->getConfigurator())); Chris@0: $argument->setConfigurator($configurator[0]); Chris@0: Chris@0: $factory = $this->resolveArguments($container, array($argument->getFactory())); Chris@0: $argument->setFactory($factory[0]); Chris@0: } Chris@0: } Chris@0: Chris@0: return $arguments; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Resolves the definition. Chris@0: * Chris@0: * @param ContainerBuilder $container The ContainerBuilder Chris@0: * @param DefinitionDecorator $definition Chris@0: * Chris@0: * @return Definition Chris@0: * Chris@0: * @throws \RuntimeException When the definition is invalid Chris@0: */ Chris@0: private function resolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition) Chris@0: { Chris@0: try { Chris@0: return $this->doResolveDefinition($container, $definition); Chris@0: } catch (ExceptionInterface $e) { Chris@0: $r = new \ReflectionProperty($e, 'message'); Chris@0: $r->setAccessible(true); Chris@0: $r->setValue($e, sprintf('Service "%s": %s', $this->currentId, $e->getMessage())); Chris@0: Chris@0: throw $e; Chris@0: } Chris@0: } Chris@0: Chris@0: private function doResolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition) Chris@0: { Chris@0: if (!$container->has($parent = $definition->getParent())) { Chris@0: throw new RuntimeException(sprintf('Parent definition "%s" does not exist.', $parent)); Chris@0: } Chris@0: Chris@0: $parentDef = $container->findDefinition($parent); Chris@0: if ($parentDef instanceof DefinitionDecorator) { Chris@0: $id = $this->currentId; Chris@0: $this->currentId = $parent; Chris@0: $parentDef = $this->resolveDefinition($container, $parentDef); Chris@0: $container->setDefinition($parent, $parentDef); Chris@0: $this->currentId = $id; Chris@0: } Chris@0: Chris@0: $this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $this->currentId, $parent)); Chris@0: $def = new Definition(); Chris@0: Chris@0: // merge in parent definition Chris@0: // purposely ignored attributes: abstract, tags Chris@0: $def->setClass($parentDef->getClass()); Chris@0: $def->setArguments($parentDef->getArguments()); Chris@0: $def->setMethodCalls($parentDef->getMethodCalls()); Chris@0: $def->setProperties($parentDef->getProperties()); Chris@0: $def->setAutowiringTypes($parentDef->getAutowiringTypes()); Chris@0: if ($parentDef->isDeprecated()) { Chris@0: $def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%')); Chris@0: } Chris@0: $def->setFactory($parentDef->getFactory()); Chris@0: $def->setConfigurator($parentDef->getConfigurator()); Chris@0: $def->setFile($parentDef->getFile()); Chris@0: $def->setPublic($parentDef->isPublic()); Chris@0: $def->setLazy($parentDef->isLazy()); Chris@0: $def->setAutowired($parentDef->isAutowired()); Chris@0: Chris@0: // overwrite with values specified in the decorator Chris@0: $changes = $definition->getChanges(); Chris@0: if (isset($changes['class'])) { Chris@0: $def->setClass($definition->getClass()); Chris@0: } Chris@0: if (isset($changes['factory'])) { Chris@0: $def->setFactory($definition->getFactory()); Chris@0: } Chris@0: if (isset($changes['configurator'])) { Chris@0: $def->setConfigurator($definition->getConfigurator()); Chris@0: } Chris@0: if (isset($changes['file'])) { Chris@0: $def->setFile($definition->getFile()); Chris@0: } Chris@0: if (isset($changes['public'])) { Chris@0: $def->setPublic($definition->isPublic()); Chris@0: } Chris@0: if (isset($changes['lazy'])) { Chris@0: $def->setLazy($definition->isLazy()); Chris@0: } Chris@0: if (isset($changes['deprecated'])) { Chris@0: $def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%')); Chris@0: } Chris@0: if (isset($changes['autowire'])) { Chris@0: $def->setAutowired($definition->isAutowired()); Chris@0: } Chris@0: if (isset($changes['decorated_service'])) { Chris@0: $decoratedService = $definition->getDecoratedService(); Chris@0: if (null === $decoratedService) { Chris@0: $def->setDecoratedService($decoratedService); Chris@0: } else { Chris@0: $def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2]); Chris@0: } Chris@0: } Chris@0: Chris@0: // merge arguments Chris@0: foreach ($definition->getArguments() as $k => $v) { Chris@0: if (is_numeric($k)) { Chris@0: $def->addArgument($v); Chris@0: continue; Chris@0: } Chris@0: Chris@0: if (0 !== strpos($k, 'index_')) { Chris@0: throw new RuntimeException(sprintf('Invalid argument key "%s" found.', $k)); Chris@0: } Chris@0: Chris@0: $index = (int) substr($k, strlen('index_')); Chris@0: $def->replaceArgument($index, $v); Chris@0: } Chris@0: Chris@0: // merge properties Chris@0: foreach ($definition->getProperties() as $k => $v) { Chris@0: $def->setProperty($k, $v); Chris@0: } Chris@0: Chris@0: // append method calls Chris@0: if (count($calls = $definition->getMethodCalls()) > 0) { Chris@0: $def->setMethodCalls(array_merge($def->getMethodCalls(), $calls)); Chris@0: } Chris@0: Chris@0: // merge autowiring types Chris@0: foreach ($definition->getAutowiringTypes() as $autowiringType) { Chris@0: $def->addAutowiringType($autowiringType); Chris@0: } Chris@0: Chris@0: // these attributes are always taken from the child Chris@0: $def->setAbstract($definition->isAbstract()); Chris@0: $def->setShared($definition->isShared()); Chris@0: $def->setTags($definition->getTags()); Chris@0: Chris@0: return $def; Chris@0: } Chris@0: }