‪TYPO3CMS  10.4
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\Prophecy\ObjectProphecy;
21 use Psr\Container\ContainerExceptionInterface;
22 use Psr\Container\ContainerInterface;
23 use Psr\Container\NotFoundExceptionInterface;
24 use stdClass as Service;
27 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
28 
32 class ‪FailsafeContainerTest extends UnitTestCase
33 {
37  protected ‪$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 
55  public function ‪testImplementsInterface(): void
56  {
57  self::assertInstanceOf(ContainerInterface::class, new Container());
58  }
59 
60  public function ‪testWithString(): void
61  {
62  $this->providerProphecy->getFactories()->willReturn([
63  'param' => function () {
64  return 'value';
65  }
66  ]);
67  $container = new Container([$this->providerProphecy->reveal()]);
68 
69  self::assertTrue($container->has('param'));
70  self::assertEquals('value', $container->get('param'));
71  }
72 
77  public function ‪testGet($factory): void
78  {
79  $this->providerProphecy->getFactories()->willReturn([
80  'service' => $factory,
81  ]);
82  $container = new Container([$this->providerProphecy->reveal()]);
83 
84  self::assertTrue($container->has('service'));
85  self::assertInstanceOf(Service::class, $container->get('service'));
86  }
87 
92  public function ‪testMultipleGetServicesShouldBeEqual($factory): void
93  {
94  $this->providerProphecy->getFactories()->willReturn([ 'service' => $factory ]);
95  // A factory can also be used as extension, as it's based on the same signature
96  $this->providerProphecy->getExtensions()->willReturn([ 'extension' => $factory ]);
97 
98  $container = new Container([$this->providerProphecy->reveal()]);
99 
100  $serviceOne = $container->get('service');
101  $serviceTwo = $container->get('service');
102 
103  $extensionOne = $container->get('extension');
104  $extensionTwo = $container->get('extension');
105 
106  self::assertSame($serviceOne, $serviceTwo);
107  self::assertSame($extensionOne, $extensionTwo);
108  }
109 
110  public function ‪testPassesContainerAsParameter(): void
111  {
112  $this->providerProphecy->getFactories()->willReturn([
113  'service' => function () {
114  return new Service();
115  },
116  'container' => function (ContainerInterface $container) {
117  return $container;
118  }
119  ]);
120  $container = new Container([$this->providerProphecy->reveal()]);
121 
122  self::assertNotSame($container, $container->get('service'));
123  self::assertSame($container, $container->get('container'));
124  }
125 
126  public function ‪testNullValueEntry(): void
127  {
128  $this->providerProphecy->getFactories()->willReturn([
129  'null' => function () {
130  return null;
131  }
132  ]);
133  $container = new Container([$this->providerProphecy->reveal()]);
134 
135  self::assertTrue($container->has('null'));
136  self::assertNull($container->get('null'));
137  }
138 
139  public function ‪testNullValueEntryCallsFactoryOnlyOnce(): void
140  {
141  $calledCount = 0;
142  $factory = function () use (&$calledCount) {
143  $calledCount++;
144  return null;
145  };
146  $this->providerProphecy->getFactories()->willReturn([
147  'null' => $factory,
148  ]);
149  $container = new Container([$this->providerProphecy->reveal()]);
150 
151  self::assertTrue($container->has('null'));
152  self::assertNull($container->get('null'));
153  self::assertTrue($container->has('null'));
154  self::assertNull($container->get('null'));
155  self::assertEquals($calledCount, 1);
156  }
157 
158  public function ‪testHas(): void
159  {
160  $this->providerProphecy->getFactories()->willReturn([
161  'service' => function () {
162  return new Service();
163  },
164  'param' => function () {
165  return 'value';
166  },
167  'int' => function () {
168  return 2;
169  },
170  'bool' => function () {
171  return false;
172  },
173  'null' => function () {
174  return null;
175  },
176  '0' => function () {
177  return 0;
178  }
179  ]);
180  $container = new Container([$this->providerProphecy->reveal()]);
181 
182  self::assertTrue($container->has('param'));
183  self::assertTrue($container->has('service'));
184  self::assertTrue($container->has('int'));
185  self::assertTrue($container->has('bool'));
186  self::assertTrue($container->has('null'));
187  self::assertFalse($container->has('non_existent'));
188  }
189 
190  public function ‪testDefaultEntry(): void
191  {
192  $default = ['param' => 'value'];
193  $container = new Container([], $default);
194 
195  self::assertSame('value', $container->get('param'));
196  }
197 
198  public function ‪testGetValidatesKeyIsPresent(): void
199  {
200  $container = new Container();
201 
202  $this->expectException(NotFoundExceptionInterface::class);
203  $this->expectExceptionMessage('Container entry "foo" is not available.');
204  $container->get('foo');
205  }
206 
211  public function ‪testExtension($factory): void
212  {
213  $providerA = ‪$this->providerProphecy;
214  $providerA->getFactories()->willReturn(['service' => $factory]);
215 
216  $providerB = $this->‪createServiceProviderProphecy();
217  $providerB->getExtensions()->willReturn([
218  'service' => function (ContainerInterface $c, Service $s) {
219  $s->value = 'value';
220  return $s;
221  },
222  ]);
223  $iterator = (function () use ($providerA, $providerB): iterable {
224  yield $providerA->reveal();
225  yield $providerB->reveal();
226  })();
227  $container = new Container($iterator);
228 
229  self::assertSame('value', $container->get('service')->value);
230  }
231 
236  public function ‪testExtendingLaterProvider($factory): void
237  {
238  $providerA = ‪$this->providerProphecy;
239  $providerA->getFactories()->willReturn(['service' => $factory]);
240 
241  $providerB = $this->‪createServiceProviderProphecy();
242  $providerB->getExtensions()->willReturn([
243  'service' => function (ContainerInterface $c, Service $s) {
244  $s->value = 'value';
245  return $s;
246  },
247  ]);
248  $container = new Container([$providerB->reveal(), $providerA->reveal()]);
249 
250  self::assertSame('value', $container->get('service')->value);
251  }
252 
257  public function ‪testExtendingOwnFactory($factory): void
258  {
259  $this->providerProphecy->getFactories()->willReturn(['service' => $factory]);
260  $this->providerProphecy->getExtensions()->willReturn(
261  [
262  'service' => function (ContainerInterface $c, Service $s) {
263  $s->value = 'value';
264  return $s;
265  },
266  ]
267  );
268  $container = new Container([$this->providerProphecy->reveal()]);
269 
270  self::assertSame('value', $container->get('service')->value);
271  }
272 
273  public function ‪testExtendingNonExistingFactory(): void
274  {
275  $this->providerProphecy->getExtensions()->willReturn([
276  'service' => function (ContainerInterface $c, Service $s = null) {
277  if ($s === null) {
278  $s = new Service();
279  }
280  $s->value = 'value';
281  return $s;
282  },
283  ]);
284  $container = new Container([$this->providerProphecy->reveal()]);
285 
286  self::assertSame('value', $container->get('service')->value);
287  }
288 
293  public function ‪testMultipleExtensions($factory): void
294  {
295  $providerA = ‪$this->providerProphecy;
296  $providerA->getFactories()->willReturn(['service' => $factory]);
297 
298  $providerB = $this->‪createServiceProviderProphecy();
299  $providerB->getExtensions()->willReturn([
300  'service' => function (ContainerInterface $c, Service $s) {
301  $s->value = '1';
302  return $s;
303  },
304  ]);
305 
306  $providerC = $this->‪createServiceProviderProphecy();
307  $providerC->getExtensions()->willReturn([
308  'service' => function (ContainerInterface $c, Service $s) {
309  $s->value .= '2';
310  return $s;
311  },
312  ]);
313  $container = new Container([$providerA->reveal(), $providerB->reveal(), $providerC->reveal()]);
314 
315  self::assertSame('12', $container->get('service')->value);
316  }
317 
322  public function ‪testEntryOverriding($factory): void
323  {
324  $providerA = ‪$this->providerProphecy;
325  $providerA->getFactories()->willReturn(['service' => $factory]);
326 
327  $providerB = $this->‪createServiceProviderProphecy();
328  $providerB->getFactories()->willReturn(['service' => function () {
329  return 'value';
330  }]);
331 
332  $container = new Container([$providerA->reveal(), $providerB->reveal()]);
333 
334  self::assertNotInstanceOf(Service::class, $container->get('service'));
335  self::assertEquals('value', $container->get('service'));
336  }
337 
338  public function ‪testCyclicDependency(): void
339  {
340  $this->providerProphecy->getFactories()->willReturn([
341  'A' => function (ContainerInterface $container) {
342  return $container->get('B');
343  },
344  'B' => function (ContainerInterface $container) {
345  return $container->get('A');
346  },
347  ]);
348 
349  $container = new Container([$this->providerProphecy->reveal()]);
350 
351  $this->expectException(ContainerExceptionInterface::class);
352  $this->expectExceptionMessage('Container entry "A" is part of a cyclic dependency chain.');
353  $container->get('A');
354  }
355 
356  public function ‪testCyclicDependencyRetrievedTwice(): void
357  {
358  $this->providerProphecy->getFactories()->willReturn([
359  'A' => function (ContainerInterface $container) {
360  return $container->get('B');
361  },
362  'B' => function (ContainerInterface $container) {
363  return $container->get('A');
364  },
365  ]);
366 
367  $container = new Container([$this->providerProphecy->reveal()]);
368 
369  $this->expectException(ContainerExceptionInterface::class);
370  $this->expectExceptionMessage('Container entry "A" is part of a cyclic dependency chain.');
371  try {
372  $container->get('A');
373  } catch (ContainerExceptionInterface $e) {
374  }
375  self::assertTrue($container->has('A'));
376  $container->get('A');
377  }
378 
379  public function ‪testNullContainer(): void
380  {
381  $container = new Container();
382  self::assertFalse($container->has('foo'));
383  }
384 
385  public function ‪testNullContainerWithDefaultEntries(): void
386  {
387  $container = new Container([], ['foo' => 'bar']);
388  self::assertTrue($container->has('foo'));
389  }
390 
391  public static function ‪factory(): Service
392  {
393  return new Service();
394  }
395 
400  public function ‪objectFactories(): array
401  {
402  return [
403  [
404  // Static callback
405  [ self::class, 'factory']
406  ],
407  [
408  // Closure
409  function () {
410  return new Service();
411  }
412  ],
413  [
414  // Invokable
415  new class() {
416  public function __invoke(): Service
417  {
418  return new Service();
419  }
420  }
421  ],
422  [
423  // Non static factory
424  [
425  new class() {
426  public function ‪factory(): Service
427  {
428  return new Service();
429  }
430  },
431  'factory'
432  ]
433  ],
434  ];
435  }
436 }
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testWithString
‪testWithString()
Definition: FailsafeContainerTest.php:59
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testExtension
‪testExtension($factory)
Definition: FailsafeContainerTest.php:210
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\objectFactories
‪objectFactories()
Definition: FailsafeContainerTest.php:399
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testExtendingOwnFactory
‪testExtendingOwnFactory($factory)
Definition: FailsafeContainerTest.php:256
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testDefaultEntry
‪testDefaultEntry()
Definition: FailsafeContainerTest.php:189
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testPassesContainerAsParameter
‪testPassesContainerAsParameter()
Definition: FailsafeContainerTest.php:109
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testEntryOverriding
‪testEntryOverriding($factory)
Definition: FailsafeContainerTest.php:321
‪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\testNullValueEntryCallsFactoryOnlyOnce
‪testNullValueEntryCallsFactoryOnlyOnce()
Definition: FailsafeContainerTest.php:138
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection
Definition: FailsafeContainerTest.php:18
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testImplementsInterface
‪testImplementsInterface()
Definition: FailsafeContainerTest.php:54
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testCyclicDependencyRetrievedTwice
‪testCyclicDependencyRetrievedTwice()
Definition: FailsafeContainerTest.php:355
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testNullContainerWithDefaultEntries
‪testNullContainerWithDefaultEntries()
Definition: FailsafeContainerTest.php:384
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testNullContainer
‪testNullContainer()
Definition: FailsafeContainerTest.php:378
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testMultipleGetServicesShouldBeEqual
‪testMultipleGetServicesShouldBeEqual($factory)
Definition: FailsafeContainerTest.php:91
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest
Definition: FailsafeContainerTest.php:33
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testGetValidatesKeyIsPresent
‪testGetValidatesKeyIsPresent()
Definition: FailsafeContainerTest.php:197
‪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\testGet
‪testGet($factory)
Definition: FailsafeContainerTest.php:76
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\factory
‪static factory()
Definition: FailsafeContainerTest.php:390
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testExtendingLaterProvider
‪testExtendingLaterProvider($factory)
Definition: FailsafeContainerTest.php:235
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testNullValueEntry
‪testNullValueEntry()
Definition: FailsafeContainerTest.php:125
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testCyclicDependency
‪testCyclicDependency()
Definition: FailsafeContainerTest.php:337
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testExtendingNonExistingFactory
‪testExtendingNonExistingFactory()
Definition: FailsafeContainerTest.php:272
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testHas
‪testHas()
Definition: FailsafeContainerTest.php:157
‪TYPO3\CMS\Core\Tests\Unit\DependencyInjection\FailsafeContainerTest\testMultipleExtensions
‪testMultipleExtensions($factory)
Definition: FailsafeContainerTest.php:292