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