diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php index 5e7ba3173e5b4..aabc5b71d6cab 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php @@ -11,6 +11,8 @@ namespace Symfony\Component\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; @@ -23,34 +25,101 @@ */ class DefinitionErrorExceptionPass extends AbstractRecursivePass { + private $erroredDefinitions = []; + private $targetReferences = []; + private $sourceReferences = []; + + /** + * @return void + */ + public function process(ContainerBuilder $container) + { + try { + parent::process($container); + + if (!$this->erroredDefinitions) { + return; + } + + $runtimeIds = []; + + foreach ($this->sourceReferences as $id => $sourceIds) { + foreach ($sourceIds as $sourceId => $isRuntime) { + if (!$isRuntime) { + continue 2; + } + } + + unset($this->erroredDefinitions[$id]); + $runtimeIds[$id] = $id; + } + + if (!$this->erroredDefinitions) { + return; + } + + foreach ($this->targetReferences as $id => $targetIds) { + if (!isset($this->sourceReferences[$id]) || isset($runtimeIds[$id]) || isset($this->erroredDefinitions[$id])) { + continue; + } + foreach ($this->targetReferences[$id] as $targetId => $isRuntime) { + foreach ($this->sourceReferences[$id] as $sourceId => $isRuntime) { + if ($sourceId !== $targetId) { + $this->sourceReferences[$targetId][$sourceId] = false; + $this->targetReferences[$sourceId][$targetId] = false; + } + } + } + + unset($this->sourceReferences[$id]); + } + + foreach ($this->erroredDefinitions as $id => $definition) { + if (isset($this->sourceReferences[$id])) { + continue; + } + + // only show the first error so the user can focus on it + $errors = $definition->getErrors(); + + throw new RuntimeException(reset($errors)); + } + } finally { + $this->erroredDefinitions = []; + $this->targetReferences = []; + $this->sourceReferences = []; + } + } + /** * {@inheritdoc} */ protected function processValue($value, bool $isRoot = false) { - if (!$value instanceof Definition || !$value->hasErrors()) { - return parent::processValue($value, $isRoot); + if ($value instanceof ArgumentInterface) { + parent::processValue($value->getValues()); + + return $value; } - if ($isRoot && !$value->isPublic()) { - $graph = $this->container->getCompiler()->getServiceReferenceGraph(); - $runtimeException = false; - foreach ($graph->getNode($this->currentId)->getInEdges() as $edge) { - if (!$edge->getValue() instanceof Reference || ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE !== $edge->getValue()->getInvalidBehavior()) { - $runtimeException = false; - break; - } - $runtimeException = true; - } - if ($runtimeException) { - return parent::processValue($value, $isRoot); + if ($value instanceof Reference && $this->currentId !== $targetId = (string) $value) { + if (ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) { + $this->sourceReferences[$targetId][$this->currentId] ?? $this->sourceReferences[$targetId][$this->currentId] = true; + $this->targetReferences[$this->currentId][$targetId] ?? $this->targetReferences[$this->currentId][$targetId] = true; + } else { + $this->sourceReferences[$targetId][$this->currentId] = false; + $this->targetReferences[$this->currentId][$targetId] = false; } + + return $value; + } + + if (!$value instanceof Definition || !$value->hasErrors()) { + return parent::processValue($value, $isRoot); } - // only show the first error so the user can focus on it - $errors = $value->getErrors(); - $message = reset($errors); + $this->erroredDefinitions[$this->currentId] = $value; - throw new RuntimeException($message); + return parent::processValue($value); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php index b22b934dd80cd..f02caf0a7f81e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DefinitionErrorExceptionPassTest.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; class DefinitionErrorExceptionPassTest extends TestCase { @@ -49,4 +50,25 @@ public function testNoExceptionThrown() $pass->process($container); $this->assertSame($def, $container->getDefinition('foo_service_id')->getArgument(0)); } + + public function testSkipNestedErrors() + { + $container = new ContainerBuilder(); + + $container->register('nested_error', 'stdClass') + ->addError('Things went wrong!'); + + $container->register('bar', 'stdClass') + ->addArgument(new Reference('nested_error')); + + $container->register('foo', 'stdClass') + ->addArgument(new Reference('bar', ContainerBuilder::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE)); + + $pass = new DefinitionErrorExceptionPass(); + $pass->process($container); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Things went wrong!'); + $container->get('foo'); + } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy