ContainerTest.php 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239
  1. <?php
  2. /**
  3. * @file
  4. * Contains \Drupal\Tests\Component\DependencyInjection\ContainerTest.
  5. */
  6. namespace Drupal\Tests\Component\DependencyInjection;
  7. use Drupal\Component\Utility\Crypt;
  8. use PHPUnit\Framework\TestCase;
  9. use Symfony\Component\DependencyInjection\ContainerInterface;
  10. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  11. use Symfony\Component\DependencyInjection\Exception\LogicException;
  12. use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
  13. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  14. use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
  15. use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  16. use Prophecy\Argument;
  17. /**
  18. * @coversDefaultClass \Drupal\Component\DependencyInjection\Container
  19. * @group DependencyInjection
  20. */
  21. class ContainerTest extends TestCase {
  22. /**
  23. * The tested container.
  24. *
  25. * @var \Symfony\Component\DependencyInjection\ContainerInterface
  26. */
  27. protected $container;
  28. /**
  29. * The container definition used for the test.
  30. *
  31. * @var array
  32. */
  33. protected $containerDefinition;
  34. /**
  35. * The container class to be tested.
  36. *
  37. * @var bool
  38. */
  39. protected $containerClass;
  40. /**
  41. * Whether the container uses the machine-optimized format or not.
  42. *
  43. * @var bool
  44. */
  45. protected $machineFormat;
  46. /**
  47. * {@inheritdoc}
  48. */
  49. protected function setUp() {
  50. $this->machineFormat = TRUE;
  51. $this->containerClass = '\Drupal\Component\DependencyInjection\Container';
  52. $this->containerDefinition = $this->getMockContainerDefinition();
  53. $this->container = new $this->containerClass($this->containerDefinition);
  54. }
  55. /**
  56. * Tests that passing a non-supported format throws an InvalidArgumentException.
  57. *
  58. * @covers ::__construct
  59. */
  60. public function testConstruct() {
  61. $container_definition = $this->getMockContainerDefinition();
  62. $container_definition['machine_format'] = !$this->machineFormat;
  63. if (method_exists($this, 'expectException')) {
  64. $this->expectException(InvalidArgumentException::class);
  65. }
  66. else {
  67. $this->setExpectedException(InvalidArgumentException::class);
  68. }
  69. $container = new $this->containerClass($container_definition);
  70. }
  71. /**
  72. * Tests that Container::getParameter() works properly.
  73. *
  74. * @covers ::getParameter
  75. */
  76. public function testGetParameter() {
  77. $this->assertEquals($this->containerDefinition['parameters']['some_config'], $this->container->getParameter('some_config'), 'Container parameter matches for %some_config%.');
  78. $this->assertEquals($this->containerDefinition['parameters']['some_other_config'], $this->container->getParameter('some_other_config'), 'Container parameter matches for %some_other_config%.');
  79. }
  80. /**
  81. * Tests that Container::getParameter() works properly for non-existing
  82. * parameters.
  83. *
  84. * @covers ::getParameter
  85. * @covers ::getParameterAlternatives
  86. * @covers ::getAlternatives
  87. */
  88. public function testGetParameterIfNotFound() {
  89. if (method_exists($this, 'expectException')) {
  90. $this->expectException(ParameterNotFoundException::class);
  91. }
  92. else {
  93. $this->setExpectedException(ParameterNotFoundException::class);
  94. }
  95. $this->container->getParameter('parameter_that_does_not_exist');
  96. }
  97. /**
  98. * Tests that Container::getParameter() works properly for NULL parameters.
  99. *
  100. * @covers ::getParameter
  101. */
  102. public function testGetParameterIfNotFoundBecauseNull() {
  103. if (method_exists($this, 'expectException')) {
  104. $this->expectException(ParameterNotFoundException::class);
  105. }
  106. else {
  107. $this->setExpectedException(ParameterNotFoundException::class);
  108. }
  109. $this->container->getParameter(NULL);
  110. }
  111. /**
  112. * Tests that Container::hasParameter() works properly.
  113. *
  114. * @covers ::hasParameter
  115. */
  116. public function testHasParameter() {
  117. $this->assertTrue($this->container->hasParameter('some_config'), 'Container parameters include %some_config%.');
  118. $this->assertFalse($this->container->hasParameter('some_config_not_exists'), 'Container parameters do not include %some_config_not_exists%.');
  119. }
  120. /**
  121. * Tests that Container::setParameter() in an unfrozen case works properly.
  122. *
  123. * @covers ::setParameter
  124. */
  125. public function testSetParameterWithUnfrozenContainer() {
  126. $container_definition = $this->containerDefinition;
  127. $container_definition['frozen'] = FALSE;
  128. $this->container = new $this->containerClass($container_definition);
  129. $this->container->setParameter('some_config', 'new_value');
  130. $this->assertEquals('new_value', $this->container->getParameter('some_config'), 'Container parameters can be set.');
  131. }
  132. /**
  133. * Tests that Container::setParameter() in a frozen case works properly.
  134. *
  135. * @covers ::setParameter
  136. */
  137. public function testSetParameterWithFrozenContainer() {
  138. $this->container = new $this->containerClass($this->containerDefinition);
  139. if (method_exists($this, 'expectException')) {
  140. $this->expectException(LogicException::class);
  141. }
  142. else {
  143. $this->setExpectedException(LogicException::class);
  144. }
  145. $this->container->setParameter('some_config', 'new_value');
  146. }
  147. /**
  148. * Tests that Container::get() works properly.
  149. *
  150. * @covers ::get
  151. * @covers ::createService
  152. */
  153. public function testGet() {
  154. $container = $this->container->get('service_container');
  155. $this->assertSame($this->container, $container, 'Container can be retrieved from itself.');
  156. // Retrieve services of the container.
  157. $other_service_class = $this->containerDefinition['services']['other.service']['class'];
  158. $other_service = $this->container->get('other.service');
  159. $this->assertInstanceOf($other_service_class, $other_service, 'other.service has the right class.');
  160. $some_parameter = $this->containerDefinition['parameters']['some_config'];
  161. $some_other_parameter = $this->containerDefinition['parameters']['some_other_config'];
  162. $service = $this->container->get('service.provider');
  163. $this->assertEquals($other_service, $service->getSomeOtherService(), '@other.service was injected via constructor.');
  164. $this->assertEquals($some_parameter, $service->getSomeParameter(), '%some_config% was injected via constructor.');
  165. $this->assertEquals($this->container, $service->getContainer(), 'Container was injected via setter injection.');
  166. $this->assertEquals($some_other_parameter, $service->getSomeOtherParameter(), '%some_other_config% was injected via setter injection.');
  167. $this->assertEquals($service->_someProperty, 'foo', 'Service has added properties.');
  168. }
  169. /**
  170. * Tests that Container::get() for non-shared services works properly.
  171. *
  172. * @covers ::get
  173. * @covers ::createService
  174. */
  175. public function testGetForNonSharedService() {
  176. $service = $this->container->get('non_shared_service');
  177. $service2 = $this->container->get('non_shared_service');
  178. $this->assertNotSame($service, $service2, 'Non shared services are always re-instantiated.');
  179. }
  180. /**
  181. * Tests that Container::get() works properly for class from parameters.
  182. *
  183. * @covers ::get
  184. * @covers ::createService
  185. */
  186. public function testGetForClassFromParameter() {
  187. $container_definition = $this->containerDefinition;
  188. $container_definition['frozen'] = FALSE;
  189. $container = new $this->containerClass($container_definition);
  190. $other_service_class = $this->containerDefinition['parameters']['some_parameter_class'];
  191. $other_service = $container->get('other.service_class_from_parameter');
  192. $this->assertInstanceOf($other_service_class, $other_service, 'other.service_class_from_parameter has the right class.');
  193. }
  194. /**
  195. * Tests that Container::set() works properly.
  196. *
  197. * @covers ::set
  198. */
  199. public function testSet() {
  200. $this->assertNull($this->container->get('new_id', ContainerInterface::NULL_ON_INVALID_REFERENCE));
  201. $mock_service = new MockService();
  202. $this->container->set('new_id', $mock_service);
  203. $this->assertSame($mock_service, $this->container->get('new_id'), 'A manual set service works as expected.');
  204. }
  205. /**
  206. * Tests that Container::has() works properly.
  207. *
  208. * @covers ::has
  209. */
  210. public function testHas() {
  211. $this->assertTrue($this->container->has('other.service'));
  212. $this->assertFalse($this->container->has('another.service'));
  213. // Set the service manually, ensure that its also respected.
  214. $mock_service = new MockService();
  215. $this->container->set('another.service', $mock_service);
  216. $this->assertTrue($this->container->has('another.service'));
  217. }
  218. /**
  219. * Tests that Container::has() for aliased services works properly.
  220. *
  221. * @covers ::has
  222. */
  223. public function testHasForAliasedService() {
  224. $service = $this->container->has('service.provider');
  225. $aliased_service = $this->container->has('service.provider_alias');
  226. $this->assertSame($service, $aliased_service);
  227. }
  228. /**
  229. * Tests that Container::get() for circular dependencies works properly.
  230. * @covers ::get
  231. * @covers ::createService
  232. */
  233. public function testGetForCircularServices() {
  234. if (method_exists($this, 'expectException')) {
  235. $this->expectException(ServiceCircularReferenceException::class);
  236. }
  237. else {
  238. $this->setExpectedException(ServiceCircularReferenceException::class);
  239. }
  240. $this->container->get('circular_dependency');
  241. }
  242. /**
  243. * Tests that Container::get() for non-existent services works properly.
  244. *
  245. * @covers ::get
  246. * @covers ::createService
  247. * @covers ::getAlternatives
  248. * @covers ::getServiceAlternatives
  249. */
  250. public function testGetForNonExistantService() {
  251. if (method_exists($this, 'expectException')) {
  252. $this->expectException(ServiceNotFoundException::class);
  253. }
  254. else {
  255. $this->setExpectedException(ServiceNotFoundException::class);
  256. }
  257. $this->container->get('service_not_exists');
  258. }
  259. /**
  260. * Tests that Container::get() for a serialized definition works properly.
  261. *
  262. * @covers ::get
  263. * @covers ::createService
  264. */
  265. public function testGetForSerializedServiceDefinition() {
  266. $container_definition = $this->containerDefinition;
  267. $container_definition['services']['other.service'] = serialize($container_definition['services']['other.service']);
  268. $container = new $this->containerClass($container_definition);
  269. // Retrieve services of the container.
  270. $other_service_class = $this->containerDefinition['services']['other.service']['class'];
  271. $other_service = $container->get('other.service');
  272. $this->assertInstanceOf($other_service_class, $other_service, 'other.service has the right class.');
  273. $service = $container->get('service.provider');
  274. $this->assertEquals($other_service, $service->getSomeOtherService(), '@other.service was injected via constructor.');
  275. }
  276. /**
  277. * Tests that Container::get() for non-existent parameters works properly.
  278. *
  279. * @covers ::get
  280. * @covers ::createService
  281. * @covers ::resolveServicesAndParameters
  282. */
  283. public function testGetForNonExistantParameterDependency() {
  284. $service = $this->container->get('service_parameter_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE);
  285. $this->assertNull($service, 'Service is NULL.');
  286. }
  287. /**
  288. * Tests Container::get() with an exception due to missing parameter on the second call.
  289. *
  290. * @covers ::get
  291. * @covers ::createService
  292. * @covers ::resolveServicesAndParameters
  293. */
  294. public function testGetForParameterDependencyWithExceptionOnSecondCall() {
  295. $service = $this->container->get('service_parameter_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE);
  296. $this->assertNull($service, 'Service is NULL.');
  297. // Reset the service.
  298. $this->container->set('service_parameter_not_exists', NULL);
  299. if (method_exists($this, 'expectException')) {
  300. $this->expectException(InvalidArgumentException::class);
  301. }
  302. else {
  303. $this->setExpectedException(InvalidArgumentException::class);
  304. }
  305. $this->container->get('service_parameter_not_exists');
  306. }
  307. /**
  308. * Tests that Container::get() for non-existent parameters works properly.
  309. *
  310. * @covers ::get
  311. * @covers ::createService
  312. * @covers ::resolveServicesAndParameters
  313. */
  314. public function testGetForNonExistantParameterDependencyWithException() {
  315. if (method_exists($this, 'expectException')) {
  316. $this->expectException(InvalidArgumentException::class);
  317. }
  318. else {
  319. $this->setExpectedException(InvalidArgumentException::class);
  320. }
  321. $this->container->get('service_parameter_not_exists');
  322. }
  323. /**
  324. * Tests that Container::get() for non-existent dependencies works properly.
  325. *
  326. * @covers ::get
  327. * @covers ::createService
  328. * @covers ::resolveServicesAndParameters
  329. */
  330. public function testGetForNonExistantServiceDependency() {
  331. $service = $this->container->get('service_dependency_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE);
  332. $this->assertNull($service, 'Service is NULL.');
  333. }
  334. /**
  335. * Tests that Container::get() for non-existent dependencies works properly.
  336. *
  337. * @covers ::get
  338. * @covers ::createService
  339. * @covers ::resolveServicesAndParameters
  340. * @covers ::getAlternatives
  341. */
  342. public function testGetForNonExistantServiceDependencyWithException() {
  343. if (method_exists($this, 'expectException')) {
  344. $this->expectException(ServiceNotFoundException::class);
  345. }
  346. else {
  347. $this->setExpectedException(ServiceNotFoundException::class);
  348. }
  349. $this->container->get('service_dependency_not_exists');
  350. }
  351. /**
  352. * Tests that Container::get() for non-existent services works properly.
  353. *
  354. * @covers ::get
  355. * @covers ::createService
  356. */
  357. public function testGetForNonExistantServiceWhenUsingNull() {
  358. $this->assertNull($this->container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does not throw exception.');
  359. }
  360. /**
  361. * Tests that Container::get() for NULL service works properly.
  362. * @covers ::get
  363. * @covers ::createService
  364. */
  365. public function testGetForNonExistantNULLService() {
  366. if (method_exists($this, 'expectException')) {
  367. $this->expectException(ServiceNotFoundException::class);
  368. }
  369. else {
  370. $this->setExpectedException(ServiceNotFoundException::class);
  371. }
  372. $this->container->get(NULL);
  373. }
  374. /**
  375. * Tests multiple Container::get() calls for non-existing dependencies work.
  376. *
  377. * @covers ::get
  378. * @covers ::createService
  379. */
  380. public function testGetForNonExistantServiceMultipleTimes() {
  381. $container = new $this->containerClass();
  382. $this->assertNull($container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does not throw exception.');
  383. $this->assertNull($container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does not throw exception on second call.');
  384. }
  385. /**
  386. * Tests multiple Container::get() calls with exception on the second time.
  387. *
  388. * @covers ::get
  389. * @covers ::createService
  390. * @covers ::getAlternatives
  391. */
  392. public function testGetForNonExistantServiceWithExceptionOnSecondCall() {
  393. $this->assertNull($this->container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does nto throw exception.');
  394. if (method_exists($this, 'expectException')) {
  395. $this->expectException(ServiceNotFoundException::class);
  396. }
  397. else {
  398. $this->setExpectedException(ServiceNotFoundException::class);
  399. }
  400. $this->container->get('service_not_exists');
  401. }
  402. /**
  403. * Tests that Container::get() for aliased services works properly.
  404. *
  405. * @covers ::get
  406. * @covers ::createService
  407. */
  408. public function testGetForAliasedService() {
  409. $service = $this->container->get('service.provider');
  410. $aliased_service = $this->container->get('service.provider_alias');
  411. $this->assertSame($service, $aliased_service);
  412. }
  413. /**
  414. * Tests that Container::get() for synthetic services works - if defined.
  415. *
  416. * @covers ::get
  417. * @covers ::createService
  418. */
  419. public function testGetForSyntheticService() {
  420. $synthetic_service = new \stdClass();
  421. $this->container->set('synthetic', $synthetic_service);
  422. $test_service = $this->container->get('synthetic');
  423. $this->assertSame($synthetic_service, $test_service);
  424. }
  425. /**
  426. * Tests that Container::get() for synthetic services throws an Exception if not defined.
  427. *
  428. * @covers ::get
  429. * @covers ::createService
  430. */
  431. public function testGetForSyntheticServiceWithException() {
  432. if (method_exists($this, 'expectException')) {
  433. $this->expectException(RuntimeException::class);
  434. }
  435. else {
  436. $this->setExpectedException(RuntimeException::class);
  437. }
  438. $this->container->get('synthetic');
  439. }
  440. /**
  441. * Tests that Container::get() for services with file includes works.
  442. *
  443. * @covers ::get
  444. * @covers ::createService
  445. */
  446. public function testGetWithFileInclude() {
  447. $file_service = $this->container->get('container_test_file_service_test');
  448. $this->assertTrue(function_exists('container_test_file_service_test_service_function'));
  449. $this->assertEquals('Hello Container', container_test_file_service_test_service_function());
  450. }
  451. /**
  452. * Tests that Container::get() for various arguments lengths works.
  453. *
  454. * @covers ::get
  455. * @covers ::createService
  456. * @covers ::resolveServicesAndParameters
  457. */
  458. public function testGetForInstantiationWithVariousArgumentLengths() {
  459. $args = [];
  460. for ($i = 0; $i < 12; $i++) {
  461. $instantiation_service = $this->container->get('service_test_instantiation_' . $i);
  462. $this->assertEquals($args, $instantiation_service->getArguments());
  463. $args[] = 'arg_' . $i;
  464. }
  465. }
  466. /**
  467. * Tests that Container::get() for wrong factories works correctly.
  468. *
  469. * @covers ::get
  470. * @covers ::createService
  471. */
  472. public function testGetForWrongFactory() {
  473. if (method_exists($this, 'expectException')) {
  474. $this->expectException(RuntimeException::class);
  475. }
  476. else {
  477. $this->setExpectedException(RuntimeException::class);
  478. }
  479. $this->container->get('wrong_factory');
  480. }
  481. /**
  482. * Tests Container::get() for factories via services (Symfony 2.7.0).
  483. *
  484. * @covers ::get
  485. * @covers ::createService
  486. */
  487. public function testGetForFactoryService() {
  488. $factory_service = $this->container->get('factory_service');
  489. $factory_service_class = $this->container->getParameter('factory_service_class');
  490. $this->assertInstanceOf($factory_service_class, $factory_service);
  491. }
  492. /**
  493. * Tests that Container::get() for factories via class works (Symfony 2.7.0).
  494. *
  495. * @covers ::get
  496. * @covers ::createService
  497. */
  498. public function testGetForFactoryClass() {
  499. $service = $this->container->get('service.provider');
  500. $factory_service = $this->container->get('factory_class');
  501. $this->assertInstanceOf(get_class($service), $factory_service);
  502. $this->assertEquals('bar', $factory_service->getSomeParameter(), 'Correct parameter was passed via the factory class instantiation.');
  503. $this->assertEquals($this->container, $factory_service->getContainer(), 'Container was injected via setter injection.');
  504. }
  505. /**
  506. * Tests that Container::get() for configurable services throws an Exception.
  507. *
  508. * @covers ::get
  509. * @covers ::createService
  510. */
  511. public function testGetForConfiguratorWithException() {
  512. if (method_exists($this, 'expectException')) {
  513. $this->expectException(InvalidArgumentException::class);
  514. }
  515. else {
  516. $this->setExpectedException(InvalidArgumentException::class);
  517. }
  518. $this->container->get('configurable_service_exception');
  519. }
  520. /**
  521. * Tests that Container::get() for configurable services works.
  522. *
  523. * @covers ::get
  524. * @covers ::createService
  525. */
  526. public function testGetForConfigurator() {
  527. $container = $this->container;
  528. // Setup a configurator.
  529. $configurator = $this->prophesize('\Drupal\Tests\Component\DependencyInjection\MockConfiguratorInterface');
  530. $configurator->configureService(Argument::type('object'))
  531. ->shouldBeCalled(1)
  532. ->will(function ($args) use ($container) {
  533. $args[0]->setContainer($container);
  534. });
  535. $container->set('configurator', $configurator->reveal());
  536. // Test that the configurator worked.
  537. $service = $container->get('configurable_service');
  538. $this->assertSame($container, $service->getContainer(), 'Container was injected via configurator.');
  539. }
  540. /**
  541. * Tests that private services work correctly.
  542. *
  543. * @covers ::get
  544. * @covers ::createService
  545. * @covers ::resolveServicesAndParameters
  546. */
  547. public function testResolveServicesAndParametersForPrivateService() {
  548. $service = $this->container->get('service_using_private');
  549. $private_service = $service->getSomeOtherService();
  550. $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.');
  551. // Test that sharing the same private services works.
  552. $service = $this->container->get('another_service_using_private');
  553. $another_private_service = $service->getSomeOtherService();
  554. $this->assertNotSame($private_service, $another_private_service, 'Private service is not shared.');
  555. $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.');
  556. }
  557. /**
  558. * Tests that private service sharing works correctly.
  559. *
  560. * @covers ::get
  561. * @covers ::createService
  562. * @covers ::resolveServicesAndParameters
  563. */
  564. public function testResolveServicesAndParametersForSharedPrivateService() {
  565. $service = $this->container->get('service_using_shared_private');
  566. $private_service = $service->getSomeOtherService();
  567. $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.');
  568. // Test that sharing the same private services works.
  569. $service = $this->container->get('another_service_using_shared_private');
  570. $same_private_service = $service->getSomeOtherService();
  571. $this->assertSame($private_service, $same_private_service, 'Private service is shared.');
  572. $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.');
  573. }
  574. /**
  575. * Tests that services with an array of arguments work correctly.
  576. *
  577. * @covers ::get
  578. * @covers ::createService
  579. * @covers ::resolveServicesAndParameters
  580. */
  581. public function testResolveServicesAndParametersForArgumentsUsingDeepArray() {
  582. $service = $this->container->get('service_using_array');
  583. $other_service = $this->container->get('other.service');
  584. $this->assertEquals($other_service, $service->getSomeOtherService(), '@other.service was injected via constructor.');
  585. }
  586. /**
  587. * Tests that services that are optional work correctly.
  588. *
  589. * @covers ::get
  590. * @covers ::createService
  591. * @covers ::resolveServicesAndParameters
  592. */
  593. public function testResolveServicesAndParametersForOptionalServiceDependencies() {
  594. $service = $this->container->get('service_with_optional_dependency');
  595. $this->assertNull($service->getSomeOtherService(), 'other service was NULL was expected.');
  596. }
  597. /**
  598. * Tests that an invalid argument throw an Exception.
  599. *
  600. * @covers ::get
  601. * @covers ::createService
  602. * @covers ::resolveServicesAndParameters
  603. */
  604. public function testResolveServicesAndParametersForInvalidArgument() {
  605. if (method_exists($this, 'expectException')) {
  606. $this->expectException(InvalidArgumentException::class);
  607. }
  608. else {
  609. $this->setExpectedException(InvalidArgumentException::class);
  610. }
  611. $this->container->get('invalid_argument_service');
  612. }
  613. /**
  614. * Tests that invalid arguments throw an Exception.
  615. *
  616. * @covers ::get
  617. * @covers ::createService
  618. * @covers ::resolveServicesAndParameters
  619. */
  620. public function testResolveServicesAndParametersForInvalidArguments() {
  621. // In case the machine-optimized format is not used, we need to simulate the
  622. // test failure.
  623. if (method_exists($this, 'expectException')) {
  624. $this->expectException(InvalidArgumentException::class);
  625. }
  626. else {
  627. $this->setExpectedException(InvalidArgumentException::class);
  628. }
  629. if (!$this->machineFormat) {
  630. throw new InvalidArgumentException('Simulating the test failure.');
  631. }
  632. $this->container->get('invalid_arguments_service');
  633. }
  634. /**
  635. * Tests that a parameter that points to a service works correctly.
  636. *
  637. * @covers ::get
  638. * @covers ::createService
  639. * @covers ::resolveServicesAndParameters
  640. */
  641. public function testResolveServicesAndParametersForServiceInstantiatedFromParameter() {
  642. $service = $this->container->get('service.provider');
  643. $test_service = $this->container->get('service_with_parameter_service');
  644. $this->assertSame($service, $test_service->getSomeOtherService(), 'Service was passed via parameter.');
  645. }
  646. /**
  647. * Tests that Container::initialized works correctly.
  648. *
  649. * @covers ::initialized
  650. */
  651. public function testInitialized() {
  652. $this->assertFalse($this->container->initialized('late.service'), 'Late service is not initialized.');
  653. $this->container->get('late.service');
  654. $this->assertTrue($this->container->initialized('late.service'), 'Late service is initialized after it was retrieved once.');
  655. }
  656. /**
  657. * Tests that Container::initialized works correctly for aliases.
  658. *
  659. * @covers ::initialized
  660. */
  661. public function testInitializedForAliases() {
  662. $this->assertFalse($this->container->initialized('late.service_alias'), 'Late service is not initialized.');
  663. $this->container->get('late.service');
  664. $this->assertTrue($this->container->initialized('late.service_alias'), 'Late service is initialized after it was retrieved once.');
  665. }
  666. /**
  667. * Tests that Container::getServiceIds() works properly.
  668. *
  669. * @covers ::getServiceIds
  670. */
  671. public function testGetServiceIds() {
  672. $service_definition_keys = array_keys($this->containerDefinition['services']);
  673. $this->assertEquals($service_definition_keys, $this->container->getServiceIds(), 'Retrieved service IDs match definition.');
  674. $mock_service = new MockService();
  675. $this->container->set('bar', $mock_service);
  676. $this->container->set('service.provider', $mock_service);
  677. $service_definition_keys[] = 'bar';
  678. $this->assertEquals($service_definition_keys, $this->container->getServiceIds(), 'Retrieved service IDs match definition after setting new services.');
  679. }
  680. /**
  681. * Gets a mock container definition.
  682. *
  683. * @return array
  684. * Associated array with parameters and services.
  685. */
  686. protected function getMockContainerDefinition() {
  687. $fake_service = new \stdClass();
  688. $parameters = [];
  689. $parameters['some_parameter_class'] = get_class($fake_service);
  690. $parameters['some_private_config'] = 'really_private_lama';
  691. $parameters['some_config'] = 'foo';
  692. $parameters['some_other_config'] = 'lama';
  693. $parameters['factory_service_class'] = get_class($fake_service);
  694. // Also test alias resolving.
  695. $parameters['service_from_parameter'] = $this->getServiceCall('service.provider_alias');
  696. $services = [];
  697. $services['service_container'] = [
  698. 'class' => '\Drupal\service_container\DependencyInjection\Container',
  699. ];
  700. $services['other.service'] = [
  701. 'class' => get_class($fake_service),
  702. ];
  703. $services['non_shared_service'] = [
  704. 'class' => get_class($fake_service),
  705. 'shared' => FALSE,
  706. ];
  707. $services['other.service_class_from_parameter'] = [
  708. 'class' => $this->getParameterCall('some_parameter_class'),
  709. ];
  710. $services['late.service'] = [
  711. 'class' => get_class($fake_service),
  712. ];
  713. $services['service.provider'] = [
  714. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  715. 'arguments' => $this->getCollection([
  716. $this->getServiceCall('other.service'),
  717. $this->getParameterCall('some_config'),
  718. ]),
  719. 'properties' => $this->getCollection(['_someProperty' => 'foo']),
  720. 'calls' => [
  721. [
  722. 'setContainer',
  723. $this->getCollection([
  724. $this->getServiceCall('service_container'),
  725. ]),
  726. ],
  727. [
  728. 'setOtherConfigParameter',
  729. $this->getCollection([
  730. $this->getParameterCall('some_other_config'),
  731. ]),
  732. ],
  733. ],
  734. 'priority' => 0,
  735. ];
  736. // Test private services.
  737. $private_service = [
  738. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  739. 'arguments' => $this->getCollection([
  740. $this->getServiceCall('other.service'),
  741. $this->getParameterCall('some_private_config'),
  742. ]),
  743. 'public' => FALSE,
  744. ];
  745. $services['service_using_private'] = [
  746. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  747. 'arguments' => $this->getCollection([
  748. $this->getPrivateServiceCall(NULL, $private_service),
  749. $this->getParameterCall('some_config'),
  750. ]),
  751. ];
  752. $services['another_service_using_private'] = [
  753. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  754. 'arguments' => $this->getCollection([
  755. $this->getPrivateServiceCall(NULL, $private_service),
  756. $this->getParameterCall('some_config'),
  757. ]),
  758. ];
  759. // Test shared private services.
  760. $id = 'private_service_shared_1';
  761. $services['service_using_shared_private'] = [
  762. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  763. 'arguments' => $this->getCollection([
  764. $this->getPrivateServiceCall($id, $private_service, TRUE),
  765. $this->getParameterCall('some_config'),
  766. ]),
  767. ];
  768. $services['another_service_using_shared_private'] = [
  769. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  770. 'arguments' => $this->getCollection([
  771. $this->getPrivateServiceCall($id, $private_service, TRUE),
  772. $this->getParameterCall('some_config'),
  773. ]),
  774. ];
  775. // Tests service with invalid argument.
  776. $services['invalid_argument_service'] = [
  777. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  778. 'arguments' => $this->getCollection([
  779. // Test passing non-strings, too.
  780. 1,
  781. (object) [
  782. 'type' => 'invalid',
  783. ],
  784. ]),
  785. ];
  786. $services['invalid_arguments_service'] = [
  787. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  788. 'arguments' => (object) [
  789. 'type' => 'invalid',
  790. ],
  791. ];
  792. // Test service that needs deep-traversal.
  793. $services['service_using_array'] = [
  794. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  795. 'arguments' => $this->getCollection([
  796. $this->getCollection([
  797. $this->getServiceCall('other.service'),
  798. ]),
  799. $this->getParameterCall('some_private_config'),
  800. ]),
  801. ];
  802. $services['service_with_optional_dependency'] = [
  803. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  804. 'arguments' => $this->getCollection([
  805. $this->getServiceCall('service.does_not_exist', ContainerInterface::NULL_ON_INVALID_REFERENCE),
  806. $this->getParameterCall('some_private_config'),
  807. ]),
  808. ];
  809. $services['factory_service'] = [
  810. 'class' => '\Drupal\service_container\ServiceContainer\ControllerInterface',
  811. 'factory' => [
  812. $this->getServiceCall('service.provider'),
  813. 'getFactoryMethod',
  814. ],
  815. 'arguments' => $this->getCollection([
  816. $this->getParameterCall('factory_service_class'),
  817. ]),
  818. ];
  819. $services['factory_class'] = [
  820. 'class' => '\Drupal\service_container\ServiceContainer\ControllerInterface',
  821. 'factory' => '\Drupal\Tests\Component\DependencyInjection\MockService::getFactoryMethod',
  822. 'arguments' => [
  823. '\Drupal\Tests\Component\DependencyInjection\MockService',
  824. [NULL, 'bar'],
  825. ],
  826. 'calls' => [
  827. [
  828. 'setContainer',
  829. $this->getCollection([
  830. $this->getServiceCall('service_container'),
  831. ]),
  832. ],
  833. ],
  834. ];
  835. $services['wrong_factory'] = [
  836. 'class' => '\Drupal\service_container\ServiceContainer\ControllerInterface',
  837. 'factory' => (object) ['I am not a factory, but I pretend to be.'],
  838. ];
  839. $services['circular_dependency'] = [
  840. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  841. 'arguments' => $this->getCollection([
  842. $this->getServiceCall('circular_dependency'),
  843. ]),
  844. ];
  845. $services['synthetic'] = [
  846. 'synthetic' => TRUE,
  847. ];
  848. // The file could have been named as a .php file. The reason it is a .data
  849. // file is that SimpleTest tries to load it. SimpleTest does not like such
  850. // fixtures and hence we use a neutral name like .data.
  851. $services['container_test_file_service_test'] = [
  852. 'class' => '\stdClass',
  853. 'file' => __DIR__ . '/Fixture/container_test_file_service_test_service_function.data',
  854. ];
  855. // Test multiple arguments.
  856. $args = [];
  857. for ($i = 0; $i < 12; $i++) {
  858. $services['service_test_instantiation_' . $i] = [
  859. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockInstantiationService',
  860. // Also test a collection that does not need resolving.
  861. 'arguments' => $this->getCollection($args, FALSE),
  862. ];
  863. $args[] = 'arg_' . $i;
  864. }
  865. $services['service_parameter_not_exists'] = [
  866. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  867. 'arguments' => $this->getCollection([
  868. $this->getServiceCall('service.provider'),
  869. $this->getParameterCall('not_exists'),
  870. ]),
  871. ];
  872. $services['service_dependency_not_exists'] = [
  873. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  874. 'arguments' => $this->getCollection([
  875. $this->getServiceCall('service_not_exists'),
  876. $this->getParameterCall('some_config'),
  877. ]),
  878. ];
  879. $services['service_with_parameter_service'] = [
  880. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  881. 'arguments' => $this->getCollection([
  882. $this->getParameterCall('service_from_parameter'),
  883. // Also test deep collections that don't need resolving.
  884. $this->getCollection([
  885. 1,
  886. ], FALSE),
  887. ]),
  888. ];
  889. // To ensure getAlternatives() finds something.
  890. $services['service_not_exists_similar'] = [
  891. 'synthetic' => TRUE,
  892. ];
  893. // Test configurator.
  894. $services['configurator'] = [
  895. 'synthetic' => TRUE,
  896. ];
  897. $services['configurable_service'] = [
  898. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  899. 'arguments' => [],
  900. 'configurator' => [
  901. $this->getServiceCall('configurator'),
  902. 'configureService'
  903. ],
  904. ];
  905. $services['configurable_service_exception'] = [
  906. 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
  907. 'arguments' => [],
  908. 'configurator' => 'configurator_service_test_does_not_exist',
  909. ];
  910. $aliases = [];
  911. $aliases['service.provider_alias'] = 'service.provider';
  912. $aliases['late.service_alias'] = 'late.service';
  913. return [
  914. 'aliases' => $aliases,
  915. 'parameters' => $parameters,
  916. 'services' => $services,
  917. 'frozen' => TRUE,
  918. 'machine_format' => $this->machineFormat,
  919. ];
  920. }
  921. /**
  922. * Helper function to return a service definition.
  923. */
  924. protected function getServiceCall($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
  925. return (object) [
  926. 'type' => 'service',
  927. 'id' => $id,
  928. 'invalidBehavior' => $invalid_behavior,
  929. ];
  930. }
  931. /**
  932. * Helper function to return a service definition.
  933. */
  934. protected function getParameterCall($name) {
  935. return (object) [
  936. 'type' => 'parameter',
  937. 'name' => $name,
  938. ];
  939. }
  940. /**
  941. * Helper function to return a private service definition.
  942. */
  943. protected function getPrivateServiceCall($id, $service_definition, $shared = FALSE) {
  944. if (!$id) {
  945. $hash = Crypt::hashBase64(serialize($service_definition));
  946. $id = 'private__' . $hash;
  947. }
  948. return (object) [
  949. 'type' => 'private_service',
  950. 'id' => $id,
  951. 'value' => $service_definition,
  952. 'shared' => $shared,
  953. ];
  954. }
  955. /**
  956. * Helper function to return a machine-optimized collection.
  957. */
  958. protected function getCollection($collection, $resolve = TRUE) {
  959. return (object) [
  960. 'type' => 'collection',
  961. 'value' => $collection,
  962. 'resolve' => $resolve,
  963. ];
  964. }
  965. }
  966. /**
  967. * Helper interface to test Container::get() with configurator.
  968. *
  969. * @group DependencyInjection
  970. */
  971. interface MockConfiguratorInterface {
  972. /**
  973. * Configures a service.
  974. *
  975. * @param object $service
  976. * The service to configure.
  977. */
  978. public function configureService($service);
  979. }
  980. /**
  981. * Helper class to test Container::get() method for varying number of parameters.
  982. *
  983. * @group DependencyInjection
  984. */
  985. class MockInstantiationService {
  986. /**
  987. * @var mixed[]
  988. */
  989. protected $arguments;
  990. /**
  991. * Construct a mock instantiation service.
  992. */
  993. public function __construct() {
  994. $this->arguments = func_get_args();
  995. }
  996. /**
  997. * Return arguments injected into the service.
  998. *
  999. * @return mixed[]
  1000. * Return the passed arguments.
  1001. */
  1002. public function getArguments() {
  1003. return $this->arguments;
  1004. }
  1005. }
  1006. /**
  1007. * Helper class to test Container::get() method.
  1008. *
  1009. * @group DependencyInjection
  1010. */
  1011. class MockService {
  1012. /**
  1013. * @var \Symfony\Component\DependencyInjection\ContainerInterface
  1014. */
  1015. protected $container;
  1016. /**
  1017. * @var object
  1018. */
  1019. protected $someOtherService;
  1020. /**
  1021. * @var string
  1022. */
  1023. protected $someParameter;
  1024. /**
  1025. * @var string
  1026. */
  1027. protected $someOtherParameter;
  1028. /**
  1029. * Constructs a MockService object.
  1030. *
  1031. * @param object $some_other_service
  1032. * (optional) Another injected service.
  1033. * @param string $some_parameter
  1034. * (optional) An injected parameter.
  1035. */
  1036. public function __construct($some_other_service = NULL, $some_parameter = NULL) {
  1037. if (is_array($some_other_service)) {
  1038. $some_other_service = $some_other_service[0];
  1039. }
  1040. $this->someOtherService = $some_other_service;
  1041. $this->someParameter = $some_parameter;
  1042. }
  1043. /**
  1044. * Sets the container object.
  1045. *
  1046. * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
  1047. * The container to inject via setter injection.
  1048. */
  1049. public function setContainer(ContainerInterface $container) {
  1050. $this->container = $container;
  1051. }
  1052. /**
  1053. * Gets the container object.
  1054. *
  1055. * @return \Symfony\Component\DependencyInjection\ContainerInterface
  1056. * The internally set container.
  1057. */
  1058. public function getContainer() {
  1059. return $this->container;
  1060. }
  1061. /**
  1062. * Gets the someOtherService object.
  1063. *
  1064. * @return object
  1065. * The injected service.
  1066. */
  1067. public function getSomeOtherService() {
  1068. return $this->someOtherService;
  1069. }
  1070. /**
  1071. * Gets the someParameter property.
  1072. *
  1073. * @return string
  1074. * The injected parameter.
  1075. */
  1076. public function getSomeParameter() {
  1077. return $this->someParameter;
  1078. }
  1079. /**
  1080. * Sets the someOtherParameter property.
  1081. *
  1082. * @param string $some_other_parameter
  1083. * The setter injected parameter.
  1084. */
  1085. public function setOtherConfigParameter($some_other_parameter) {
  1086. $this->someOtherParameter = $some_other_parameter;
  1087. }
  1088. /**
  1089. * Gets the someOtherParameter property.
  1090. *
  1091. * @return string
  1092. * The injected parameter.
  1093. */
  1094. public function getSomeOtherParameter() {
  1095. return $this->someOtherParameter;
  1096. }
  1097. /**
  1098. * Provides a factory method to get a service.
  1099. *
  1100. * @param string $class
  1101. * The class name of the class to instantiate
  1102. * @param array $arguments
  1103. * (optional) Arguments to pass to the new class.
  1104. *
  1105. * @return object
  1106. * The instantiated service object.
  1107. */
  1108. public static function getFactoryMethod($class, $arguments = []) {
  1109. $r = new \ReflectionClass($class);
  1110. $service = ($r->getConstructor() === NULL) ? $r->newInstance() : $r->newInstanceArgs($arguments);
  1111. return $service;
  1112. }
  1113. }