diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 0fece9802e..fb8c135814 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -208,7 +208,7 @@ class AutowirePass extends AbstractRecursivePass continue; } $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, false); - $type = $type ? sprintf('is type-hinted "%s"', $type) : 'has no type-hint'; + $type = $type ? sprintf('is type-hinted "%s"', ltrim($type, '\\')) : 'has no type-hint'; throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" %s, you should configure its value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method, $type)); } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php index 0051fb5ac3..7972fcffbe 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php @@ -83,7 +83,7 @@ class ResolveBindingsPass extends AbstractRecursivePass $this->unusedBindings[$bindingId] = array($key, $this->currentId); } - if (isset($key[0]) && '$' === $key[0]) { + if (preg_match('/^(?:(?:array|bool|float|int|string) )?\$/', $key)) { continue; } @@ -123,15 +123,21 @@ class ResolveBindingsPass extends AbstractRecursivePass continue; } + $typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter); + + if (array_key_exists($k = ltrim($typeHint, '\\').' $'.$parameter->name, $bindings)) { + $arguments[$key] = $this->getBindingValue($bindings[$k]); + + continue; + } + if (array_key_exists('$'.$parameter->name, $bindings)) { $arguments[$key] = $this->getBindingValue($bindings['$'.$parameter->name]); continue; } - $typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true); - - if (!isset($bindings[$typeHint])) { + if (!$typeHint || '\\' !== $typeHint[0] || !isset($bindings[$typeHint = substr($typeHint, 1)])) { continue; } diff --git a/src/Symfony/Component/DependencyInjection/Definition.php b/src/Symfony/Component/DependencyInjection/Definition.php index b6ddff186a..807ce13a07 100644 --- a/src/Symfony/Component/DependencyInjection/Definition.php +++ b/src/Symfony/Component/DependencyInjection/Definition.php @@ -859,6 +859,10 @@ class Definition public function setBindings(array $bindings) { foreach ($bindings as $key => $binding) { + if (0 < strpos($key, '$') && $key !== $k = preg_replace('/[ \t]*\$/', ' $', $key)) { + unset($bindings[$key]); + $bindings[$key = $k] = $binding; + } if (!$binding instanceof BoundArgument) { $bindings[$key] = new BoundArgument($binding); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/BindTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/BindTrait.php index 4511ed659d..5d6c57cb3b 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/BindTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/BindTrait.php @@ -31,7 +31,7 @@ trait BindTrait final public function bind($nameOrFqcn, $valueOrRef) { $valueOrRef = static::processValue($valueOrRef, true); - if (isset($nameOrFqcn[0]) && '$' !== $nameOrFqcn[0] && !$valueOrRef instanceof Reference) { + if (!preg_match('/^(?:(?:array|bool|float|int|string)[ \t]*+)?\$/', $nameOrFqcn) && !$valueOrRef instanceof Reference) { throw new InvalidArgumentException(sprintf('Invalid binding for service "%s": named arguments must start with a "$", and FQCN must map to references. Neither applies to binding "%s".', $this->id, $nameOrFqcn)); } $bindings = $this->definition->getBindings(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php index 65f5ceb80f..48f9620e8c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php @@ -111,4 +111,28 @@ class ResolveBindingsPassTest extends TestCase $this->assertEquals(array(array('setDefaultLocale', array('fr'))), $definition->getMethodCalls()); } + + public function testTupleBinding() + { + $container = new ContainerBuilder(); + + $bindings = array( + '$c' => new BoundArgument(new Reference('bar')), + CaseSensitiveClass::class.'$c' => new BoundArgument(new Reference('foo')), + ); + + $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class); + $definition->addMethodCall('setSensitiveClass'); + $definition->addMethodCall('setAnotherC'); + $definition->setBindings($bindings); + + $pass = new ResolveBindingsPass(); + $pass->process($container); + + $expected = array( + array('setSensitiveClass', array(new Reference('foo'))), + array('setAnotherC', array(new Reference('bar'))), + ); + $this->assertEquals($expected, $definition->getMethodCalls()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NamedArgumentsDummy.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NamedArgumentsDummy.php index 09d907dfae..802ba84271 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NamedArgumentsDummy.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/NamedArgumentsDummy.php @@ -18,4 +18,8 @@ class NamedArgumentsDummy public function setSensitiveClass(CaseSensitiveClass $c) { } + + public function setAnotherC($c) + { + } } diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php index 490b595b94..c9387423b8 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php @@ -122,7 +122,7 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface $args = array(); foreach ($parameters as $p) { /** @var \ReflectionParameter $p */ - $type = $target = ProxyHelper::getTypeHint($r, $p, true); + $type = ltrim($target = ProxyHelper::getTypeHint($r, $p), '\\'); $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; if (isset($arguments[$r->name][$p->name])) { @@ -134,7 +134,7 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface } elseif ($p->allowsNull() && !$p->isOptional()) { $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; } - } elseif (isset($bindings[$bindingName = '$'.$p->name]) || isset($bindings[$bindingName = $type])) { + } elseif (isset($bindings[$bindingName = $type.' $'.$p->name]) || isset($bindings[$bindingName = '$'.$p->name]) || isset($bindings[$bindingName = $type])) { $binding = $bindings[$bindingName]; list($bindingValue, $bindingId) = $binding->getValues(); @@ -150,7 +150,7 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface } continue; - } elseif (!$type || !$autowire) { + } elseif (!$type || !$autowire || '\\' !== $target[0]) { continue; } elseif (!$p->allowsNull()) { $invalidBehavior = ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE; @@ -171,6 +171,7 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface throw new InvalidArgumentException($message); } + $target = ltrim($target, '\\'); $args[$p->name] = $type ? new TypedReference($target, $type, $invalidBehavior) : new Reference($target, $invalidBehavior); } // register the maps as a per-method service-locators diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php index 98c77f4aa3..189515496c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php @@ -308,16 +308,23 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase public function provideBindings() { - return array(array(ControllerDummy::class), array('$bar')); + return array( + array(ControllerDummy::class.'$bar'), + array(ControllerDummy::class), + array('$bar'), + ); } - public function testBindScalarValueToControllerArgument() + /** + * @dataProvider provideBindScalarValueToControllerArgument + */ + public function testBindScalarValueToControllerArgument($bindingKey) { $container = new ContainerBuilder(); $resolver = $container->register('argument_resolver.service')->addArgument(array()); $container->register('foo', ArgumentWithoutTypeController::class) - ->setBindings(array('$someArg' => '%foo%')) + ->setBindings(array($bindingKey => '%foo%')) ->addTag('controller.service_arguments'); $container->setParameter('foo', 'foo_val'); @@ -339,6 +346,12 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase $this->assertTrue($container->has((string) $reference)); $this->assertSame('foo_val', $container->get((string) $reference)); } + + public function provideBindScalarValueToControllerArgument() + { + yield array('$someArg'); + yield array('string $someArg'); + } } class RegisterTestController @@ -396,7 +409,7 @@ class NonExistentClassOptionalController class ArgumentWithoutTypeController { - public function fooAction($someArg) + public function fooAction(string $someArg) { } }