Making the RegisterControllerArgumentLocatorsPass throw an exception on a bad type-hint
This commit is contained in:
parent
ae9bda1ffc
commit
22b905226d
@ -110,9 +110,12 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
|
||||
}
|
||||
|
||||
foreach ($methods as list($r, $parameters)) {
|
||||
/** @var \ReflectionMethod $r */
|
||||
|
||||
// create a per-method map of argument-names to service/type-references
|
||||
$args = array();
|
||||
foreach ($parameters as $p) {
|
||||
/** @var \ReflectionParameter $p */
|
||||
$type = $target = ProxyHelper::getTypeHint($r, $p, true);
|
||||
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
|
||||
|
||||
@ -129,6 +132,17 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($type && !$p->isOptional() && !$p->allowsNull() && !class_exists($type) && !interface_exists($type, false)) {
|
||||
$message = sprintf('Cannot determine controller argument for "%s::%s()": the $%s argument is type-hinted with the non-existent class or interface: "%s".', $class, $r->name, $p->name, $type);
|
||||
|
||||
// see if the type-hint lives in the same namespace as the controller
|
||||
if (0 === strncmp($type, $class, strrpos($class, '\\'))) {
|
||||
$message .= ' Did you forget to add a use statement?';
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException($message);
|
||||
}
|
||||
|
||||
$args[$p->name] = $type ? new TypedReference($target, $type, $r->class, $invalidBehavior) : new Reference($target, $invalidBehavior);
|
||||
}
|
||||
// register the maps as a per-method service-locators
|
||||
|
@ -204,6 +204,68 @@ class RegisterControllerArgumentLocatorsPassTest extends TestCase
|
||||
$locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
|
||||
$this->assertSame(array('foo:fooAction'), array_keys($locator));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Cannot determine controller argument for "Symfony\Component\HttpKernel\Tests\DependencyInjection\NonExistentClassController::fooAction()": the $nonExistent argument is type-hinted with the non-existent class or interface: "Symfony\Component\HttpKernel\Tests\DependencyInjection\NonExistentClass". Did you forget to add a use statement?
|
||||
*/
|
||||
public function testExceptionOnNonExistentTypeHint()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', NonExistentClassController::class)
|
||||
->addTag('controller.service_arguments');
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Cannot determine controller argument for "Symfony\Component\HttpKernel\Tests\DependencyInjection\NonExistentClassDifferentNamespaceController::fooAction()": the $nonExistent argument is type-hinted with the non-existent class or interface: "Acme\NonExistentClass".
|
||||
*/
|
||||
public function testExceptionOnNonExistentTypeHintDifferentNamespace()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', NonExistentClassDifferentNamespaceController::class)
|
||||
->addTag('controller.service_arguments');
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
public function testNoExceptionOnNonExistentTypeHintOptionalArg()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$resolver = $container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', NonExistentClassOptionalController::class)
|
||||
->addTag('controller.service_arguments');
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
|
||||
$locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
|
||||
$this->assertSame(array('foo:barAction', 'foo:fooAction'), array_keys($locator));
|
||||
}
|
||||
|
||||
public function testArgumentWithNoTypeHintIsOk()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$resolver = $container->register('argument_resolver.service')->addArgument(array());
|
||||
|
||||
$container->register('foo', ArgumentWithoutTypeController::class)
|
||||
->addTag('controller.service_arguments');
|
||||
|
||||
$pass = new RegisterControllerArgumentLocatorsPass();
|
||||
$pass->process($container);
|
||||
|
||||
$locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
|
||||
$this->assertEmpty(array_keys($locator));
|
||||
}
|
||||
}
|
||||
|
||||
class RegisterTestController
|
||||
@ -233,3 +295,35 @@ class ContainerAwareRegisterTestController implements ContainerAwareInterface
|
||||
class ControllerDummy
|
||||
{
|
||||
}
|
||||
|
||||
class NonExistentClassController
|
||||
{
|
||||
public function fooAction(NonExistentClass $nonExistent)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class NonExistentClassDifferentNamespaceController
|
||||
{
|
||||
public function fooAction(\Acme\NonExistentClass $nonExistent)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class NonExistentClassOptionalController
|
||||
{
|
||||
public function fooAction(NonExistentClass $nonExistent = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function barAction(NonExistentClass $nonExistent = null, $bar)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class ArgumentWithoutTypeController
|
||||
{
|
||||
public function fooAction($someArg)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ class RemoveEmptyControllerArgumentLocatorsPassTest extends TestCase
|
||||
|
||||
class RemoveTestController1
|
||||
{
|
||||
public function fooAction(\stdClass $bar, NotFound $baz)
|
||||
public function fooAction(\stdClass $bar, ClassNotInContainer $baz)
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -131,7 +131,7 @@ class RemoveTestController2
|
||||
{
|
||||
}
|
||||
|
||||
public function fooAction(NotFound $bar)
|
||||
public function fooAction(ClassNotInContainer $bar)
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -142,3 +142,7 @@ class InvokableRegisterTestController
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class ClassNotInContainer
|
||||
{
|
||||
}
|
||||
|
Reference in New Issue
Block a user