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