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\ContainerBuilder; Chris@0: use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; Chris@0: use Symfony\Component\DependencyInjection\Reference; Chris@0: Chris@0: /** Chris@0: * Replaces aliases with actual service definitions, effectively removing these Chris@0: * aliases. Chris@0: * Chris@0: * @author Johannes M. Schmitt Chris@0: */ Chris@0: class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface Chris@0: { Chris@0: private $compiler; Chris@0: private $formatter; Chris@0: Chris@0: /** Chris@0: * Process the Container to replace aliases with service definitions. Chris@0: * Chris@0: * @param ContainerBuilder $container Chris@0: * Chris@0: * @throws InvalidArgumentException if the service definition does not exist Chris@0: */ Chris@0: public function process(ContainerBuilder $container) Chris@0: { Chris@0: // Setup Chris@0: $this->compiler = $container->getCompiler(); Chris@0: $this->formatter = $this->compiler->getLoggingFormatter(); Chris@0: // First collect all alias targets that need to be replaced Chris@0: $seenAliasTargets = array(); Chris@0: $replacements = array(); Chris@0: foreach ($container->getAliases() as $definitionId => $target) { Chris@0: $targetId = (string) $target; Chris@0: // Special case: leave this target alone Chris@0: if ('service_container' === $targetId) { Chris@0: continue; Chris@0: } Chris@0: // Check if target needs to be replaces Chris@0: if (isset($replacements[$targetId])) { Chris@0: $container->setAlias($definitionId, $replacements[$targetId]); Chris@0: } Chris@0: // No need to process the same target twice Chris@0: if (isset($seenAliasTargets[$targetId])) { Chris@0: continue; Chris@0: } Chris@0: // Process new target Chris@0: $seenAliasTargets[$targetId] = true; Chris@0: try { Chris@0: $definition = $container->getDefinition($targetId); Chris@0: } catch (InvalidArgumentException $e) { Chris@0: throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $definitionId, $targetId), null, $e); Chris@0: } Chris@0: if ($definition->isPublic()) { Chris@0: continue; Chris@0: } Chris@0: // Remove private definition and schedule for replacement Chris@0: $definition->setPublic(true); Chris@0: $container->setDefinition($definitionId, $definition); Chris@0: $container->removeDefinition($targetId); Chris@0: $replacements[$targetId] = $definitionId; Chris@0: } Chris@0: Chris@0: // Now replace target instances in all definitions Chris@0: foreach ($container->getDefinitions() as $definitionId => $definition) { Chris@0: $definition->setArguments($this->updateArgumentReferences($replacements, $definitionId, $definition->getArguments())); Chris@0: $definition->setMethodCalls($this->updateArgumentReferences($replacements, $definitionId, $definition->getMethodCalls())); Chris@0: $definition->setProperties($this->updateArgumentReferences($replacements, $definitionId, $definition->getProperties())); Chris@0: $definition->setFactory($this->updateFactoryReference($replacements, $definition->getFactory())); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Recursively updates references in an array. Chris@0: * Chris@0: * @param array $replacements Table of aliases to replace Chris@0: * @param string $definitionId Identifier of this definition Chris@0: * @param array $arguments Where to replace the aliases Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: private function updateArgumentReferences(array $replacements, $definitionId, array $arguments) Chris@0: { Chris@0: foreach ($arguments as $k => $argument) { Chris@0: // Handle recursion step Chris@0: if (is_array($argument)) { Chris@0: $arguments[$k] = $this->updateArgumentReferences($replacements, $definitionId, $argument); Chris@0: continue; Chris@0: } Chris@0: // Skip arguments that don't need replacement Chris@0: if (!$argument instanceof Reference) { Chris@0: continue; Chris@0: } Chris@0: $referenceId = (string) $argument; Chris@0: if (!isset($replacements[$referenceId])) { Chris@0: continue; Chris@0: } Chris@0: // Perform the replacement Chris@0: $newId = $replacements[$referenceId]; Chris@0: $arguments[$k] = new Reference($newId, $argument->getInvalidBehavior()); Chris@0: $this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $definitionId, $referenceId, $newId)); Chris@0: } Chris@0: Chris@0: return $arguments; Chris@0: } Chris@0: Chris@0: private function updateFactoryReference(array $replacements, $factory) Chris@0: { Chris@0: if (is_array($factory) && $factory[0] instanceof Reference && isset($replacements[$referenceId = (string) $factory[0]])) { Chris@0: $factory[0] = new Reference($replacements[$referenceId], $factory[0]->getInvalidBehavior()); Chris@0: } Chris@0: Chris@0: return $factory; Chris@0: } Chris@0: }