RegisterControllerArgumentLocatorsPassTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpKernel\Tests\DependencyInjection;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
  13. use Symfony\Component\DependencyInjection\ContainerAwareInterface;
  14. use Symfony\Component\DependencyInjection\ContainerAwareTrait;
  15. use Symfony\Component\DependencyInjection\ContainerBuilder;
  16. use Symfony\Component\DependencyInjection\ContainerInterface;
  17. use Symfony\Component\DependencyInjection\ServiceLocator;
  18. use Symfony\Component\DependencyInjection\TypedReference;
  19. use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass;
  20. class RegisterControllerArgumentLocatorsPassTest extends TestCase
  21. {
  22. /**
  23. * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
  24. * @expectedExceptionMessage Class "Symfony\Component\HttpKernel\Tests\DependencyInjection\NotFound" used for service "foo" cannot be found.
  25. */
  26. public function testInvalidClass()
  27. {
  28. $container = new ContainerBuilder();
  29. $container->register('argument_resolver.service')->addArgument(array());
  30. $container->register('foo', NotFound::class)
  31. ->addTag('controller.service_arguments')
  32. ;
  33. $pass = new RegisterControllerArgumentLocatorsPass();
  34. $pass->process($container);
  35. }
  36. /**
  37. * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
  38. * @expectedExceptionMessage Missing "action" attribute on tag "controller.service_arguments" {"argument":"bar"} for service "foo".
  39. */
  40. public function testNoAction()
  41. {
  42. $container = new ContainerBuilder();
  43. $container->register('argument_resolver.service')->addArgument(array());
  44. $container->register('foo', RegisterTestController::class)
  45. ->addTag('controller.service_arguments', array('argument' => 'bar'))
  46. ;
  47. $pass = new RegisterControllerArgumentLocatorsPass();
  48. $pass->process($container);
  49. }
  50. /**
  51. * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
  52. * @expectedExceptionMessage Missing "argument" attribute on tag "controller.service_arguments" {"action":"fooAction"} for service "foo".
  53. */
  54. public function testNoArgument()
  55. {
  56. $container = new ContainerBuilder();
  57. $container->register('argument_resolver.service')->addArgument(array());
  58. $container->register('foo', RegisterTestController::class)
  59. ->addTag('controller.service_arguments', array('action' => 'fooAction'))
  60. ;
  61. $pass = new RegisterControllerArgumentLocatorsPass();
  62. $pass->process($container);
  63. }
  64. /**
  65. * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
  66. * @expectedExceptionMessage Missing "id" attribute on tag "controller.service_arguments" {"action":"fooAction","argument":"bar"} for service "foo".
  67. */
  68. public function testNoService()
  69. {
  70. $container = new ContainerBuilder();
  71. $container->register('argument_resolver.service')->addArgument(array());
  72. $container->register('foo', RegisterTestController::class)
  73. ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar'))
  74. ;
  75. $pass = new RegisterControllerArgumentLocatorsPass();
  76. $pass->process($container);
  77. }
  78. /**
  79. * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
  80. * @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".
  81. */
  82. public function testInvalidMethod()
  83. {
  84. $container = new ContainerBuilder();
  85. $container->register('argument_resolver.service')->addArgument(array());
  86. $container->register('foo', RegisterTestController::class)
  87. ->addTag('controller.service_arguments', array('action' => 'barAction', 'argument' => 'bar', 'id' => 'bar_service'))
  88. ;
  89. $pass = new RegisterControllerArgumentLocatorsPass();
  90. $pass->process($container);
  91. }
  92. /**
  93. * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
  94. * @expectedExceptionMessage Invalid "controller.service_arguments" tag for service "foo": method "fooAction()" has no "baz" argument on class "Symfony\Component\HttpKernel\Tests\DependencyInjection\RegisterTestController".
  95. */
  96. public function testInvalidArgument()
  97. {
  98. $container = new ContainerBuilder();
  99. $container->register('argument_resolver.service')->addArgument(array());
  100. $container->register('foo', RegisterTestController::class)
  101. ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'baz', 'id' => 'bar'))
  102. ;
  103. $pass = new RegisterControllerArgumentLocatorsPass();
  104. $pass->process($container);
  105. }
  106. public function testAllActions()
  107. {
  108. $container = new ContainerBuilder();
  109. $resolver = $container->register('argument_resolver.service')->addArgument(array());
  110. $container->register('foo', RegisterTestController::class)
  111. ->addTag('controller.service_arguments')
  112. ;
  113. $pass = new RegisterControllerArgumentLocatorsPass();
  114. $pass->process($container);
  115. $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
  116. $this->assertEquals(array('foo:fooAction'), array_keys($locator));
  117. $this->assertInstanceof(ServiceClosureArgument::class, $locator['foo:fooAction']);
  118. $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
  119. $this->assertSame(ServiceLocator::class, $locator->getClass());
  120. $this->assertFalse($locator->isPublic());
  121. $expected = array('bar' => new ServiceClosureArgument(new TypedReference(ControllerDummy::class, ControllerDummy::class, RegisterTestController::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)));
  122. $this->assertEquals($expected, $locator->getArgument(0));
  123. }
  124. public function testExplicitArgument()
  125. {
  126. $container = new ContainerBuilder();
  127. $resolver = $container->register('argument_resolver.service')->addArgument(array());
  128. $container->register('foo', RegisterTestController::class)
  129. ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => 'bar'))
  130. ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => 'baz')) // should be ignored, the first wins
  131. ;
  132. $pass = new RegisterControllerArgumentLocatorsPass();
  133. $pass->process($container);
  134. $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
  135. $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
  136. $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, RegisterTestController::class)));
  137. $this->assertEquals($expected, $locator->getArgument(0));
  138. }
  139. public function testOptionalArgument()
  140. {
  141. $container = new ContainerBuilder();
  142. $resolver = $container->register('argument_resolver.service')->addArgument(array());
  143. $container->register('foo', RegisterTestController::class)
  144. ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => '?bar'))
  145. ;
  146. $pass = new RegisterControllerArgumentLocatorsPass();
  147. $pass->process($container);
  148. $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
  149. $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]);
  150. $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, RegisterTestController::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)));
  151. $this->assertEquals($expected, $locator->getArgument(0));
  152. }
  153. public function testSkipSetContainer()
  154. {
  155. $container = new ContainerBuilder();
  156. $resolver = $container->register('argument_resolver.service')->addArgument(array());
  157. $container->register('foo', ContainerAwareRegisterTestController::class)
  158. ->addTag('controller.service_arguments');
  159. $pass = new RegisterControllerArgumentLocatorsPass();
  160. $pass->process($container);
  161. $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
  162. $this->assertSame(array('foo:fooAction'), array_keys($locator));
  163. }
  164. /**
  165. * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
  166. * @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?
  167. */
  168. public function testExceptionOnNonExistentTypeHint()
  169. {
  170. $container = new ContainerBuilder();
  171. $container->register('argument_resolver.service')->addArgument(array());
  172. $container->register('foo', NonExistentClassController::class)
  173. ->addTag('controller.service_arguments');
  174. $pass = new RegisterControllerArgumentLocatorsPass();
  175. $pass->process($container);
  176. }
  177. /**
  178. * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
  179. * @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".
  180. */
  181. public function testExceptionOnNonExistentTypeHintDifferentNamespace()
  182. {
  183. $container = new ContainerBuilder();
  184. $container->register('argument_resolver.service')->addArgument(array());
  185. $container->register('foo', NonExistentClassDifferentNamespaceController::class)
  186. ->addTag('controller.service_arguments');
  187. $pass = new RegisterControllerArgumentLocatorsPass();
  188. $pass->process($container);
  189. }
  190. public function testNoExceptionOnNonExistentTypeHintOptionalArg()
  191. {
  192. $container = new ContainerBuilder();
  193. $resolver = $container->register('argument_resolver.service')->addArgument(array());
  194. $container->register('foo', NonExistentClassOptionalController::class)
  195. ->addTag('controller.service_arguments');
  196. $pass = new RegisterControllerArgumentLocatorsPass();
  197. $pass->process($container);
  198. $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
  199. $this->assertSame(array('foo:barAction', 'foo:fooAction'), array_keys($locator));
  200. }
  201. public function testArgumentWithNoTypeHintIsOk()
  202. {
  203. $container = new ContainerBuilder();
  204. $resolver = $container->register('argument_resolver.service')->addArgument(array());
  205. $container->register('foo', ArgumentWithoutTypeController::class)
  206. ->addTag('controller.service_arguments');
  207. $pass = new RegisterControllerArgumentLocatorsPass();
  208. $pass->process($container);
  209. $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0);
  210. $this->assertEmpty(array_keys($locator));
  211. }
  212. }
  213. class RegisterTestController
  214. {
  215. public function __construct(ControllerDummy $bar)
  216. {
  217. }
  218. public function fooAction(ControllerDummy $bar)
  219. {
  220. }
  221. protected function barAction(ControllerDummy $bar)
  222. {
  223. }
  224. }
  225. class ContainerAwareRegisterTestController implements ContainerAwareInterface
  226. {
  227. use ContainerAwareTrait;
  228. public function fooAction(ControllerDummy $bar)
  229. {
  230. }
  231. }
  232. class ControllerDummy
  233. {
  234. }
  235. class NonExistentClassController
  236. {
  237. public function fooAction(NonExistentClass $nonExistent)
  238. {
  239. }
  240. }
  241. class NonExistentClassDifferentNamespaceController
  242. {
  243. public function fooAction(\Acme\NonExistentClass $nonExistent)
  244. {
  245. }
  246. }
  247. class NonExistentClassOptionalController
  248. {
  249. public function fooAction(NonExistentClass $nonExistent = null)
  250. {
  251. }
  252. public function barAction(NonExistentClass $nonExistent = null, $bar)
  253. {
  254. }
  255. }
  256. class ArgumentWithoutTypeController
  257. {
  258. public function fooAction($someArg)
  259. {
  260. }
  261. }