Chris@14: Chris@14: * Chris@14: * For the full copyright and license information, please view the LICENSE Chris@14: * file that was distributed with this source code. Chris@14: */ Chris@14: Chris@14: namespace Symfony\Component\DependencyInjection\Compiler; Chris@14: Chris@14: use Symfony\Component\DependencyInjection\ChildDefinition; Chris@14: use Symfony\Component\DependencyInjection\Definition; Chris@14: use Symfony\Component\DependencyInjection\Exception\ExceptionInterface; Chris@14: use Symfony\Component\DependencyInjection\Exception\RuntimeException; Chris@17: use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; Chris@14: Chris@14: /** Chris@14: * This replaces all ChildDefinition instances with their equivalent fully Chris@14: * merged Definition instance. Chris@14: * Chris@14: * @author Johannes M. Schmitt Chris@14: * @author Nicolas Grekas Chris@14: */ Chris@14: class ResolveChildDefinitionsPass extends AbstractRecursivePass Chris@14: { Chris@17: private $currentPath; Chris@17: Chris@14: protected function processValue($value, $isRoot = false) Chris@14: { Chris@14: if (!$value instanceof Definition) { Chris@14: return parent::processValue($value, $isRoot); Chris@14: } Chris@14: if ($isRoot) { Chris@14: // yes, we are specifically fetching the definition from the Chris@14: // container to ensure we are not operating on stale data Chris@14: $value = $this->container->getDefinition($this->currentId); Chris@14: } Chris@14: if ($value instanceof ChildDefinition) { Chris@17: $this->currentPath = []; Chris@14: $value = $this->resolveDefinition($value); Chris@14: if ($isRoot) { Chris@14: $this->container->setDefinition($this->currentId, $value); Chris@14: } Chris@14: } Chris@14: Chris@14: return parent::processValue($value, $isRoot); Chris@14: } Chris@14: Chris@14: /** Chris@14: * Resolves the definition. Chris@14: * Chris@14: * @return Definition Chris@14: * Chris@14: * @throws RuntimeException When the definition is invalid Chris@14: */ Chris@14: private function resolveDefinition(ChildDefinition $definition) Chris@14: { Chris@14: try { Chris@14: return $this->doResolveDefinition($definition); Chris@17: } catch (ServiceCircularReferenceException $e) { Chris@17: throw $e; Chris@14: } catch (ExceptionInterface $e) { Chris@14: $r = new \ReflectionProperty($e, 'message'); Chris@14: $r->setAccessible(true); Chris@14: $r->setValue($e, sprintf('Service "%s": %s', $this->currentId, $e->getMessage())); Chris@14: Chris@14: throw $e; Chris@14: } Chris@14: } Chris@14: Chris@14: private function doResolveDefinition(ChildDefinition $definition) Chris@14: { Chris@14: if (!$this->container->has($parent = $definition->getParent())) { Chris@14: throw new RuntimeException(sprintf('Parent definition "%s" does not exist.', $parent)); Chris@14: } Chris@14: Chris@17: $searchKey = array_search($parent, $this->currentPath); Chris@17: $this->currentPath[] = $parent; Chris@17: Chris@17: if (false !== $searchKey) { Chris@17: throw new ServiceCircularReferenceException($parent, \array_slice($this->currentPath, $searchKey)); Chris@17: } Chris@17: Chris@14: $parentDef = $this->container->findDefinition($parent); Chris@14: if ($parentDef instanceof ChildDefinition) { Chris@14: $id = $this->currentId; Chris@14: $this->currentId = $parent; Chris@14: $parentDef = $this->resolveDefinition($parentDef); Chris@14: $this->container->setDefinition($parent, $parentDef); Chris@14: $this->currentId = $id; Chris@14: } Chris@14: Chris@14: $this->container->log($this, sprintf('Resolving inheritance for "%s" (parent: %s).', $this->currentId, $parent)); Chris@14: $def = new Definition(); Chris@14: Chris@14: // merge in parent definition Chris@14: // purposely ignored attributes: abstract, shared, tags, autoconfigured Chris@14: $def->setClass($parentDef->getClass()); Chris@14: $def->setArguments($parentDef->getArguments()); Chris@14: $def->setMethodCalls($parentDef->getMethodCalls()); Chris@14: $def->setProperties($parentDef->getProperties()); Chris@14: if ($parentDef->getAutowiringTypes(false)) { Chris@14: $def->setAutowiringTypes($parentDef->getAutowiringTypes(false)); Chris@14: } Chris@14: if ($parentDef->isDeprecated()) { Chris@14: $def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%')); Chris@14: } Chris@14: $def->setFactory($parentDef->getFactory()); Chris@14: $def->setConfigurator($parentDef->getConfigurator()); Chris@14: $def->setFile($parentDef->getFile()); Chris@14: $def->setPublic($parentDef->isPublic()); Chris@14: $def->setLazy($parentDef->isLazy()); Chris@14: $def->setAutowired($parentDef->isAutowired()); Chris@14: $def->setChanges($parentDef->getChanges()); Chris@14: Chris@16: $def->setBindings($definition->getBindings() + $parentDef->getBindings()); Chris@14: Chris@14: // overwrite with values specified in the decorator Chris@14: $changes = $definition->getChanges(); Chris@14: if (isset($changes['class'])) { Chris@14: $def->setClass($definition->getClass()); Chris@14: } Chris@14: if (isset($changes['factory'])) { Chris@14: $def->setFactory($definition->getFactory()); Chris@14: } Chris@14: if (isset($changes['configurator'])) { Chris@14: $def->setConfigurator($definition->getConfigurator()); Chris@14: } Chris@14: if (isset($changes['file'])) { Chris@14: $def->setFile($definition->getFile()); Chris@14: } Chris@14: if (isset($changes['public'])) { Chris@14: $def->setPublic($definition->isPublic()); Chris@14: } else { Chris@14: $def->setPrivate($definition->isPrivate() || $parentDef->isPrivate()); Chris@14: } Chris@14: if (isset($changes['lazy'])) { Chris@14: $def->setLazy($definition->isLazy()); Chris@14: } Chris@14: if (isset($changes['deprecated'])) { Chris@14: $def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%')); Chris@14: } Chris@14: if (isset($changes['autowired'])) { Chris@14: $def->setAutowired($definition->isAutowired()); Chris@14: } Chris@14: if (isset($changes['shared'])) { Chris@14: $def->setShared($definition->isShared()); Chris@14: } Chris@14: if (isset($changes['decorated_service'])) { Chris@14: $decoratedService = $definition->getDecoratedService(); Chris@14: if (null === $decoratedService) { Chris@14: $def->setDecoratedService($decoratedService); Chris@14: } else { Chris@14: $def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2]); Chris@14: } Chris@14: } Chris@14: Chris@14: // merge arguments Chris@14: foreach ($definition->getArguments() as $k => $v) { Chris@14: if (is_numeric($k)) { Chris@14: $def->addArgument($v); Chris@14: } elseif (0 === strpos($k, 'index_')) { Chris@17: $def->replaceArgument((int) substr($k, \strlen('index_')), $v); Chris@14: } else { Chris@14: $def->setArgument($k, $v); Chris@14: } Chris@14: } Chris@14: Chris@14: // merge properties Chris@14: foreach ($definition->getProperties() as $k => $v) { Chris@14: $def->setProperty($k, $v); Chris@14: } Chris@14: Chris@14: // append method calls Chris@14: if ($calls = $definition->getMethodCalls()) { Chris@14: $def->setMethodCalls(array_merge($def->getMethodCalls(), $calls)); Chris@14: } Chris@14: Chris@14: // merge autowiring types Chris@14: foreach ($definition->getAutowiringTypes(false) as $autowiringType) { Chris@14: $def->addAutowiringType($autowiringType); Chris@14: } Chris@14: Chris@14: // these attributes are always taken from the child Chris@14: $def->setAbstract($definition->isAbstract()); Chris@14: $def->setTags($definition->getTags()); Chris@14: // autoconfigure is never taken from parent (on purpose) Chris@14: // and it's not legal on an instanceof Chris@14: $def->setAutoconfigured($definition->isAutoconfigured()); Chris@14: Chris@14: return $def; Chris@14: } Chris@14: } Chris@14: Chris@14: class_alias(ResolveChildDefinitionsPass::class, ResolveDefinitionTemplatesPass::class);