Throw a sensible exception when controller has been removed

This commit is contained in:
Samuel ROZE 2017-12-05 16:52:04 +00:00
parent 22a6a7e4c5
commit 458d63fbb9
No known key found for this signature in database
GPG Key ID: 835426F55A19FB84
2 changed files with 85 additions and 12 deletions

View File

@ -63,19 +63,28 @@ class ContainerControllerResolver extends ControllerResolver
return parent::createController($controller);
}
$method = null;
if (1 == substr_count($controller, ':')) {
// controller in the "service:method" notation
list($service, $method) = explode(':', $controller, 2);
return array($this->container->get($service), $method);
list($controller, $method) = explode(':', $controller, 2);
}
if ($this->container->has($controller) && method_exists($service = $this->container->get($controller), '__invoke')) {
// invokable controller in the "service" notation
return $service;
if (!$this->container->has($controller)) {
$this->throwExceptionIfControllerWasRemoved($controller);
throw new \LogicException(sprintf('Controller not found: service "%s" does not exist.', $controller));
}
throw new \LogicException(sprintf('Unable to parse the controller name "%s".', $controller));
$service = $this->container->get($controller);
if (null !== $method) {
return array($service, $method);
}
if (!method_exists($service, '__invoke')) {
throw new \LogicException(sprintf('Controller "%s" cannot be called without a method name. Did you forget an "__invoke" method?', $controller));
}
return $service;
}
/**
@ -94,10 +103,19 @@ class ContainerControllerResolver extends ControllerResolver
} catch (\TypeError $e) {
}
if ($this->container instanceof Container && isset($this->container->getRemovedIds()[$class])) {
throw new \LogicException(sprintf('Controller "%s" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"?', $class), 0, $e);
}
$this->throwExceptionIfControllerWasRemoved($class, $e);
throw $e;
}
/**
* @param string $controller
* @param \Exception|\Throwable|null $previous
*/
private function throwExceptionIfControllerWasRemoved($controller, $previous = null)
{
if ($this->container instanceof Container && isset($this->container->getRemovedIds()[$controller])) {
throw new \LogicException(sprintf('Controller "%s" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"?', $controller), 0, $previous);
}
}
}

View File

@ -23,6 +23,10 @@ class ContainerControllerResolverTest extends ControllerResolverTest
public function testGetControllerService()
{
$container = $this->createMockContainer();
$container->expects($this->once())
->method('has')
->with('foo')
->will($this->returnValue(true));
$container->expects($this->once())
->method('get')
->with('foo')
@ -175,6 +179,57 @@ class ContainerControllerResolverTest extends ControllerResolverTest
$this->assertSame(array($service, 'action'), $controller);
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Controller "app.my_controller" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"?
*/
public function testExceptionWhenUsingRemovedControllerService()
{
$container = $this->getMockBuilder(Container::class)->getMock();
$container->expects($this->at(0))
->method('has')
->with('app.my_controller')
->will($this->returnValue(false))
;
$container->expects($this->atLeastOnce())
->method('getRemovedIds')
->with()
->will($this->returnValue(array('app.my_controller' => true)))
;
$resolver = $this->createControllerResolver(null, $container);
$request = Request::create('/');
$request->attributes->set('_controller', 'app.my_controller');
$resolver->getController($request);
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Controller "app.my_controller" cannot be called without a method name. Did you forget an "__invoke" method?
*/
public function testExceptionWhenUsingControllerWithoutAnInvokeMethod()
{
$container = $this->getMockBuilder(Container::class)->getMock();
$container->expects($this->once())
->method('has')
->with('app.my_controller')
->will($this->returnValue(true))
;
$container->expects($this->once())
->method('get')
->with('app.my_controller')
->will($this->returnValue(new ImpossibleConstructController('toto', 'controller')))
;
$resolver = $this->createControllerResolver(null, $container);
$request = Request::create('/');
$request->attributes->set('_controller', 'app.my_controller');
$resolver->getController($request);
}
/**
* @dataProvider getUndefinedControllers
*/
@ -197,9 +252,9 @@ class ContainerControllerResolverTest extends ControllerResolverTest
public function getUndefinedControllers()
{
return array(
array('foo', \LogicException::class, '/Unable to parse the controller name "foo"\./'),
array('foo', \LogicException::class, '/Controller not found: service "foo" does not exist\./'),
array('oof::bar', \InvalidArgumentException::class, '/Class "oof" does not exist\./'),
array('stdClass', \LogicException::class, '/Unable to parse the controller name "stdClass"\./'),
array('stdClass', \LogicException::class, '/Controller not found: service "stdClass" does not exist\./'),
array(
'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar',
\InvalidArgumentException::class,