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@0: use Symfony\Component\DependencyInjection\Definition; Chris@14: use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; Chris@0: use Symfony\Component\DependencyInjection\Reference; Chris@0: Chris@0: /** Chris@0: * Inline service definitions where this is possible. Chris@0: * Chris@0: * @author Johannes M. Schmitt Chris@0: */ Chris@14: class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface Chris@0: { Chris@17: private $cloningIds = []; Chris@17: private $inlinedServiceIds = []; 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@14: * Returns an array of all services inlined by this pass. Chris@0: * Chris@14: * The key is the inlined service id and its value is the list of services it was inlined into. Chris@14: * Chris@14: * @deprecated since version 3.4, to be removed in 4.0. Chris@14: * Chris@14: * @return array Chris@0: */ Chris@14: public function getInlinedServiceIds() Chris@0: { Chris@14: @trigger_error('Calling InlineServiceDefinitionsPass::getInlinedServiceIds() is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); Chris@0: Chris@14: return $this->inlinedServiceIds; Chris@0: } Chris@0: Chris@0: /** Chris@14: * {@inheritdoc} Chris@0: */ Chris@14: protected function processValue($value, $isRoot = false) Chris@0: { Chris@14: if ($value instanceof ArgumentInterface) { Chris@14: // Reference found in ArgumentInterface::getValues() are not inlineable Chris@14: return $value; Chris@0: } Chris@0: Chris@14: if ($value instanceof Definition && $this->cloningIds) { Chris@14: if ($value->isShared()) { Chris@14: return $value; Chris@14: } Chris@14: $value = clone $value; Chris@14: } Chris@14: Chris@14: if (!$value instanceof Reference || !$this->container->hasDefinition($id = $this->container->normalizeId($value))) { Chris@14: return parent::processValue($value, $isRoot); Chris@14: } Chris@14: Chris@14: $definition = $this->container->getDefinition($id); Chris@14: Chris@14: if (!$this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) { Chris@14: return $value; Chris@14: } Chris@14: Chris@14: $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); Chris@14: $this->inlinedServiceIds[$id][] = $this->currentId; Chris@14: Chris@14: if ($definition->isShared()) { Chris@14: return $definition; Chris@14: } Chris@14: Chris@14: if (isset($this->cloningIds[$id])) { Chris@14: $ids = array_keys($this->cloningIds); Chris@14: $ids[] = $id; Chris@14: Chris@17: throw new ServiceCircularReferenceException($id, \array_slice($ids, array_search($id, $ids))); Chris@14: } Chris@14: Chris@14: $this->cloningIds[$id] = true; Chris@14: try { Chris@14: return $this->processValue($definition); Chris@14: } finally { Chris@14: unset($this->cloningIds[$id]); Chris@14: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks if the definition is inlineable. Chris@0: * Chris@0: * @return bool If the definition is inlineable Chris@0: */ Chris@14: private function isInlineableDefinition($id, Definition $definition, ServiceReferenceGraph $graph) Chris@0: { Chris@16: if ($definition->getErrors() || $definition->isDeprecated() || $definition->isLazy() || $definition->isSynthetic()) { Chris@16: return false; Chris@16: } Chris@16: Chris@0: if (!$definition->isShared()) { Chris@0: return true; Chris@0: } Chris@0: Chris@16: if ($definition->isPublic() || $definition->isPrivate()) { Chris@0: return false; Chris@0: } Chris@0: Chris@14: if (!$graph->hasNode($id)) { Chris@0: return true; Chris@0: } Chris@0: Chris@0: if ($this->currentId == $id) { Chris@0: return false; Chris@0: } Chris@0: Chris@17: $ids = []; Chris@17: $isReferencedByConstructor = false; Chris@14: foreach ($graph->getNode($id)->getInEdges() as $edge) { Chris@17: $isReferencedByConstructor = $isReferencedByConstructor || $edge->isReferencedByConstructor(); Chris@17: if ($edge->isWeak() || $edge->isLazy()) { Chris@14: return false; Chris@14: } Chris@0: $ids[] = $edge->getSourceNode()->getId(); Chris@0: } Chris@0: Chris@17: if (!$ids) { Chris@17: return true; Chris@17: } Chris@17: Chris@17: if (\count(array_unique($ids)) > 1) { Chris@0: return false; Chris@0: } Chris@0: Chris@17: if (\count($ids) > 1 && \is_array($factory = $definition->getFactory()) && ($factory[0] instanceof Reference || $factory[0] instanceof Definition)) { Chris@0: return false; Chris@0: } Chris@0: Chris@17: return $this->container->getDefinition($ids[0])->isShared(); Chris@0: } Chris@0: }