[DI] fix definition and usage of AbstractArgument

This commit is contained in:
Nicolas Grekas 2020-04-23 14:10:14 +02:00
parent 69b6c90330
commit abb463c749
12 changed files with 42 additions and 34 deletions

View File

@ -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.'.';
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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.');
}

View File

@ -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);

View File

@ -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;

View File

@ -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)) {

View File

@ -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());
}
}

View File

@ -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');
}

View File

@ -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);

View File

@ -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);

View File

@ -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);