From ab8dc0c9d3fcc059c68a196e071d7b006ca41b2a Mon Sep 17 00:00:00 2001 From: "Ariel J. Birnbaum" Date: Tue, 22 Mar 2016 14:30:07 +0100 Subject: [PATCH] Optimize ReplaceAliasByActualDefinitionPass Previous implementation passed over every alias and every definition, for every alias (n*(n+m) runtime). New implementation passes once over all aliases and once over all definitions (n+m). Also removing needless "restart" logic --- it has no real effect in either case. --- .../ReplaceAliasByActualDefinitionPass.php | 135 +++++++++--------- 1 file changed, 64 insertions(+), 71 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php index 734761de26..94121889fe 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php @@ -25,7 +25,6 @@ class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface { private $compiler; private $formatter; - private $sourceId; /** * Process the Container to replace aliases with service definitions. @@ -36,105 +35,99 @@ class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { + // Setup $this->compiler = $container->getCompiler(); $this->formatter = $this->compiler->getLoggingFormatter(); - - foreach ($container->getAliases() as $id => $alias) { - $aliasId = (string) $alias; - - if ('service_container' === $aliasId) { + // First collect all alias targets that need to be replaced + $seenAliasTargets = array(); + $replacements = array(); + foreach ($container->getAliases() as $definitionId => $target) { + $targetId = (string) $target; + // Special case: leave this target alone + if ('service_container' === $targetId) { continue; } - - try { - $definition = $container->getDefinition($aliasId); - } catch (InvalidArgumentException $e) { - throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $id, $alias), null, $e); + // Check if target needs to be replaces + if (isset($replacements[$targetId])) { + $container->setAlias($definitionId, $replacements[$targetId]); + } + // No neeed to process the same target twice + if (isset($seenAliasTargets[$targetId])) { + continue; + } + // Process new target + $seenAliasTargets[$targetId] = true; + try { + $definition = $container->getDefinition($targetId); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $definitionId, $targetId), null, $e); } - if ($definition->isPublic()) { continue; } - + // Remove private definition and schedule for replacement $definition->setPublic(true); - $container->setDefinition($id, $definition); - $container->removeDefinition($aliasId); - - $this->updateReferences($container, $aliasId, $id); - - // we have to restart the process due to concurrent modification of - // the container - $this->process($container); - - break; + $container->setDefinition($definitionId, $definition); + $container->removeDefinition($targetId); + $replacements[$targetId] = $definitionId; + } + // Now replace target instances in all definitions + foreach ($container->getDefinitions() as $definitionId => $definition) { + $definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments())); + $definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls())); + $definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties())); + $definition->setFactoryService($this->updateFactoryReferenceId($replacements, $definition->getFactoryService())); } } /** - * Updates references to remove aliases. + * Recursively updates references in an array. * - * @param ContainerBuilder $container The container - * @param string $currentId The alias identifier being replaced - * @param string $newId The id of the service the alias points to - */ - private function updateReferences($container, $currentId, $newId) - { - foreach ($container->getAliases() as $id => $alias) { - if ($currentId === (string) $alias) { - $container->setAlias($id, $newId); - } - } - - foreach ($container->getDefinitions() as $id => $definition) { - $this->sourceId = $id; - - $definition->setArguments( - $this->updateArgumentReferences($definition->getArguments(), $currentId, $newId) - ); - - $definition->setMethodCalls( - $this->updateArgumentReferences($definition->getMethodCalls(), $currentId, $newId) - ); - - $definition->setProperties( - $this->updateArgumentReferences($definition->getProperties(), $currentId, $newId) - ); - - $definition->setFactoryService($this->updateFactoryServiceReference($definition->getFactoryService(), $currentId, $newId)); - } - } - - /** - * Updates argument references. - * - * @param array $arguments An array of Arguments - * @param string $currentId The alias identifier - * @param string $newId The identifier the alias points to + * @param array $replacements Table of aliases to replace + * @param string $definitionId Identifier of this definition + * @param array $arguments Where to replace the aliases * * @return array */ - private function updateArgumentReferences(array $arguments, $currentId, $newId) + private function updateArgumentReferences(array $replacements, $definitionId, array $arguments) { foreach ($arguments as $k => $argument) { + // Handle recursion step if (is_array($argument)) { - $arguments[$k] = $this->updateArgumentReferences($argument, $currentId, $newId); - } elseif ($argument instanceof Reference) { - if ($currentId === (string) $argument) { - $arguments[$k] = new Reference($newId, $argument->getInvalidBehavior()); - $this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $this->sourceId, $currentId, $newId)); - } + $arguments[$k] = $this->updateArgumentReferences($replacements, $definitionId, $argument); + continue; } + // Skip arguments that don't need replacement + if (!$argument instanceof Reference) { + continue; + } + $referenceId = (string) $argument; + if (!isset($replacements[$referenceId])) { + continue; + } + // Perform the replacement + $newId = $replacements[$referenceId]; + $arguments[$k] = new Reference($newId, $argument->getInvalidBehavior()); + $this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $definitionId, $referenceId, $newId)); } return $arguments; } - private function updateFactoryServiceReference($factoryService, $currentId, $newId) + /** + * Returns the updated reference for the factory service. + * + * @param array $replacements Table of aliases to replace + * @param string|null $referenceId Factory service reference identifier + * + * @return string|null + */ + private function updateFactoryReferenceId(array $replacements, $referenceId) { - if (null === $factoryService) { + if (null === $referenceId) { return; } - return $currentId === $factoryService ? $newId : $factoryService; + return isset($replacements[$referenceId]) ? $replacements[$referenceId] : $referenceId; } }