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.