4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\HttpKernel\Tests\Controller;
14 use Psr\Container\ContainerInterface;
15 use Psr\Log\LoggerInterface;
16 use Symfony\Component\Debug\ErrorHandler;
17 use Symfony\Component\DependencyInjection\Container;
18 use Symfony\Component\HttpFoundation\Request;
19 use Symfony\Component\HttpKernel\Controller\ContainerControllerResolver;
21 class ContainerControllerResolverTest extends ControllerResolverTest
23 public function testGetControllerService()
25 $container = $this->createMockContainer();
26 $container->expects($this->once())
29 ->will($this->returnValue(true));
30 $container->expects($this->once())
33 ->will($this->returnValue($this))
36 $resolver = $this->createControllerResolver(null, $container);
37 $request = Request::create('/');
38 $request->attributes->set('_controller', 'foo:controllerMethod1');
40 $controller = $resolver->getController($request);
42 $this->assertInstanceOf(get_class($this), $controller[0]);
43 $this->assertSame('controllerMethod1', $controller[1]);
46 public function testGetControllerInvokableService()
48 $invokableController = new InvokableController('bar');
50 $container = $this->createMockContainer();
51 $container->expects($this->once())
54 ->will($this->returnValue(true))
56 $container->expects($this->once())
59 ->will($this->returnValue($invokableController))
62 $resolver = $this->createControllerResolver(null, $container);
63 $request = Request::create('/');
64 $request->attributes->set('_controller', 'foo');
66 $controller = $resolver->getController($request);
68 $this->assertEquals($invokableController, $controller);
71 public function testGetControllerInvokableServiceWithClassNameAsName()
73 $invokableController = new InvokableController('bar');
74 $className = __NAMESPACE__.'\InvokableController';
76 $container = $this->createMockContainer();
77 $container->expects($this->once())
80 ->will($this->returnValue(true))
82 $container->expects($this->once())
85 ->will($this->returnValue($invokableController))
88 $resolver = $this->createControllerResolver(null, $container);
89 $request = Request::create('/');
90 $request->attributes->set('_controller', $className);
92 $controller = $resolver->getController($request);
94 $this->assertEquals($invokableController, $controller);
97 public function testNonInstantiableController()
99 $container = $this->createMockContainer();
100 $container->expects($this->once())
102 ->with(NonInstantiableController::class)
103 ->will($this->returnValue(false))
106 $resolver = $this->createControllerResolver(null, $container);
107 $request = Request::create('/');
108 $request->attributes->set('_controller', array(NonInstantiableController::class, 'action'));
110 $controller = $resolver->getController($request);
112 $this->assertSame(array(NonInstantiableController::class, 'action'), $controller);
116 * @expectedException \LogicException
117 * @expectedExceptionMessage Controller "Symfony\Component\HttpKernel\Tests\Controller\ImpossibleConstructController" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"?
119 public function testNonConstructController()
121 $container = $this->getMockBuilder(Container::class)->getMock();
122 $container->expects($this->at(0))
124 ->with(ImpossibleConstructController::class)
125 ->will($this->returnValue(true))
128 $container->expects($this->at(1))
130 ->with(ImpossibleConstructController::class)
131 ->will($this->returnValue(false))
134 $container->expects($this->atLeastOnce())
135 ->method('getRemovedIds')
137 ->will($this->returnValue(array(ImpossibleConstructController::class => true)))
140 $resolver = $this->createControllerResolver(null, $container);
141 $request = Request::create('/');
142 $request->attributes->set('_controller', array(ImpossibleConstructController::class, 'action'));
144 if (\PHP_VERSION_ID < 70100) {
145 ErrorHandler::register();
147 $resolver->getController($request);
149 restore_error_handler();
150 restore_exception_handler();
153 $resolver->getController($request);
157 public function testNonInstantiableControllerWithCorrespondingService()
159 $service = new \stdClass();
161 $container = $this->createMockContainer();
162 $container->expects($this->atLeastOnce())
164 ->with(NonInstantiableController::class)
165 ->will($this->returnValue(true))
167 $container->expects($this->atLeastOnce())
169 ->with(NonInstantiableController::class)
170 ->will($this->returnValue($service))
173 $resolver = $this->createControllerResolver(null, $container);
174 $request = Request::create('/');
175 $request->attributes->set('_controller', array(NonInstantiableController::class, 'action'));
177 $controller = $resolver->getController($request);
179 $this->assertSame(array($service, 'action'), $controller);
183 * @expectedException \LogicException
184 * @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"?
186 public function testExceptionWhenUsingRemovedControllerService()
188 $container = $this->getMockBuilder(Container::class)->getMock();
189 $container->expects($this->at(0))
191 ->with('app.my_controller')
192 ->will($this->returnValue(false))
195 $container->expects($this->atLeastOnce())
196 ->method('getRemovedIds')
198 ->will($this->returnValue(array('app.my_controller' => true)))
201 $resolver = $this->createControllerResolver(null, $container);
203 $request = Request::create('/');
204 $request->attributes->set('_controller', 'app.my_controller');
205 $resolver->getController($request);
209 * @expectedException \LogicException
210 * @expectedExceptionMessage Controller "app.my_controller" cannot be called without a method name. Did you forget an "__invoke" method?
212 public function testExceptionWhenUsingControllerWithoutAnInvokeMethod()
214 $container = $this->getMockBuilder(Container::class)->getMock();
215 $container->expects($this->once())
217 ->with('app.my_controller')
218 ->will($this->returnValue(true))
220 $container->expects($this->once())
222 ->with('app.my_controller')
223 ->will($this->returnValue(new ImpossibleConstructController('toto', 'controller')))
226 $resolver = $this->createControllerResolver(null, $container);
228 $request = Request::create('/');
229 $request->attributes->set('_controller', 'app.my_controller');
230 $resolver->getController($request);
234 * @dataProvider getUndefinedControllers
236 public function testGetControllerOnNonUndefinedFunction($controller, $exceptionName = null, $exceptionMessage = null)
238 // All this logic needs to be duplicated, since calling parent::testGetControllerOnNonUndefinedFunction will override the expected excetion and not use the regex
239 $resolver = $this->createControllerResolver();
240 if (method_exists($this, 'expectException')) {
241 $this->expectException($exceptionName);
242 $this->expectExceptionMessageRegExp($exceptionMessage);
244 $this->setExpectedExceptionRegExp($exceptionName, $exceptionMessage);
247 $request = Request::create('/');
248 $request->attributes->set('_controller', $controller);
249 $resolver->getController($request);
252 public function getUndefinedControllers()
255 array('foo', \LogicException::class, '/Controller not found: service "foo" does not exist\./'),
256 array('oof::bar', \InvalidArgumentException::class, '/Class "oof" does not exist\./'),
257 array('stdClass', \LogicException::class, '/Controller not found: service "stdClass" does not exist\./'),
259 'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar',
260 \InvalidArgumentException::class,
261 '/.?[cC]ontroller(.*?) for URI "\/" is not callable\.( Expected method(.*) Available methods)?/',
266 protected function createControllerResolver(LoggerInterface $logger = null, ContainerInterface $container = null)
269 $container = $this->createMockContainer();
272 return new ContainerControllerResolver($container, $logger);
275 protected function createMockContainer()
277 return $this->getMockBuilder(ContainerInterface::class)->getMock();
281 class InvokableController
283 public function __construct($bar) // mandatory argument to prevent automatic instantiation
287 public function __invoke()
292 abstract class NonInstantiableController
294 public static function action()
299 class ImpossibleConstructController
301 public function __construct($toto, $controller)
305 public function action()