‪TYPO3CMS  11.5
FailsafeContainerTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Prophecy\PhpUnit\ProphecyTrait;
21 use Prophecy\Prophecy\ObjectProphecy;
22 use Psr\Container\ContainerExceptionInterface;
23 use Psr\Container\ContainerInterface;
24 use Psr\Container\NotFoundExceptionInterface;
25 use stdClass as Service;
28 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
29 
33 class ‪FailsafeContainerTest extends UnitTestCase
34 {
35  use ProphecyTrait;
36 
37  protected ?ObjectProphecy ‪$providerProphecy;
38 
39  protected function ‪setUp(): void
40  {
41  parent::setUp();
42 
43  $this->providerProphecy = $this->‪createServiceProviderProphecy();
44  }
45 
46  protected function ‪createServiceProviderProphecy(array $extensions = [], array $factories = []): ObjectProphecy
47  {
48  $prophecy = $this->prophesize();
49  $prophecy->willImplement(ServiceProviderInterface::class);
50  $prophecy->getFactories()->willReturn($extensions);
51  $prophecy->getExtensions()->willReturn($factories);
52  return $prophecy;
53  }
54 
58  public function ‪implementsInterface(): void
59  {
60  self::assertInstanceOf(ContainerInterface::class, new Container());
61  }
62 
66  public function ‪withString(): void
67  {
68  $this->providerProphecy->getFactories()->willReturn([
69  'param' => static function () {
70  return 'value';
71  },
72  ]);
73  $container = new Container([$this->providerProphecy->reveal()]);
74 
75  self::assertTrue($container->has('param'));
76  self::assertEquals('value', $container->get('param'));
77  }
78 
84  public function get($factory): void
85  {
86  $this->providerProphecy->getFactories()->willReturn([
87  'service' => $factory,
88  ]);
89  $container = new Container([$this->providerProphecy->reveal()]);
90 
91  self::assertTrue($container->has('service'));
92  self::assertInstanceOf(Service::class, $container->get('service'));
93  }
94 
100  public function ‪multipleGetServicesShouldBeEqual($factory): void
101  {
102  $this->providerProphecy->getFactories()->willReturn([ 'service' => $factory ]);
103  // A factory can also be used as extension, as it's based on the same signature
104  $this->providerProphecy->getExtensions()->willReturn([ 'extension' => $factory ]);
105 
106  $container = new Container([$this->providerProphecy->reveal()]);
107 
108  $serviceOne = $container->get('service');
109  $serviceTwo = $container->get('service');
110 
111  $extensionOne = $container->get('extension');
112  $extensionTwo = $container->get('extension');
113 
114  self::assertSame($serviceOne, $serviceTwo);
115  self::assertSame($extensionOne, $extensionTwo);
116  }
117 
121  public function ‪passesContainerAsParameter(): void
122  {
123  $this->providerProphecy->getFactories()->willReturn([
124  'service' => static function () {
125  return new Service();
126  },
127  'container' => static function (ContainerInterface $container) {
128  return $container;
129  },
130  ]);
131  $container = new Container([$this->providerProphecy->reveal()]);
132 
133  self::assertNotSame($container, $container->get('service'));
134  self::assertSame($container, $container->get('container'));
135  }
136 
140  public function ‪nullValueEntry(): void
141  {
142  $this->providerProphecy->getFactories()->willReturn([
143  'null' => static function () {
144  return null;
145  },
146  ]);
147  $container = new Container([$this->providerProphecy->reveal()]);
148 
149  self::assertTrue($container->has('null'));
150  self::assertNull($container->get('null'));
151  }
152 
156  public function ‪nullValueEntryCallsFactoryOnlyOnce(): void
157  {
158  $calledCount = 0;
159  $factory = static function () use (&$calledCount) {
160  $calledCount++;
161  return null;
162  };
163  $this->providerProphecy->getFactories()->willReturn([
164  'null' => $factory,
165  ]);
166  $container = new Container([$this->providerProphecy->reveal()]);
167 
168  self::assertTrue($container->has('null'));
169  self::assertNull($container->get('null'));
170  self::assertTrue($container->has('null'));
171  self::assertNull($container->get('null'));
172  self::assertEquals(1, $calledCount);
173  }
174 
178  public function ‪has(): void
179  {
180  $this->providerProphecy->getFactories()->willReturn([
181  'service' => static function () {
182  return new Service();
183  },
184  'param' => static function () {
185  return 'value';
186  },
187  'int' => static function () {
188  return 2;
189  },
190  'bool' => static function () {
191  return false;
192  },
193  'null' => static function () {
194  return null;
195  },
196  '0' => static function () {
197  return 0;
198  },
199  ]);
200  $container = new Container([$this->providerProphecy->reveal()]);
201 
202  self::assertTrue($container->has('param'));
203  self::assertTrue($container->has('service'));
204  self::assertTrue($container->has('int'));
205  self::assertTrue($container->has('bool'));
206  self::assertTrue($container->has('null'));
207  self::assertFalse($container->has('non_existent'));
208  }
209 
213  public function ‪defaultEntry(): void
214  {
215  $default = ['param' => 'value'];
216  $container = new Container([], $default);
217 
218  self::assertSame('value', $container->get('param'));
219  }
220 
224  public function ‪getValidatesKeyIsPresent(): void
225  {
226  $container = new Container();
227 
228  $this->expectException(NotFoundExceptionInterface::class);
229  $this->expectExceptionMessage('Container entry "foo" is not available.');
230  $container->get('foo');
231  }
232 
238  public function ‪extension($factory): void
239  {
240  $providerA = ‪$this->providerProphecy;
241  $providerA->getFactories()->willReturn(['service' => $factory]);
242 
243  $providerB = $this->‪createServiceProviderProphecy();
244  $providerB->getExtensions()->willReturn([
245  'service' => static function (ContainerInterface $c, Service $s) {
246  $s->value = 'value';
247  return $s;
248  },
249  ]);
250  $iterator = (static function () use ($providerA, $providerB): iterable {
251  yield $providerA->reveal();
252  yield $providerB->reveal();
253  })();
254  $container = new Container($iterator);
255 
256  self::assertSame('value', $container->get('service')->value);
257  }
258 
264  public function ‪extendingLaterProvider($factory): void
265  {
266  $providerA = ‪$this->providerProphecy;
267  $providerA->getFactories()->willReturn(['service' => $factory]);
268 
269  $providerB = $this->‪createServiceProviderProphecy();
270  $providerB->getExtensions()->willReturn([
271  'service' => static function (ContainerInterface $c, Service $s) {
272  $s->value = 'value';
273  return $s;
274  },
275  ]);
276  $container = new Container([$providerB->reveal(), $providerA->reveal()]);
277 
278  self::assertSame('value', $container->get('service')->value);
279  }
280 
286  public function ‪extendingOwnFactory($factory): void
287  {
288  $this->providerProphecy->getFactories()->willReturn(['service' => $factory]);
289  $this->providerProphecy->getExtensions()->willReturn(
290  [
291  'service' => static function (ContainerInterface $c, Service $s) {
292  $s->value = 'value';
293  return $s;
294  },
295  ]
296  );
297  $container = new Container([$this->providerProphecy->reveal()]);
298 
299  self::assertSame('value', $container->get('service')->value);
300  }
301 
305  public function ‪extendingNonExistingFactory(): void
306  {
307  $this->providerProphecy->getExtensions()->willReturn([
308  'service' => static function (ContainerInterface $c, Service $s = null) {
309  if ($s === null) {
310  $s = new Service();
311  }
312  $s->value = 'value';
313  return $s;
314  },
315  ]);
316  $container = new Container([$this->providerProphecy->reveal()]);
317 
318  self::assertSame('value', $container->get('service')->value);
319  }
320 
326  public function ‪multipleExtensions($factory): void
327  {
328  $providerA = ‪$this->providerProphecy;
329  $providerA->getFactories()->willReturn(['service' => $factory]);
330 
331  $providerB = $this->‪createServiceProviderProphecy();
332  $providerB->getExtensions()->willReturn([
333  'service' => static function (ContainerInterface $c, Service $s) {
334  $s->value = '1';
335  return $s;
336  },
337  ]);
338 
339  $providerC = $this->‪createServiceProviderProphecy();
340  $providerC->getExtensions()->willReturn([
341  'service' => static function (ContainerInterface $c, Service $s) {
342  $s->value .= '2';
343  return $s;
344  },
345  ]);
346  $container = new Container([$providerA->reveal(), $providerB->reveal(), $providerC->reveal()]);
347 
348  self::assertSame('12', $container->get('service')->value);
349  }
350 
356  public function ‪entryOverriding($factory): void
357  {
358  $providerA = ‪$this->providerProphecy;
359  $providerA->getFactories()->willReturn(['service' => $factory]);
360 
361  $providerB = $this->‪createServiceProviderProphecy();
362  $providerB->getFactories()->willReturn(['service' => static function () {
363  return 'value';
364  }]);
365 
366  $container = new Container([$providerA->reveal(), $providerB->reveal()]);
367 
368  self::assertNotInstanceOf(Service::class, $container->get('service'));
369  self::assertEquals('value', $container->get('service'));
370  }
371 
375  public function ‪cyclicDependency(): void
376  {
377  $this->providerProphecy->getFactories()->willReturn([
378  'A' => static function (ContainerInterface $container) {
379  return $container->get('B');
380  },
381  'B' => static function (ContainerInterface $container) {
382  return $container->get('A');
383  },
384  ]);
385 
386  $container = new Container([$this->providerProphecy->reveal()]);
387 
388  $this->expectException(ContainerExceptionInterface::class);
389  $this->expectExceptionMessage('Container entry "A" is part of a cyclic dependency chain.');
390  $container->get('A');
391  }
392 
396  public function ‪cyclicDependencyRetrievedTwice(): void
397  {
398  $this->providerProphecy->getFactories()->willReturn([
399  'A' => static function (ContainerInterface $container) {
400  return $container->get('B');
401  },
402  'B' => static function (ContainerInterface $container) {
403  return $container->get('A');
404  },
405  ]);
406 
407  $container = new Container([$this->providerProphecy->reveal()]);
408 
409  $this->expectException(ContainerExceptionInterface::class);
410  $this->expectExceptionMessage('Container entry "A" is part of a cyclic dependency chain.');
411  try {
412  $container->get('A');
413  } catch (ContainerExceptionInterface $e) {
414  }
415  self::assertTrue($container->has('A'));
416  $container->get('A');
417  }
418 
422  public function ‪nullContainer(): void
423  {
424  $container = new Container();
425  self::assertFalse($container->has('foo'));
426  }
427 public function ‪nullContainerWithDefaultEntries(): void
431  {
432  $container = new Container([], ['foo' => 'bar']);
433  self::assertTrue($container->has('foo'));
434  }
435 
436  public static function ‪factory(): Service
437  {
438  return new Service();
439  }
440 
445  public function ‪objectFactories(): array
446  {
447  return [
448  [
449  // Static callback
450  [ self::class, 'factory'],
451  ],
452  [
453  // Closure
454  static function () {
455  return new Service();
456  },
457  ],
458  [
459  // Invokable
460  new class () {
461  public function __invoke(): Service
462  {
463  return new Service();
464  }
465  },
466  ],
467  [
468  // Non static factory
469  [
470  new class () {
471  public function ‪factory(): Service
472  {
473  return new Service();
474  }
475  },
476  'factory',
477  ],
478  ],
479  ];
480  }
481 }
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\nullContainer
‪nullContainer()
Definition: FailsafeContainerTest.php:421
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\passesContainerAsParameter
‪passesContainerAsParameter()
Definition: FailsafeContainerTest.php:120
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\nullValueEntry
‪nullValueEntry()
Definition: FailsafeContainerTest.php:139
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\objectFactories
‪objectFactories()
Definition: FailsafeContainerTest.php:444
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\extendingNonExistingFactory
‪extendingNonExistingFactory()
Definition: FailsafeContainerTest.php:304
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\cyclicDependency
‪cyclicDependency()
Definition: FailsafeContainerTest.php:374
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\has
‪has()
Definition: FailsafeContainerTest.php:177
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\entryOverriding
‪entryOverriding($factory)
Definition: FailsafeContainerTest.php:355
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\extension
‪extension($factory)
Definition: FailsafeContainerTest.php:237
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\extendingOwnFactory
‪extendingOwnFactory($factory)
Definition: FailsafeContainerTest.php:285
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\cyclicDependencyRetrievedTwice
‪cyclicDependencyRetrievedTwice()
Definition: FailsafeContainerTest.php:395
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\extendingLaterProvider
‪extendingLaterProvider($factory)
Definition: FailsafeContainerTest.php:263
‪TYPO3\CMS\Core\DependencyInjection\FailsafeContainer
Definition: FailsafeContainer.php:26
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\createServiceProviderProphecy
‪createServiceProviderProphecy(array $extensions=[], array $factories=[])
Definition: FailsafeContainerTest.php:45
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\setUp
‪setUp()
Definition: FailsafeContainerTest.php:38
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\multipleGetServicesShouldBeEqual
‪multipleGetServicesShouldBeEqual($factory)
Definition: FailsafeContainerTest.php:99
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection
Definition: ConsoleCommandPassTest.php:18
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\nullContainerWithDefaultEntries
‪nullContainerWithDefaultEntries()
Definition: FailsafeContainerTest.php:429
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\defaultEntry
‪defaultEntry()
Definition: FailsafeContainerTest.php:212
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\getValidatesKeyIsPresent
‪getValidatesKeyIsPresent()
Definition: FailsafeContainerTest.php:223
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest
Definition: FailsafeContainerTest.php:34
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\nullValueEntryCallsFactoryOnlyOnce
‪nullValueEntryCallsFactoryOnlyOnce()
Definition: FailsafeContainerTest.php:155
‪TYPO3\CMS\Core\DependencyInjection\ServiceProviderInterface
Definition: ServiceProviderInterface.php:24
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\$providerProphecy
‪ObjectProphecy $providerProphecy
Definition: FailsafeContainerTest.php:36
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\implementsInterface
‪implementsInterface()
Definition: FailsafeContainerTest.php:57
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\multipleExtensions
‪multipleExtensions($factory)
Definition: FailsafeContainerTest.php:325
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\withString
‪withString()
Definition: FailsafeContainerTest.php:65
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\factory
‪static factory()
Definition: FailsafeContainerTest.php:435