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@14: use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; Chris@17: use Symfony\Component\DependencyInjection\ContainerBuilder; Chris@14: use Symfony\Component\DependencyInjection\ContainerInterface; Chris@0: use Symfony\Component\DependencyInjection\Definition; Chris@14: use Symfony\Component\DependencyInjection\Exception\RuntimeException; Chris@12: use Symfony\Component\DependencyInjection\ExpressionLanguage; Chris@0: use Symfony\Component\DependencyInjection\Reference; Chris@12: use Symfony\Component\ExpressionLanguage\Expression; Chris@0: Chris@0: /** Chris@0: * Run this pass before passes that need to know more about the relation of Chris@0: * your services. Chris@0: * Chris@0: * This class will populate the ServiceReferenceGraph with information. You can Chris@0: * retrieve the graph in other passes from the compiler. Chris@0: * Chris@0: * @author Johannes M. Schmitt Chris@0: */ Chris@14: class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements RepeatablePassInterface Chris@0: { Chris@0: private $graph; Chris@0: private $currentDefinition; Chris@0: private $onlyConstructorArguments; Chris@17: private $hasProxyDumper; Chris@14: private $lazy; Chris@12: private $expressionLanguage; Chris@17: private $byConstructor; Chris@0: Chris@0: /** Chris@0: * @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls Chris@0: */ Chris@17: public function __construct($onlyConstructorArguments = false, $hasProxyDumper = true) Chris@0: { Chris@0: $this->onlyConstructorArguments = (bool) $onlyConstructorArguments; Chris@17: $this->hasProxyDumper = (bool) $hasProxyDumper; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setRepeatedPass(RepeatedPass $repeatedPass) Chris@0: { Chris@14: // no-op for BC Chris@0: } Chris@0: Chris@0: /** Chris@0: * Processes a ContainerBuilder object to populate the service reference graph. Chris@0: */ Chris@0: public function process(ContainerBuilder $container) Chris@0: { Chris@0: $this->container = $container; Chris@0: $this->graph = $container->getCompiler()->getServiceReferenceGraph(); Chris@0: $this->graph->clear(); Chris@14: $this->lazy = false; Chris@17: $this->byConstructor = false; Chris@0: Chris@14: foreach ($container->getAliases() as $id => $alias) { Chris@14: $targetId = $this->getDefinitionId((string) $alias); Chris@14: $this->graph->connect($id, $alias, $targetId, $this->getDefinition($targetId), null); Chris@0: } Chris@0: Chris@14: parent::process($container); Chris@0: } Chris@0: Chris@14: protected function processValue($value, $isRoot = false) Chris@0: { Chris@14: $lazy = $this->lazy; Chris@0: Chris@14: if ($value instanceof ArgumentInterface) { Chris@14: $this->lazy = true; Chris@14: parent::processValue($value->getValues()); Chris@14: $this->lazy = $lazy; Chris@14: Chris@14: return $value; Chris@14: } Chris@14: if ($value instanceof Expression) { Chris@17: $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container']); Chris@14: Chris@14: return $value; Chris@14: } Chris@14: if ($value instanceof Reference) { Chris@14: $targetId = $this->getDefinitionId((string) $value); Chris@14: $targetDefinition = $this->getDefinition($targetId); Chris@14: Chris@14: $this->graph->connect( Chris@14: $this->currentId, Chris@14: $this->currentDefinition, Chris@14: $targetId, Chris@14: $targetDefinition, Chris@14: $value, Chris@17: $this->lazy || ($this->hasProxyDumper && $targetDefinition && $targetDefinition->isLazy()), Chris@17: ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior(), Chris@17: $this->byConstructor Chris@14: ); Chris@14: Chris@14: return $value; Chris@14: } Chris@14: if (!$value instanceof Definition) { Chris@14: return parent::processValue($value, $isRoot); Chris@14: } Chris@14: if ($isRoot) { Chris@14: if ($value->isSynthetic() || $value->isAbstract()) { Chris@14: return $value; Chris@0: } Chris@14: $this->currentDefinition = $value; Chris@17: } elseif ($this->currentDefinition === $value) { Chris@17: return $value; Chris@0: } Chris@14: $this->lazy = false; Chris@14: Chris@17: $byConstructor = $this->byConstructor; Chris@17: $this->byConstructor = true; Chris@14: $this->processValue($value->getFactory()); Chris@14: $this->processValue($value->getArguments()); Chris@17: $this->byConstructor = $byConstructor; Chris@14: Chris@14: if (!$this->onlyConstructorArguments) { Chris@14: $this->processValue($value->getProperties()); Chris@14: $this->processValue($value->getMethodCalls()); Chris@14: $this->processValue($value->getConfigurator()); Chris@14: } Chris@14: $this->lazy = $lazy; Chris@14: Chris@14: return $value; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns a service definition given the full name or an alias. Chris@0: * Chris@0: * @param string $id A full id or alias for a service definition Chris@0: * Chris@0: * @return Definition|null The definition related to the supplied id Chris@0: */ Chris@0: private function getDefinition($id) Chris@0: { Chris@0: return null === $id ? null : $this->container->getDefinition($id); Chris@0: } Chris@0: Chris@0: private function getDefinitionId($id) Chris@0: { Chris@0: while ($this->container->hasAlias($id)) { Chris@0: $id = (string) $this->container->getAlias($id); Chris@0: } Chris@0: Chris@0: if (!$this->container->hasDefinition($id)) { Chris@0: return; Chris@0: } Chris@0: Chris@14: return $this->container->normalizeId($id); Chris@0: } Chris@12: Chris@12: private function getExpressionLanguage() Chris@12: { Chris@12: if (null === $this->expressionLanguage) { Chris@14: if (!class_exists(ExpressionLanguage::class)) { Chris@14: throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); Chris@14: } Chris@14: Chris@12: $providers = $this->container->getExpressionLanguageProviders(); Chris@12: $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) { Chris@12: if ('""' === substr_replace($arg, '', 1, -1)) { Chris@12: $id = stripcslashes(substr($arg, 1, -1)); Chris@14: $id = $this->getDefinitionId($id); Chris@12: Chris@12: $this->graph->connect( Chris@12: $this->currentId, Chris@12: $this->currentDefinition, Chris@14: $id, Chris@12: $this->getDefinition($id) Chris@12: ); Chris@12: } Chris@12: Chris@12: return sprintf('$this->get(%s)', $arg); Chris@12: }); Chris@12: } Chris@12: Chris@12: return $this->expressionLanguage; Chris@12: } Chris@0: }