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\ContainerInterface; Chris@0: use Symfony\Component\DependencyInjection\Reference; Chris@0: use Symfony\Component\DependencyInjection\ContainerBuilder; Chris@0: use Symfony\Component\DependencyInjection\Exception\RuntimeException; Chris@0: Chris@0: /** Chris@0: * Emulates the invalid behavior if the reference is not found within the Chris@0: * container. Chris@0: * Chris@0: * @author Johannes M. Schmitt Chris@0: */ Chris@0: class ResolveInvalidReferencesPass implements CompilerPassInterface Chris@0: { Chris@0: private $container; Chris@0: Chris@0: /** Chris@0: * Process the ContainerBuilder to resolve invalid references. Chris@0: * Chris@0: * @param ContainerBuilder $container Chris@0: */ Chris@0: public function process(ContainerBuilder $container) Chris@0: { Chris@0: $this->container = $container; Chris@0: foreach ($container->getDefinitions() as $definition) { Chris@0: if ($definition->isSynthetic() || $definition->isAbstract()) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: $definition->setArguments( Chris@0: $this->processArguments($definition->getArguments()) Chris@0: ); Chris@0: Chris@0: $calls = array(); Chris@0: foreach ($definition->getMethodCalls() as $call) { Chris@0: try { Chris@0: $calls[] = array($call[0], $this->processArguments($call[1], true)); Chris@0: } catch (RuntimeException $e) { Chris@0: // this call is simply removed Chris@0: } Chris@0: } Chris@0: $definition->setMethodCalls($calls); Chris@0: Chris@0: $properties = array(); Chris@0: foreach ($definition->getProperties() as $name => $value) { Chris@0: try { Chris@0: $value = $this->processArguments(array($value), true); Chris@0: $properties[$name] = reset($value); Chris@0: } catch (RuntimeException $e) { Chris@0: // ignore property Chris@0: } Chris@0: } Chris@0: $definition->setProperties($properties); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Processes arguments to determine invalid references. Chris@0: * Chris@0: * @param array $arguments An array of Reference objects Chris@0: * @param bool $inMethodCall Chris@0: * @param bool $inCollection Chris@0: * Chris@0: * @return array Chris@0: * Chris@0: * @throws RuntimeException When the config is invalid Chris@0: */ Chris@0: private function processArguments(array $arguments, $inMethodCall = false, $inCollection = false) Chris@0: { Chris@0: $isNumeric = array_keys($arguments) === range(0, count($arguments) - 1); Chris@0: Chris@0: foreach ($arguments as $k => $argument) { Chris@0: if (is_array($argument)) { Chris@0: $arguments[$k] = $this->processArguments($argument, $inMethodCall, true); Chris@0: } elseif ($argument instanceof Reference) { Chris@0: $id = (string) $argument; Chris@0: Chris@0: $invalidBehavior = $argument->getInvalidBehavior(); Chris@0: $exists = $this->container->has($id); Chris@0: Chris@0: // resolve invalid behavior Chris@0: if (!$exists && ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) { Chris@0: $arguments[$k] = null; Chris@0: } elseif (!$exists && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) { Chris@0: if ($inCollection) { Chris@0: unset($arguments[$k]); Chris@0: continue; Chris@0: } Chris@0: if ($inMethodCall) { Chris@0: throw new RuntimeException('Method shouldn\'t be called.'); Chris@0: } Chris@0: Chris@0: $arguments[$k] = null; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: // Ensure numerically indexed arguments have sequential numeric keys. Chris@0: if ($isNumeric) { Chris@0: $arguments = array_values($arguments); Chris@0: } Chris@0: Chris@0: return $arguments; Chris@0: } Chris@0: }