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\ChildDefinition;
17 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
18 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
19 use Symfony\Component\DependencyInjection\ContainerBuilder;
20 use Symfony\Component\DependencyInjection\ContainerInterface;
21 use Symfony\Component\DependencyInjection\Reference;
22 use Symfony\Component\DependencyInjection\ServiceLocator;
23 use Symfony\Component\DependencyInjection\TypedReference;
24 use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass;
26 class RegisterControllerArgumentLocatorsPassTest extends TestCase
29 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
30 * @expectedExceptionMessage Class "Symfony\Component\HttpKernel\Tests\DependencyInjection\NotFound" used for service "foo" cannot be found.
32 public function testInvalidClass()
34 $container = new ContainerBuilder();
35 $container->register('argument_resolver.service')->addArgument(array());
37 $container->register('foo', NotFound::class)
38 ->addTag('controller.service_arguments')
41 $pass = new RegisterControllerArgumentLocatorsPass();
42 $pass->process($container);
46 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
47 * @expectedExceptionMessage Missing "action" attribute on tag "controller.service_arguments" {"argument":"bar"} for service "foo".
49 public function testNoAction()
51 $container = new ContainerBuilder();
52 $container->register('argument_resolver.service')->addArgument(array());
54 $container->register('foo', RegisterTestController::class)
55 ->addTag('controller.service_arguments', array('argument' => 'bar'))
58 $pass = new RegisterControllerArgumentLocatorsPass();
59 $pass->process($container);
63 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
64 * @expectedExceptionMessage Missing "argument" attribute on tag "controller.service_arguments" {"action":"fooAction"} for service "foo".
66 public function testNoArgument()
68 $container = new ContainerBuilder();
69 $container->register('argument_resolver.service')->addArgument(array());
71 $container->register('foo', RegisterTestController::class)
72 ->addTag('controller.service_arguments', array('action' => 'fooAction'))
75 $pass = new RegisterControllerArgumentLocatorsPass();
76 $pass->process($container);
80 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
81 * @expectedExceptionMessage Missing "id" attribute on tag "controller.service_arguments" {"action":"fooAction","argument":"bar"} for service "foo".
83 public function testNoService()
85 $container = new ContainerBuilder();
86 $container->register('argument_resolver.service')->addArgument(array());
88 $container->register('foo', RegisterTestController::class)
89 ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar'))
92 $pass = new RegisterControllerArgumentLocatorsPass();
93 $pass->process($container);
97 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
98 * @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".
100 public function testInvalidMethod()
102 $container = new ContainerBuilder();
103 $container->register('argument_resolver.service')->addArgument(array());
105 $container->register('foo', RegisterTestController::class)
106 ->addTag('controller.service_arguments', array('action' => 'barAction', 'argument' => 'bar', 'id' => 'bar_service'))
109 $pass = new RegisterControllerArgumentLocatorsPass();
110 $pass->process($container);
114 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
115 * @expectedExceptionMessage Invalid "controller.service_arguments" tag for service "foo": method "fooAction()" has no "baz" argument on class "Symfony\Component\HttpKernel\Tests\DependencyInjection\RegisterTestController".
117 public function testInvalidArgument()
119 $container = new ContainerBuilder();
120 $container->register('argument_resolver.service')->addArgument(array());
122 $container->register('foo', RegisterTestController::class)
123 ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'baz', 'id' => 'bar'))
126 $pass = new RegisterControllerArgumentLocatorsPass();
127 $pass->process($container);
130 public function testAllActions()
132 $container = new ContainerBuilder();
133 $resolver = $container->register('argument_resolver.service')->addArgument(array());
135 $container->register('foo', RegisterTestController::class)
136 ->addTag('controller.service_arguments')
139 $pass = new RegisterControllerArgumentLocatorsPass();
140 $pass->process($container);
142 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
144 $this->assertEquals(array('foo:fooAction'), array_keys($locator));
145 $this->assertInstanceof(ServiceClosureArgument::class, $locator['foo:fooAction']);
147 $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
149 $this->assertSame(ServiceLocator::class, $locator->getClass());
150 $this->assertFalse($locator->isPublic());
152 $expected = array('bar' => new ServiceClosureArgument(new TypedReference(ControllerDummy::class, ControllerDummy::class, RegisterTestController::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)));
153 $this->assertEquals($expected, $locator->getArgument(0));
156 public function testExplicitArgument()
158 $container = new ContainerBuilder();
159 $resolver = $container->register('argument_resolver.service')->addArgument(array());
161 $container->register('foo', RegisterTestController::class)
162 ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => 'bar'))
163 ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => 'baz')) // should be ignored, the first wins
166 $pass = new RegisterControllerArgumentLocatorsPass();
167 $pass->process($container);
169 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
170 $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
172 $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, RegisterTestController::class)));
173 $this->assertEquals($expected, $locator->getArgument(0));
176 public function testOptionalArgument()
178 $container = new ContainerBuilder();
179 $resolver = $container->register('argument_resolver.service')->addArgument(array());
181 $container->register('foo', RegisterTestController::class)
182 ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => '?bar'))
185 $pass = new RegisterControllerArgumentLocatorsPass();
186 $pass->process($container);
188 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
189 $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
191 $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, RegisterTestController::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)));
192 $this->assertEquals($expected, $locator->getArgument(0));
195 public function testSkipSetContainer()
197 $container = new ContainerBuilder();
198 $resolver = $container->register('argument_resolver.service')->addArgument(array());
200 $container->register('foo', ContainerAwareRegisterTestController::class)
201 ->addTag('controller.service_arguments');
203 $pass = new RegisterControllerArgumentLocatorsPass();
204 $pass->process($container);
206 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
207 $this->assertSame(array('foo:fooAction'), array_keys($locator));
211 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
212 * @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?
214 public function testExceptionOnNonExistentTypeHint()
216 $container = new ContainerBuilder();
217 $container->register('argument_resolver.service')->addArgument(array());
219 $container->register('foo', NonExistentClassController::class)
220 ->addTag('controller.service_arguments');
222 $pass = new RegisterControllerArgumentLocatorsPass();
223 $pass->process($container);
227 * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
228 * @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".
230 public function testExceptionOnNonExistentTypeHintDifferentNamespace()
232 $container = new ContainerBuilder();
233 $container->register('argument_resolver.service')->addArgument(array());
235 $container->register('foo', NonExistentClassDifferentNamespaceController::class)
236 ->addTag('controller.service_arguments');
238 $pass = new RegisterControllerArgumentLocatorsPass();
239 $pass->process($container);
242 public function testNoExceptionOnNonExistentTypeHintOptionalArg()
244 $container = new ContainerBuilder();
245 $resolver = $container->register('argument_resolver.service')->addArgument(array());
247 $container->register('foo', NonExistentClassOptionalController::class)
248 ->addTag('controller.service_arguments');
250 $pass = new RegisterControllerArgumentLocatorsPass();
251 $pass->process($container);
253 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
254 $this->assertSame(array('foo:barAction', 'foo:fooAction'), array_keys($locator));
257 public function testArgumentWithNoTypeHintIsOk()
259 $container = new ContainerBuilder();
260 $resolver = $container->register('argument_resolver.service')->addArgument(array());
262 $container->register('foo', ArgumentWithoutTypeController::class)
263 ->addTag('controller.service_arguments');
265 $pass = new RegisterControllerArgumentLocatorsPass();
266 $pass->process($container);
268 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
269 $this->assertEmpty(array_keys($locator));
272 public function testControllersAreMadePublic()
274 $container = new ContainerBuilder();
275 $resolver = $container->register('argument_resolver.service')->addArgument(array());
277 $container->register('foo', ArgumentWithoutTypeController::class)
279 ->addTag('controller.service_arguments');
281 $pass = new RegisterControllerArgumentLocatorsPass();
282 $pass->process($container);
284 $this->assertTrue($container->getDefinition('foo')->isPublic());
288 * @dataProvider provideBindings
290 public function testBindings($bindingName)
292 $container = new ContainerBuilder();
293 $resolver = $container->register('argument_resolver.service')->addArgument(array());
295 $container->register('foo', RegisterTestController::class)
296 ->setBindings(array($bindingName => new Reference('foo')))
297 ->addTag('controller.service_arguments');
299 $pass = new RegisterControllerArgumentLocatorsPass();
300 $pass->process($container);
302 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
304 $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
306 $expected = array('bar' => new ServiceClosureArgument(new Reference('foo')));
307 $this->assertEquals($expected, $locator->getArgument(0));
310 public function provideBindings()
312 return array(array(ControllerDummy::class), array('$bar'));
315 public function testDoNotBindScalarValueToControllerArgument()
317 $container = new ContainerBuilder();
318 $resolver = $container->register('argument_resolver.service')->addArgument(array());
320 $container->register('foo', ArgumentWithoutTypeController::class)
321 ->setBindings(array('$someArg' => '%foo%'))
322 ->addTag('controller.service_arguments');
324 $pass = new RegisterControllerArgumentLocatorsPass();
325 $pass->process($container);
327 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
328 $this->assertEmpty($locator);
331 public function testBindingsOnChildDefinitions()
333 $container = new ContainerBuilder();
334 $resolver = $container->register('argument_resolver.service')->addArgument(array());
336 $container->register('parent', ArgumentWithoutTypeController::class);
338 $container->setDefinition('child', (new ChildDefinition('parent'))
339 ->setBindings(array('$someArg' => new Reference('parent')))
340 ->addTag('controller.service_arguments')
343 $pass = new RegisterControllerArgumentLocatorsPass();
344 $pass->process($container);
346 $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
347 $this->assertInstanceOf(ServiceClosureArgument::class, $locator['child:fooAction']);
349 $locator = $container->getDefinition((string) $locator['child:fooAction']->getValues()[0])->getArgument(0);
350 $this->assertInstanceOf(ServiceClosureArgument::class, $locator['someArg']);
351 $this->assertEquals(new Reference('parent'), $locator['someArg']->getValues()[0]);
355 class RegisterTestController
357 public function __construct(ControllerDummy $bar)
361 public function fooAction(ControllerDummy $bar)
365 protected function barAction(ControllerDummy $bar)
370 class ContainerAwareRegisterTestController implements ContainerAwareInterface
372 use ContainerAwareTrait;
374 public function fooAction(ControllerDummy $bar)
379 class ControllerDummy
383 class NonExistentClassController
385 public function fooAction(NonExistentClass $nonExistent)
390 class NonExistentClassDifferentNamespaceController
392 public function fooAction(\Acme\NonExistentClass $nonExistent)
397 class NonExistentClassOptionalController
399 public function fooAction(NonExistentClass $nonExistent = null)
403 public function barAction(NonExistentClass $nonExistent = null, $bar)
408 class ArgumentWithoutTypeController
410 public function fooAction($someArg)