TaggedHandlersPassTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. <?php
  2. /**
  3. * @file
  4. * Contains \Drupal\Tests\Core\DependencyInjection\Compiler\TaggedHandlersPassTest.
  5. */
  6. namespace Drupal\Tests\Core\DependencyInjection\Compiler;
  7. use Drupal\Core\DependencyInjection\Compiler\TaggedHandlersPass;
  8. use Drupal\Tests\UnitTestCase;
  9. use Symfony\Component\DependencyInjection\ContainerBuilder;
  10. use Symfony\Component\DependencyInjection\Exception\LogicException;
  11. use Symfony\Component\DependencyInjection\Reference;
  12. /**
  13. * @coversDefaultClass \Drupal\Core\DependencyInjection\Compiler\TaggedHandlersPass
  14. * @group DependencyInjection
  15. */
  16. class TaggedHandlersPassTest extends UnitTestCase {
  17. protected function buildContainer($environment = 'dev') {
  18. $container = new ContainerBuilder();
  19. $container->setParameter('kernel.environment', $environment);
  20. return $container;
  21. }
  22. /**
  23. * Tests without any consumers.
  24. *
  25. * @covers ::process
  26. */
  27. public function testProcessNoConsumers() {
  28. $container = $this->buildContainer();
  29. $container
  30. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumer');
  31. $handler_pass = new TaggedHandlersPass();
  32. $handler_pass->process($container);
  33. $this->assertCount(2, $container->getDefinitions());
  34. $this->assertFalse($container->getDefinition('consumer_id')->hasMethodCall('addHandler'));
  35. }
  36. /**
  37. * Tests a required consumer with no handlers.
  38. *
  39. * @covers ::process
  40. */
  41. public function testProcessRequiredHandlers() {
  42. $container = $this->buildContainer();
  43. $container
  44. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumer')
  45. ->addTag('service_collector', [
  46. 'required' => TRUE,
  47. ]);
  48. $handler_pass = new TaggedHandlersPass();
  49. $this->expectException(LogicException::class);
  50. $this->expectExceptionMessage("At least one service tagged with 'consumer_id' is required.");
  51. $handler_pass->process($container);
  52. }
  53. /**
  54. * Tests a required consumer with no handlers.
  55. *
  56. * @covers ::process
  57. * @covers ::processServiceIdCollectorPass
  58. */
  59. public function testIdCollectorProcessRequiredHandlers() {
  60. $this->expectException(LogicException::class);
  61. $this->expectExceptionMessage("At least one service tagged with 'consumer_id' is required.");
  62. $container = $this->buildContainer();
  63. $container
  64. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumer')
  65. ->addTag('service_id_collector', [
  66. 'required' => TRUE,
  67. ]);
  68. $handler_pass = new TaggedHandlersPass();
  69. $handler_pass->process($container);
  70. }
  71. /**
  72. * Tests consumer with missing interface in non-production environment.
  73. *
  74. * @covers ::process
  75. */
  76. public function testProcessMissingInterface() {
  77. $container = $this->buildContainer();
  78. $container
  79. ->register('consumer_id0', __NAMESPACE__ . '\ValidConsumer')
  80. ->addTag('service_collector');
  81. $container
  82. ->register('consumer_id1', __NAMESPACE__ . '\InvalidConsumer')
  83. ->addTag('service_collector');
  84. $handler_pass = new TaggedHandlersPass();
  85. $this->expectException(LogicException::class);
  86. $this->expectExceptionMessage("Service consumer 'consumer_id1' class method Drupal\Tests\Core\DependencyInjection\Compiler\InvalidConsumer::addHandler() has to type-hint an interface.");
  87. $handler_pass->process($container);
  88. }
  89. /**
  90. * Tests one consumer and two handlers.
  91. *
  92. * @covers ::process
  93. */
  94. public function testProcess() {
  95. $container = $this->buildContainer();
  96. $container
  97. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumer')
  98. ->addTag('service_collector');
  99. $container
  100. ->register('handler1', __NAMESPACE__ . '\ValidHandler')
  101. ->addTag('consumer_id');
  102. $container
  103. ->register('handler2', __NAMESPACE__ . '\ValidHandler')
  104. ->addTag('consumer_id');
  105. $handler_pass = new TaggedHandlersPass();
  106. $handler_pass->process($container);
  107. $method_calls = $container->getDefinition('consumer_id')->getMethodCalls();
  108. $this->assertCount(2, $method_calls);
  109. }
  110. /**
  111. * Tests one consumer and two handlers with service ID collection.
  112. *
  113. * @covers ::process
  114. */
  115. public function testserviceIdProcess() {
  116. $container = $this->buildContainer();
  117. $container
  118. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumer')
  119. ->addTag('service_id_collector');
  120. $container
  121. ->register('handler1', __NAMESPACE__ . '\ValidHandler')
  122. ->addTag('consumer_id');
  123. $container
  124. ->register('handler2', __NAMESPACE__ . '\ValidHandler')
  125. ->addTag('consumer_id');
  126. $handler_pass = new TaggedHandlersPass();
  127. $handler_pass->process($container);
  128. $arguments = $container->getDefinition('consumer_id')->getArguments();
  129. $this->assertCount(1, $arguments);
  130. $this->assertCount(2, $arguments[0]);
  131. }
  132. /**
  133. * Tests handler priority sorting.
  134. *
  135. * @covers ::process
  136. */
  137. public function testProcessPriority() {
  138. $container = $this->buildContainer();
  139. $container
  140. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumer')
  141. ->addTag('service_collector');
  142. $container
  143. ->register('handler1', __NAMESPACE__ . '\ValidHandler')
  144. ->addTag('consumer_id');
  145. $container
  146. ->register('handler2', __NAMESPACE__ . '\ValidHandler')
  147. ->addTag('consumer_id', [
  148. 'priority' => 10,
  149. ]);
  150. $handler_pass = new TaggedHandlersPass();
  151. $handler_pass->process($container);
  152. $method_calls = $container->getDefinition('consumer_id')->getMethodCalls();
  153. $this->assertCount(2, $method_calls);
  154. $this->assertEquals(new Reference('handler2'), $method_calls[0][1][0]);
  155. $this->assertEquals(10, $method_calls[0][1][1]);
  156. $this->assertEquals(new Reference('handler1'), $method_calls[1][1][0]);
  157. $this->assertEquals(0, $method_calls[1][1][1]);
  158. }
  159. /**
  160. * Tests handler priority sorting for service ID collection.
  161. *
  162. * @covers ::process
  163. */
  164. public function testserviceIdProcessPriority() {
  165. $container = $this->buildContainer();
  166. $container
  167. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumer')
  168. ->addTag('service_id_collector');
  169. $container
  170. ->register('handler1', __NAMESPACE__ . '\ValidHandler')
  171. ->addTag('consumer_id');
  172. $container
  173. ->register('handler2', __NAMESPACE__ . '\ValidHandler')
  174. ->addTag('consumer_id', [
  175. 'priority' => 20,
  176. ]);
  177. $container
  178. ->register('handler3', __NAMESPACE__ . '\ValidHandler')
  179. ->addTag('consumer_id', [
  180. 'priority' => 10,
  181. ]);
  182. $handler_pass = new TaggedHandlersPass();
  183. $handler_pass->process($container);
  184. $arguments = $container->getDefinition('consumer_id')->getArguments();
  185. $this->assertCount(1, $arguments);
  186. $this->assertSame(['handler2', 'handler3', 'handler1'], $arguments[0]);
  187. }
  188. /**
  189. * Tests consumer method without priority parameter.
  190. *
  191. * @covers ::process
  192. */
  193. public function testProcessNoPriorityParam() {
  194. $container = $this->buildContainer();
  195. $container
  196. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumer')
  197. ->addTag('service_collector', [
  198. 'call' => 'addNoPriority',
  199. ]);
  200. $container
  201. ->register('handler1', __NAMESPACE__ . '\ValidHandler')
  202. ->addTag('consumer_id');
  203. $container
  204. ->register('handler2', __NAMESPACE__ . '\ValidHandler')
  205. ->addTag('consumer_id', [
  206. 'priority' => 10,
  207. ]);
  208. $handler_pass = new TaggedHandlersPass();
  209. $handler_pass->process($container);
  210. $method_calls = $container->getDefinition('consumer_id')->getMethodCalls();
  211. $this->assertCount(2, $method_calls);
  212. $this->assertEquals(new Reference('handler2'), $method_calls[0][1][0]);
  213. $this->assertCount(1, $method_calls[0][1]);
  214. $this->assertEquals(new Reference('handler1'), $method_calls[1][1][0]);
  215. $this->assertCount(1, $method_calls[0][1]);
  216. }
  217. /**
  218. * Tests consumer method with an ID parameter.
  219. *
  220. * @covers ::process
  221. */
  222. public function testProcessWithIdParameter() {
  223. $container = $this->buildContainer();
  224. $container
  225. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumer')
  226. ->addTag('service_collector', [
  227. 'call' => 'addWithId',
  228. ]);
  229. $container
  230. ->register('handler1', __NAMESPACE__ . '\ValidHandler')
  231. ->addTag('consumer_id');
  232. $container
  233. ->register('handler2', __NAMESPACE__ . '\ValidHandler')
  234. ->addTag('consumer_id', [
  235. 'priority' => 10,
  236. ]);
  237. $handler_pass = new TaggedHandlersPass();
  238. $handler_pass->process($container);
  239. $method_calls = $container->getDefinition('consumer_id')->getMethodCalls();
  240. $this->assertCount(2, $method_calls);
  241. $this->assertEquals(new Reference('handler2'), $method_calls[0][1][0]);
  242. $this->assertEquals('handler2', $method_calls[0][1][1]);
  243. $this->assertEquals(10, $method_calls[0][1][2]);
  244. $this->assertEquals(new Reference('handler1'), $method_calls[1][1][0]);
  245. $this->assertEquals('handler1', $method_calls[1][1][1]);
  246. $this->assertEquals(0, $method_calls[1][1][2]);
  247. }
  248. /**
  249. * Tests interface validation in non-production environment.
  250. *
  251. * @covers ::process
  252. */
  253. public function testProcessInterfaceMismatch() {
  254. $container = $this->buildContainer();
  255. $container
  256. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumer')
  257. ->addTag('service_collector');
  258. $container
  259. ->register('handler1', __NAMESPACE__ . '\InvalidHandler')
  260. ->addTag('consumer_id');
  261. $container
  262. ->register('handler2', __NAMESPACE__ . '\ValidHandler')
  263. ->addTag('consumer_id', [
  264. 'priority' => 10,
  265. ]);
  266. $handler_pass = new TaggedHandlersPass();
  267. $this->expectException(LogicException::class);
  268. $handler_pass->process($container);
  269. }
  270. /**
  271. * Tests consumer method with extra parameters.
  272. *
  273. * @covers ::process
  274. */
  275. public function testProcessWithExtraArguments() {
  276. $container = $this->buildContainer();
  277. $container
  278. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumerWithExtraArguments')
  279. ->addTag('service_collector');
  280. $container
  281. ->register('handler1', __NAMESPACE__ . '\ValidHandler')
  282. ->addTag('consumer_id', [
  283. 'extra1' => 'extra1',
  284. 'extra2' => 'extra2',
  285. ]);
  286. $handler_pass = new TaggedHandlersPass();
  287. $handler_pass->process($container);
  288. $method_calls = $container->getDefinition('consumer_id')->getMethodCalls();
  289. $this->assertCount(4, $method_calls[0][1]);
  290. $this->assertEquals(new Reference('handler1'), $method_calls[0][1][0]);
  291. $this->assertEquals(0, $method_calls[0][1][1]);
  292. $this->assertEquals('extra1', $method_calls[0][1][2]);
  293. $this->assertEquals('extra2', $method_calls[0][1][3]);
  294. }
  295. /**
  296. * Tests consumer method with extra parameters and no priority.
  297. *
  298. * @covers ::process
  299. */
  300. public function testProcessNoPriorityAndExtraArguments() {
  301. $container = $this->buildContainer();
  302. $container
  303. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumerWithExtraArguments')
  304. ->addTag('service_collector', [
  305. 'call' => 'addNoPriority',
  306. ]);
  307. $container
  308. ->register('handler1', __NAMESPACE__ . '\ValidHandler')
  309. ->addTag('consumer_id', [
  310. 'extra' => 'extra',
  311. ]);
  312. $handler_pass = new TaggedHandlersPass();
  313. $handler_pass->process($container);
  314. $method_calls = $container->getDefinition('consumer_id')->getMethodCalls();
  315. $this->assertCount(2, $method_calls[0][1]);
  316. $this->assertEquals(new Reference('handler1'), $method_calls[0][1][0]);
  317. $this->assertEquals('extra', $method_calls[0][1][1]);
  318. }
  319. /**
  320. * Tests consumer method with priority, id and extra parameters.
  321. *
  322. * @covers ::process
  323. */
  324. public function testProcessWithIdAndExtraArguments() {
  325. $container = $this->buildContainer();
  326. $container
  327. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumerWithExtraArguments')
  328. ->addTag('service_collector', [
  329. 'call' => 'addWithId',
  330. ]);
  331. $container
  332. ->register('handler1', __NAMESPACE__ . '\ValidHandler')
  333. ->addTag('consumer_id', [
  334. 'extra1' => 'extra1',
  335. ]);
  336. $handler_pass = new TaggedHandlersPass();
  337. $handler_pass->process($container);
  338. $method_calls = $container->getDefinition('consumer_id')->getMethodCalls();
  339. $this->assertCount(5, $method_calls[0][1]);
  340. $this->assertEquals(new Reference('handler1'), $method_calls[0][1][0]);
  341. $this->assertEquals('handler1', $method_calls[0][1][1]);
  342. $this->assertEquals(0, $method_calls[0][1][2]);
  343. $this->assertEquals('extra1', $method_calls[0][1][3]);
  344. $this->assertNull($method_calls[0][1][4]);
  345. }
  346. /**
  347. * Tests consumer method with priority and extra parameters in different order.
  348. *
  349. * @covers ::process
  350. */
  351. public function testProcessWithDifferentArgumentsOrderAndDefaultValue() {
  352. $container = $this->buildContainer();
  353. $container
  354. ->register('consumer_id', __NAMESPACE__ . '\ValidConsumerWithExtraArguments')
  355. ->addTag('service_collector', [
  356. 'call' => 'addWithDifferentOrder',
  357. ]);
  358. $container
  359. ->register('handler1', __NAMESPACE__ . '\ValidHandler')
  360. ->addTag('consumer_id', [
  361. 'priority' => 0,
  362. 'extra1' => 'extra1',
  363. 'extra3' => 'extra3',
  364. ]);
  365. $handler_pass = new TaggedHandlersPass();
  366. $handler_pass->process($container);
  367. $method_calls = $container->getDefinition('consumer_id')->getMethodCalls();
  368. $this->assertCount(5, $method_calls[0][1]);
  369. $expected = [new Reference('handler1'), 'extra1', 0, 'default2', 'extra3'];
  370. $this->assertEquals($expected, array_values($method_calls[0][1]));
  371. }
  372. }
  373. interface HandlerInterface {
  374. }
  375. class ValidConsumer {
  376. public function addHandler(HandlerInterface $instance, $priority = 0) {
  377. }
  378. public function addNoPriority(HandlerInterface $instance) {
  379. }
  380. public function addWithId(HandlerInterface $instance, $id, $priority = 0) {
  381. }
  382. }
  383. class InvalidConsumer {
  384. public function addHandler($instance, $priority = 0) {
  385. }
  386. }
  387. class ValidConsumerWithExtraArguments {
  388. public function addHandler(HandlerInterface $instance, $priority = 0, $extra1 = '', $extra2 = '') {
  389. }
  390. public function addNoPriority(HandlerInterface $instance, $extra) {
  391. }
  392. public function addWithId(HandlerInterface $instance, $id, $priority = 0, $extra1 = '', $extra2 = NULL) {
  393. }
  394. public function addWithDifferentOrder(HandlerInterface $instance, $extra1, $priority = 0, $extra2 = 'default2', $extra3 = 'default3') {
  395. }
  396. }
  397. class ValidHandler implements HandlerInterface {
  398. }
  399. class InvalidHandler {
  400. }