diff --git a/src/Symfony/Component/DependencyInjection/Argument/AbstractArgument.php b/src/Symfony/Component/DependencyInjection/Argument/AbstractArgument.php index 8eb6d5b424..3ba5ff33ba 100644 --- a/src/Symfony/Component/DependencyInjection/Argument/AbstractArgument.php +++ b/src/Symfony/Component/DependencyInjection/Argument/AbstractArgument.php @@ -16,29 +16,26 @@ namespace Symfony\Component\DependencyInjection\Argument; */ final class AbstractArgument { - private $serviceId; - private $argKey; private $text; + private $context; - public function __construct(string $serviceId, string $argKey, string $text = '') + public function __construct(string $text = '') { - $this->serviceId = $serviceId; - $this->argKey = $argKey; - $this->text = $text; + $this->text = trim($text, '. '); } - public function getServiceId(): string + public function setContext(string $context): void { - return $this->serviceId; - } - - public function getArgumentKey(): string - { - return $this->argKey; + $this->context = $context.' is abstract'.('' === $this->text ? '' : ': '); } public function getText(): string { return $this->text; } + + public function getTextWithContext(): string + { + return $this->context.$this->text.'.'; + } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php index e0a019493a..fd3c5e4d1d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Argument\AbstractArgument; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; @@ -28,6 +29,10 @@ class ResolveNamedArgumentsPass extends AbstractRecursivePass */ protected function processValue($value, bool $isRoot = false) { + if ($value instanceof AbstractArgument && $value->getText().'.' === $value->getTextWithContext()) { + $value->setContext(sprintf('A value found in service "%s"', $this->currentId)); + } + if (!$value instanceof Definition) { return parent::processValue($value, $isRoot); } @@ -41,6 +46,10 @@ class ResolveNamedArgumentsPass extends AbstractRecursivePass $resolvedArguments = []; foreach ($arguments as $key => $argument) { + if ($argument instanceof AbstractArgument && $argument->getText().'.' === $argument->getTextWithContext()) { + $argument->setContext(sprintf('Argument '.(\is_int($key) ? 1 + $key : '"%3$s"').' of '.('__construct' === $method ? 'service "%s"' : 'method call "%s::%s()"'), $this->currentId, $method, $key)); + } + if (\is_int($key)) { $resolvedArguments[$key] = $argument; continue; @@ -107,6 +116,12 @@ class ResolveNamedArgumentsPass extends AbstractRecursivePass $value->setMethodCalls($calls); } + foreach ($value->getProperties() as $key => $argument) { + if ($argument instanceof AbstractArgument && $argument->getText().'.' === $argument->getTextWithContext()) { + $argument->setContext(sprintf('Property "%s" of service "%s"', $key, $this->currentId)); + } + } + return parent::processValue($value, $isRoot); } } diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index e08e4a141d..2153304485 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -1219,7 +1219,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface } elseif ($value instanceof Expression) { $value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this]); } elseif ($value instanceof AbstractArgument) { - throw new RuntimeException(sprintf('Argument "%s" of service "%s" is abstract%s, did you forget to define it?', $value->getArgumentKey(), $value->getServiceId(), $value->getText() ? ' ('.$value->getText().')' : '')); + throw new RuntimeException($value->getTextWithContext()); } return $value; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 8f2db860ec..98b74ad87f 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -1787,7 +1787,7 @@ EOF; return $code; } } elseif ($value instanceof AbstractArgument) { - throw new RuntimeException(sprintf('Argument "%s" of service "%s" is abstract%s, did you forget to define it?', $value->getArgumentKey(), $value->getServiceId(), $value->getText() ? ' ('.$value->getText().')' : '')); + throw new RuntimeException($value->getTextWithContext()); } elseif (\is_object($value) || \is_resource($value)) { throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); } diff --git a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php index 805fa95850..bf2ea990a9 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -320,10 +320,6 @@ class XmlDumper extends Dumper $text = $this->document->createTextNode(self::phpToXml(base64_encode($value))); $element->appendChild($text); } elseif ($value instanceof AbstractArgument) { - $argKey = $value->getArgumentKey(); - if (!is_numeric($argKey)) { - $element->setAttribute('key', $argKey); - } $element->setAttribute('type', 'abstract'); $text = $this->document->createTextNode(self::phpToXml($value->getText())); $element->appendChild($text); diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 18843bf979..f4c90f6f78 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -537,8 +537,7 @@ class XmlFileLoader extends FileLoader $arguments[$key] = $value; break; case 'abstract': - $serviceId = $node->getAttribute('id'); - $arguments[$key] = new AbstractArgument($serviceId, (string) $key, $arg->nodeValue); + $arguments[$key] = new AbstractArgument($arg->nodeValue); break; case 'string': $arguments[$key] = $arg->nodeValue; diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index c5073082d5..7504a1fb2c 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -449,7 +449,7 @@ class YamlFileLoader extends FileLoader } if (isset($service['arguments'])) { - $definition->setArguments($this->resolveServices($service['arguments'], $file, false, $id)); + $definition->setArguments($this->resolveServices($service['arguments'], $file)); } if (isset($service['properties'])) { @@ -721,7 +721,7 @@ class YamlFileLoader extends FileLoader * * @return array|string|Reference|ArgumentInterface */ - private function resolveServices($value, string $file, bool $isParameter = false, string $serviceId = '', string $argKey = '') + private function resolveServices($value, string $file, bool $isParameter = false) { if ($value instanceof TaggedValue) { $argument = $value->getValue(); @@ -795,7 +795,7 @@ class YamlFileLoader extends FileLoader return new Reference($id); } if ('abstract' === $value->getTag()) { - return new AbstractArgument($serviceId, $argKey, $value->getValue()); + return new AbstractArgument($value->getValue()); } throw new InvalidArgumentException(sprintf('Unsupported tag "!%s".', $value->getTag())); @@ -803,7 +803,7 @@ class YamlFileLoader extends FileLoader if (\is_array($value)) { foreach ($value as $k => $v) { - $value[$k] = $this->resolveServices($v, $file, $isParameter, $serviceId, $k); + $value[$k] = $this->resolveServices($v, $file, $isParameter); } } elseif (\is_string($value) && 0 === strpos($value, '@=')) { if (!class_exists(Expression::class)) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Argument/AbstractArgumentTest.php b/src/Symfony/Component/DependencyInjection/Tests/Argument/AbstractArgumentTest.php index 91b1a5665b..ae279ded83 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Argument/AbstractArgumentTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Argument/AbstractArgumentTest.php @@ -18,9 +18,7 @@ class AbstractArgumentTest extends TestCase { public function testAbstractArgumentGetters() { - $argument = new AbstractArgument('foo', '$bar', 'should be defined by Pass'); - $this->assertSame('foo', $argument->getServiceId()); - $this->assertSame('$bar', $argument->getArgumentKey()); + $argument = new AbstractArgument('should be defined by Pass'); $this->assertSame('should be defined by Pass', $argument->getText()); } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 93db5b694d..4f935fd219 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -552,11 +552,14 @@ class ContainerBuilderTest extends TestCase public function testCreateServiceWithAbstractArgument() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Argument "$baz" of service "foo" is abstract (should be defined by Pass), did you forget to define it?'); + $this->expectExceptionMessage('Argument "$baz" of service "foo" is abstract: should be defined by Pass.'); $builder = new ContainerBuilder(); $builder->register('foo', FooWithAbstractArgument::class) - ->addArgument(new AbstractArgument('foo', '$baz', 'should be defined by Pass')); + ->setArgument('$baz', new AbstractArgument('should be defined by Pass')) + ->setPublic(true); + + $builder->compile(); $builder->get('foo'); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index cacd85ff69..b85cae02df 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -1372,12 +1372,12 @@ class PhpDumperTest extends TestCase public function testDumpServiceWithAbstractArgument() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Argument "$baz" of service "Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument" is abstract (should be defined by Pass), did you forget to define it?'); + $this->expectExceptionMessage('Argument "$baz" of service "Symfony\Component\DependencyInjection\Tests\Fixtures\FooWithAbstractArgument" is abstract: should be defined by Pass.'); $container = new ContainerBuilder(); $container->register(FooWithAbstractArgument::class, FooWithAbstractArgument::class) - ->setArgument('$baz', new AbstractArgument(FooWithAbstractArgument::class, '$baz', 'should be defined by Pass')) + ->setArgument('$baz', new AbstractArgument('should be defined by Pass')) ->setArgument('$bar', 'test') ->setPublic(true); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php index f0bcd6d7de..b5ea873159 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php @@ -264,7 +264,7 @@ class XmlDumperTest extends TestCase { $container = new ContainerBuilder(); $container->register(FooWithAbstractArgument::class, FooWithAbstractArgument::class) - ->setArgument('$baz', new AbstractArgument(FooWithAbstractArgument::class, '$baz', 'should be defined by Pass')) + ->setArgument('$baz', new AbstractArgument('should be defined by Pass')) ->setArgument('$bar', 'test'); $dumper = new XmlDumper($container); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php index d7896ae436..fa1266f535 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php @@ -123,7 +123,7 @@ class YamlDumperTest extends TestCase { $container = new ContainerBuilder(); $container->register(FooWithAbstractArgument::class, FooWithAbstractArgument::class) - ->setArgument('$baz', new AbstractArgument(FooWithAbstractArgument::class, '$baz', 'should be defined by Pass')) + ->setArgument('$baz', new AbstractArgument('should be defined by Pass')) ->setArgument('$bar', 'test'); $dumper = new YamlDumper($container);