From 8b67567dfb6de94ba42a60beb9af4c61511d3b0a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 27 Jan 2023 11:03:16 +0100 Subject: [PATCH] [DependencyInjection] Fix combinatory explosion when autowiring union and intersection types --- .../Compiler/AutowirePass.php | 63 +++++++------------ 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index c2b80770c880f..81c4b11e11f0d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -45,7 +45,6 @@ class AutowirePass extends AbstractRecursivePass private $decoratedMethodIndex; private $decoratedMethodArgumentIndex; private $typesClone; - private $combinedAliases; public function __construct(bool $throwOnAutowireException = true) { @@ -61,8 +60,6 @@ public function __construct(bool $throwOnAutowireException = true) */ public function process(ContainerBuilder $container) { - $this->populateCombinedAliases($container); - try { $this->typesClone = clone $this; parent::process($container); @@ -75,7 +72,6 @@ public function process(ContainerBuilder $container) $this->decoratedMethodIndex = null; $this->decoratedMethodArgumentIndex = null; $this->typesClone = null; - $this->combinedAliases = []; } } @@ -371,12 +367,12 @@ private function getAutowiredReference(TypedReference $reference, bool $filterTy return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } - if (null !== ($alias = $this->combinedAliases[$alias] ?? null) && !$this->container->findDefinition($alias)->isAbstract()) { + if (null !== ($alias = $this->getCombinedAlias($type, $name) ?? null) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } if ($this->container->has($name) && !$this->container->findDefinition($name)->isAbstract()) { - foreach ($this->container->getAliases() + $this->combinedAliases as $id => $alias) { + foreach ($this->container->getAliases() as $id => $alias) { if ($name === (string) $alias && str_starts_with($id, $type.' $')) { return new TypedReference($name, $type, $reference->getInvalidBehavior()); } @@ -388,7 +384,7 @@ private function getAutowiredReference(TypedReference $reference, bool $filterTy return new TypedReference($type, $type, $reference->getInvalidBehavior()); } - if (null !== ($alias = $this->combinedAliases[$type] ?? null) && !$this->container->findDefinition($alias)->isAbstract()) { + if (null !== ($alias = $this->getCombinedAlias($type) ?? null) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } @@ -582,44 +578,31 @@ private function populateAutowiringAlias(string $id): void } } - private function populateCombinedAliases(ContainerBuilder $container): void + private function getCombinedAlias(string $type, string $name = null): ?string { - $this->combinedAliases = []; - $reverseAliases = []; - - foreach ($container->getAliases() as $id => $alias) { - if (!preg_match('/(?(DEFINE)(?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+))^((?&V)(?:\\\\(?&V))*+)(?: \$((?&V)))?$/', $id, $m)) { - continue; - } - - $type = $m[2]; - $name = $m[3] ?? ''; - $reverseAliases[(string) $alias][$name][] = $type; + if (str_contains($type, '&')) { + $types = explode('&', $type); + } elseif (str_contains($type, '|')) { + $types = explode('|', $type); + } else { + return null; } - foreach ($reverseAliases as $alias => $names) { - foreach ($names as $name => $types) { - if (2 > $count = \count($types)) { - continue; - } - sort($types); - $i = 1 << $count; - - // compute the powerset of the list of types - while ($i--) { - $set = []; - for ($j = 0; $j < $count; ++$j) { - if ($i & (1 << $j)) { - $set[] = $types[$j]; - } - } + $alias = null; + $suffix = $name ? ' $'.$name : ''; - if (2 <= \count($set)) { - $this->combinedAliases[implode('&', $set).('' === $name ? '' : ' $'.$name)] = $alias; - $this->combinedAliases[implode('|', $set).('' === $name ? '' : ' $'.$name)] = $alias; - } - } + foreach ($types as $type) { + if (!$this->container->hasAlias($type.$suffix)) { + return null; + } + + if (null === $alias) { + $alias = (string) $this->container->getAlias($type.$suffix); + } elseif ((string) $this->container->getAlias($type.$suffix) !== $alias) { + return null; } } + + return $alias; } }