From 0e92399daa581eb4d6aba1318c0a97c150c83a1d Mon Sep 17 00:00:00 2001 From: gauss Date: Sat, 2 Nov 2019 19:24:35 +0200 Subject: [PATCH] [DI] Suggest typed argument when binding fails with untyped argument --- .../Compiler/ResolveBindingsPass.php | 18 +++++++++++++++--- .../Tests/Compiler/ResolveBindingsPassTest.php | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php index dccc75a11b..aa688f4dfd 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php @@ -112,6 +112,8 @@ class ResolveBindingsPass extends AbstractRecursivePass return parent::processValue($value, $isRoot); } + $bindingNames = []; + foreach ($bindings as $key => $binding) { list($bindingValue, $bindingId, $used, $bindingType, $file) = $binding->getValues(); if ($used) { @@ -121,7 +123,11 @@ class ResolveBindingsPass extends AbstractRecursivePass $this->unusedBindings[$bindingId] = [$key, $this->currentId, $bindingType, $file]; } - if (preg_match('/^(?:(?:array|bool|float|int|string) )?\$/', $key)) { + if (preg_match('/^(?:(?:array|bool|float|int|string|([^ $]++)) )\$/', $key, $m)) { + $bindingNames[substr($key, \strlen($m[0]))] = $binding; + } + + if (!isset($m[1])) { continue; } @@ -182,11 +188,17 @@ class ResolveBindingsPass extends AbstractRecursivePass continue; } - if (!$typeHint || '\\' !== $typeHint[0] || !isset($bindings[$typeHint = substr($typeHint, 1)])) { + if ($typeHint && '\\' === $typeHint[0] && isset($bindings[$typeHint = substr($typeHint, 1)])) { + $arguments[$key] = $this->getBindingValue($bindings[$typeHint]); + continue; } - $arguments[$key] = $this->getBindingValue($bindings[$typeHint]); + if (isset($bindingNames[$parameter->name])) { + $bindingKey = array_search($binding, $bindings, true); + $argumentType = substr($bindingKey, 0, strpos($bindingKey, ' ')); + $this->errorMessages[] = sprintf('Did you forget to add the type "%s" to argument "$%s" of method "%s::%s()"?', $argumentType, $parameter->name, $reflectionMethod->class, $reflectionMethod->name); + } } if ($arguments !== $call[1]) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php index bb8417098e..e35ff1edf0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php @@ -18,6 +18,7 @@ use Symfony\Component\DependencyInjection\Compiler\DefinitionErrorExceptionPass; use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; @@ -157,4 +158,19 @@ class ResolveBindingsPassTest extends TestCase $this->assertSame([1 => 'bar'], $container->getDefinition(NamedArgumentsDummy::class)->getArguments()); } + + public function testEmptyBindingTypehint() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Did you forget to add the type "string" to argument "$apiKey" of method "Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy::__construct()"?'); + + $container = new ContainerBuilder(); + $bindings = [ + 'string $apiKey' => new BoundArgument('foo'), + ]; + $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class); + $definition->setBindings($bindings); + $pass = new ResolveBindingsPass(); + $pass->process($container); + } }