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\DependencyInjection;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
16 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
17 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
18 use Symfony\Component\DependencyInjection\ContainerBuilder;
19 use Symfony\Component\DependencyInjection\ContainerInterface;
20 use Symfony\Component\DependencyInjection\ServiceLocator;
21 use Symfony\Component\DependencyInjection\TypedReference;
22 use Symfony\Component\DependencyInjection\Reference;
23 use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass;
25 class RegisterControllerArgumentLocatorsPassTest extends TestCase
28 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
29 * @expectedExceptionMessage Class "Symfony\Component\HttpKernel\Tests\DependencyInjection\NotFound" used for service "foo" cannot be found.
31 public function testInvalidClass()
33 $container = new ContainerBuilder();
34 $container->register('argument_resolver.service')->addArgument(array());
36 $container->register('foo', NotFound::class)
37 ->addTag('controller.service_arguments')
40 $pass = new RegisterControllerArgumentLocatorsPass();
41 $pass->process($container);
45 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
46 * @expectedExceptionMessage Missing "action" attribute on tag "controller.service_arguments" {"argument":"bar"} for service "foo".
48 public function testNoAction()
50 $container = new ContainerBuilder();
51 $container->register('argument_resolver.service')->addArgument(array());
53 $container->register('foo', RegisterTestController::class)
54 ->addTag('controller.service_arguments', array('argument' => 'bar'))
57 $pass = new RegisterControllerArgumentLocatorsPass();
58 $pass->process($container);
62 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
63 * @expectedExceptionMessage Missing "argument" attribute on tag "controller.service_arguments" {"action":"fooAction"} for service "foo".
65 public function testNoArgument()
67 $container = new ContainerBuilder();
68 $container->register('argument_resolver.service')->addArgument(array());
70 $container->register('foo', RegisterTestController::class)
71 ->addTag('controller.service_arguments', array('action' => 'fooAction'))
74 $pass = new RegisterControllerArgumentLocatorsPass();
75 $pass->process($container);
79 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
80 * @expectedExceptionMessage Missing "id" attribute on tag "controller.service_arguments" {"action":"fooAction","argument":"bar"} for service "foo".
82 public function testNoService()
84 $container = new ContainerBuilder();
85 $container->register('argument_resolver.service')->addArgument(array());
87 $container->register('foo', RegisterTestController::class)
88 ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar'))
91 $pass = new RegisterControllerArgumentLocatorsPass();
92 $pass->process($container);
96 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
97 * @expectedExceptionMessage Invalid "action" attribute on tag "controller.service_arguments" for service "foo": no public "barAction()" method found on class "Symfony\Component\HttpKernel\Tests\DependencyInjection\RegisterTestController".
99 public function testInvalidMethod()
101 $container = new ContainerBuilder();
102 $container->register('argument_resolver.service')->addArgument(array());
104 $container->register('foo', RegisterTestController::class)
105 ->addTag('controller.service_arguments', array('action' => 'barAction', 'argument' => 'bar', 'id' => 'bar_service'))
108 $pass = new RegisterControllerArgumentLocatorsPass();
109 $pass->process($container);
113 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
114 * @expectedExceptionMessage Invalid "controller.service_arguments" tag for service "foo": method "fooAction()" has no "baz" argument on class "Symfony\Component\HttpKernel\Tests\DependencyInjection\RegisterTestController".
116 public function testInvalidArgument()
118 $container = new ContainerBuilder();
119 $container->register('argument_resolver.service')->addArgument(array());
121 $container->register('foo', RegisterTestController::class)
122 ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'baz', 'id' => 'bar'))
125 $pass = new RegisterControllerArgumentLocatorsPass();
126 $pass->process($container);
129 public function testAllActions()
131 $container = new ContainerBuilder();
132 $resolver = $container->register('argument_resolver.service')->addArgument(array());
134 $container->register('foo', RegisterTestController::class)
135 ->addTag('controller.service_arguments')
138 $pass = new RegisterControllerArgumentLocatorsPass();
139 $pass->process($container);
141 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
143 $this->assertEquals(array('foo:fooAction'), array_keys($locator));
144 $this->assertInstanceof(ServiceClosureArgument::class, $locator['foo:fooAction']);
146 $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
148 $this->assertSame(ServiceLocator::class, $locator->getClass());
149 $this->assertFalse($locator->isPublic());
151 $expected = array('bar' => new ServiceClosureArgument(new TypedReference(ControllerDummy::class, ControllerDummy::class, RegisterTestController::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)));
152 $this->assertEquals($expected, $locator->getArgument(0));
155 public function testExplicitArgument()
157 $container = new ContainerBuilder();
158 $resolver = $container->register('argument_resolver.service')->addArgument(array());
160 $container->register('foo', RegisterTestController::class)
161 ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => 'bar'))
162 ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => 'baz')) // should be ignored, the first wins
165 $pass = new RegisterControllerArgumentLocatorsPass();
166 $pass->process($container);
168 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
169 $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
171 $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, RegisterTestController::class)));
172 $this->assertEquals($expected, $locator->getArgument(0));
175 public function testOptionalArgument()
177 $container = new ContainerBuilder();
178 $resolver = $container->register('argument_resolver.service')->addArgument(array());
180 $container->register('foo', RegisterTestController::class)
181 ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => '?bar'))
184 $pass = new RegisterControllerArgumentLocatorsPass();
185 $pass->process($container);
187 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
188 $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
190 $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, RegisterTestController::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)));
191 $this->assertEquals($expected, $locator->getArgument(0));
194 public function testSkipSetContainer()
196 $container = new ContainerBuilder();
197 $resolver = $container->register('argument_resolver.service')->addArgument(array());
199 $container->register('foo', ContainerAwareRegisterTestController::class)
200 ->addTag('controller.service_arguments');
202 $pass = new RegisterControllerArgumentLocatorsPass();
203 $pass->process($container);
205 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
206 $this->assertSame(array('foo:fooAction'), array_keys($locator));
210 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
211 * @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?
213 public function testExceptionOnNonExistentTypeHint()
215 $container = new ContainerBuilder();
216 $container->register('argument_resolver.service')->addArgument(array());
218 $container->register('foo', NonExistentClassController::class)
219 ->addTag('controller.service_arguments');
221 $pass = new RegisterControllerArgumentLocatorsPass();
222 $pass->process($container);
226 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
227 * @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".
229 public function testExceptionOnNonExistentTypeHintDifferentNamespace()
231 $container = new ContainerBuilder();
232 $container->register('argument_resolver.service')->addArgument(array());
234 $container->register('foo', NonExistentClassDifferentNamespaceController::class)
235 ->addTag('controller.service_arguments');
237 $pass = new RegisterControllerArgumentLocatorsPass();
238 $pass->process($container);
241 public function testNoExceptionOnNonExistentTypeHintOptionalArg()
243 $container = new ContainerBuilder();
244 $resolver = $container->register('argument_resolver.service')->addArgument(array());
246 $container->register('foo', NonExistentClassOptionalController::class)
247 ->addTag('controller.service_arguments');
249 $pass = new RegisterControllerArgumentLocatorsPass();
250 $pass->process($container);
252 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
253 $this->assertSame(array('foo:barAction', 'foo:fooAction'), array_keys($locator));
256 public function testArgumentWithNoTypeHintIsOk()
258 $container = new ContainerBuilder();
259 $resolver = $container->register('argument_resolver.service')->addArgument(array());
261 $container->register('foo', ArgumentWithoutTypeController::class)
262 ->addTag('controller.service_arguments');
264 $pass = new RegisterControllerArgumentLocatorsPass();
265 $pass->process($container);
267 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
268 $this->assertEmpty(array_keys($locator));
271 public function testControllersAreMadePublic()
273 $container = new ContainerBuilder();
274 $resolver = $container->register('argument_resolver.service')->addArgument(array());
276 $container->register('foo', ArgumentWithoutTypeController::class)
278 ->addTag('controller.service_arguments');
280 $pass = new RegisterControllerArgumentLocatorsPass();
281 $pass->process($container);
283 $this->assertTrue($container->getDefinition('foo')->isPublic());
287 * @dataProvider provideBindings
289 public function testBindings($bindingName)
291 $container = new ContainerBuilder();
292 $resolver = $container->register('argument_resolver.service')->addArgument(array());
294 $container->register('foo', RegisterTestController::class)
295 ->setBindings(array($bindingName => new Reference('foo')))
296 ->addTag('controller.service_arguments');
298 $pass = new RegisterControllerArgumentLocatorsPass();
299 $pass->process($container);
301 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
303 $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
305 $expected = array('bar' => new ServiceClosureArgument(new Reference('foo')));
306 $this->assertEquals($expected, $locator->getArgument(0));
309 public function provideBindings()
311 return array(array(ControllerDummy::class), array('$bar'));
314 public function testDoNotBindScalarValueToControllerArgument()
316 $container = new ContainerBuilder();
317 $resolver = $container->register('argument_resolver.service')->addArgument(array());
319 $container->register('foo', ArgumentWithoutTypeController::class)
320 ->setBindings(array('$someArg' => '%foo%'))
321 ->addTag('controller.service_arguments');
323 $pass = new RegisterControllerArgumentLocatorsPass();
324 $pass->process($container);
326 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
327 $this->assertEmpty($locator);
331 class RegisterTestController
333 public function __construct(ControllerDummy $bar)
337 public function fooAction(ControllerDummy $bar)
341 protected function barAction(ControllerDummy $bar)
346 class ContainerAwareRegisterTestController implements ContainerAwareInterface
348 use ContainerAwareTrait;
350 public function fooAction(ControllerDummy $bar)
355 class ControllerDummy
359 class NonExistentClassController
361 public function fooAction(NonExistentClass $nonExistent)
366 class NonExistentClassDifferentNamespaceController
368 public function fooAction(\Acme\NonExistentClass $nonExistent)
373 class NonExistentClassOptionalController
375 public function fooAction(NonExistentClass $nonExistent = null)
379 public function barAction(NonExistentClass $nonExistent = null, $bar)
384 class ArgumentWithoutTypeController
386 public function fooAction($someArg)