diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php index 10b3cf1b0dd81..fb8c41845cf39 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php @@ -16,8 +16,14 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass; +use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass; use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass; +use Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass; +use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass; +use Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; @@ -81,6 +87,16 @@ private function getContainerBuilder(): ContainerBuilder $refl = new \ReflectionProperty($parameterBag, 'resolved'); $refl->setAccessible(true); $refl->setValue($parameterBag, true); + + // To keep in sync with the default removing passes of PassConfig minus DefinitionErrorExceptionPass + $container->getCompilerPassConfig()->setRemovingPasses([ + new RemovePrivateAliasesPass(), + new ReplaceAliasByActualDefinitionPass(), + new RemoveAbstractDefinitionsPass(), + new RemoveUnusedDefinitionsPass(), + new InlineServiceDefinitionsPass(new AnalyzeServiceReferencesPass()), + new AnalyzeServiceReferencesPass(), + ]); } return $this->containerBuilder = $container; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php index 5ee0ff1f491cc..04d9592d73810 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php @@ -48,8 +48,7 @@ protected function processValue($value, $isRoot = false) } // only show the first error so the user can focus on it - $errors = $value->getErrors(); - $message = reset($errors); + $message = $value->getFirstError(); throw new RuntimeException($message); } diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 247a021fd786e..e49ff14e9f180 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -606,8 +606,8 @@ private function doGet(string $id, int $invalidBehavior = ContainerInterface::EX throw $e; } - if ($definition->hasErrors() && $e = $definition->getErrors()) { - throw new RuntimeException(reset($e)); + if ($e = $definition->getFirstError()) { + throw new RuntimeException($e); } if ($isConstructorArgument) { diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index e2bc713a3a911..378dfa8dae2bc 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -937,4 +937,26 @@ public function hasErrors(): bool { return (bool) $this->errors; } + + /** + * @internal + */ + public function getFirstError(): ?string + { + if (!$this->hasErrors()) { + return null; + } + + $error = $this->errors[0]; + + if ($error instanceof \Closure) { + return (string) $error(); + } + + if (!\is_string($error)) { + return (string) $error; + } + + return $error; + } } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 15d5c09f767ca..ada671ba3643a 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -1667,7 +1667,7 @@ private function dumpValue($value, bool $interpolate = true): string continue; } $definition = $this->container->findDefinition($id = (string) $v); - $load = !($definition->hasErrors() && $e = $definition->getErrors()) ? $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition) : reset($e); + $load = !($e = $definition->getFirstError()) ? $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition) : $e; $serviceMap .= sprintf("\n %s => [%s, %s, %s, %s],", $this->export($k), $this->export($definition->isShared() ? ($definition->isPublic() ? 'services' : 'privates') : false), @@ -1686,10 +1686,10 @@ private function dumpValue($value, bool $interpolate = true): string list($this->definitionVariables, $this->referenceVariables) = $scope; } } elseif ($value instanceof Definition) { - if ($value->hasErrors() && $e = $value->getErrors()) { + if ($e = $value->getFirstError()) { $this->addThrow = true; - return sprintf('$this->throw(%s)', $this->export(reset($e))); + return sprintf('$this->throw(%s)', $this->export($e)); } if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) { return $this->dumpValue($this->definitionVariables[$value], $interpolate); @@ -1800,10 +1800,10 @@ private function getServiceCall(string $id, Reference $reference = null): string return $code; } } elseif ($this->isTrivialInstance($definition)) { - if ($definition->hasErrors() && $e = $definition->getErrors()) { + if ($e = $definition->getFirstError()) { $this->addThrow = true; - return sprintf('$this->throw(%s)', $this->export(reset($e))); + return sprintf('$this->throw(%s)', $this->export($e)); } $code = $this->addNewInstance($definition, '', $id); if ($definition->isShared() && !isset($this->singleUsePrivateIds[$id])) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index fb5d827acdc0a..d2383f97ca94b 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -211,6 +211,10 @@ private function addService(Definition $definition, ?string $id, \DOMElement $pa $service->appendChild($configurator); } + if ($e = $definition->getFirstError()) { + $service->setAttribute('error', $e); + } + $parent->appendChild($service); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index ccb68ee8f1ee1..6136df7b709b3 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -156,6 +156,10 @@ private function addService(string $id, Definition $definition): string $code .= sprintf(" configurator: %s\n", $this->dumper->dump($this->dumpCallable($callable), 0)); } + if ($e = $definition->getFirstError()) { + $code .= sprintf(" error: %s\n", $this->dumper->dump($e)); + } + return $code; } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 41a9f0a3ac933..cd089b4fb69d5 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -382,6 +382,10 @@ private function parseDefinition(\DOMElement $service, string $file, array $defa $definition->setDecoratedService($decorates, $renameId, $priority, $invalidBehavior); } + if ($e = $service->getAttribute('error')) { + $definition->addError($e); + } + return $definition; } diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index ee8c1401513c0..82b02006983b7 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -62,6 +62,7 @@ class YamlFileLoader extends FileLoader 'autowire' => 'autowire', 'autoconfigure' => 'autoconfigure', 'bind' => 'bind', + 'error' => 'error', ]; private static $prototypeKeywords = [ @@ -602,6 +603,10 @@ private function parseDefinition(string $id, $service, string $file, array $defa throw new InvalidArgumentException(sprintf('A "resource" attribute must be set when the "namespace" attribute is set for service "%s" in %s. Check your YAML syntax.', $id, $file)); } + if (isset($service['error'])) { + $definition->addError($service['error']); + } + if (\array_key_exists('resource', $service)) { if (!\is_string($service['resource'])) { throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file)); diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index 2f745c3326d49..103ab73977a6f 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -134,6 +134,7 @@ + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml index 55ec20ee10059..67a48252dd885 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -147,7 +147,7 @@ - + diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml index fd2be046f8cd6..4e95a154f3e83 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -177,3 +177,4 @@ services: public: true errored_definition: class: stdClass + error: Service "errored_definition" is broken.