Passed
Push — master ( be38f3...113e43 )
by Alexander
01:37
created

InjectorTest::testCustomDependency()

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
eloc 7
nc 1
nop 0
dl 0
loc 13
c 3
b 0
f 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Injector\Tests;
6
7
use DateTime;
8
use DateTimeImmutable;
9
use DateTimeInterface;
10
use PHPUnit\Framework\TestCase;
11
use Psr\Container\ContainerInterface;
12
use Psr\Container\NotFoundExceptionInterface;
13
use stdClass;
14
use Yiisoft\Injector\Injector;
15
use Yiisoft\Injector\InvalidArgumentException;
16
use Yiisoft\Injector\MissingRequiredArgumentException;
17
use Yiisoft\Injector\Tests\Support\ColorInterface;
18
use Yiisoft\Injector\Tests\Support\EngineInterface;
19
use Yiisoft\Injector\Tests\Support\EngineMarkTwo;
20
use Yiisoft\Injector\Tests\Support\EngineZIL130;
21
use Yiisoft\Injector\Tests\Support\EngineVAZ2101;
22
use Yiisoft\Injector\Tests\Support\Invokeable;
23
use Yiisoft\Injector\Tests\Support\LightEngine;
24
use Yiisoft\Injector\Tests\Support\MakeEmptyConstructor;
25
use Yiisoft\Injector\Tests\Support\MakeEngineCollector;
26
use Yiisoft\Injector\Tests\Support\MakeEngineMatherWithParam;
27
use Yiisoft\Injector\Tests\Support\MakeNoConstructor;
28
use Yiisoft\Injector\Tests\Support\MakePrivateConstructor;
29
30
class InjectorTest extends TestCase
31
{
32
    /**
33
     * Injector should be able to invoke closure.
34
     */
35
    public function testInvokeClosure(): void
36
    {
37
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
38
39
        $getEngineName = fn (EngineInterface $engine) => $engine->getName();
40
41
        $engineName = (new Injector($container))->invoke($getEngineName);
42
43
        $this->assertSame('Mark Two', $engineName);
44
    }
45
46
    /**
47
     * Injector should be able to invoke array callable.
48
     */
49
    public function testInvokeCallableArray(): void
50
    {
51
        $container = $this->getContainer();
52
53
        $object = new EngineVAZ2101();
54
55
        $engine = (new Injector($container))->invoke([$object, 'rust'], ['index' => 5.0]);
56
57
        $this->assertInstanceOf(EngineVAZ2101::class, $engine);
58
    }
59
60
    /**
61
     * Injector should be able to invoke static method.
62
     */
63
    public function testInvokeStatic(): void
64
    {
65
        $container = $this->getContainer();
66
67
        $result = (new Injector($container))->invoke([EngineVAZ2101::class, 'isWroomWroom']);
68
69
        $this->assertIsBool($result);
70
    }
71
72
    /**
73
     * Injector should be able to invoke static method.
74
     */
75
    public function testInvokeAnonymousClass(): void
76
    {
77
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
78
        $class = new class() {
79
            public EngineInterface $engine;
80
            public function setEngine(EngineInterface $engine)
81
            {
82
                $this->engine = $engine;
83
            }
84
        };
85
86
        (new Injector($container))->invoke([$class, 'setEngine']);
87
88
        $this->assertInstanceOf(EngineInterface::class, $class->engine);
89
    }
90
91
    /**
92
     * Injector should be able to invoke method without arguments.
93
     */
94
    public function testInvokeWithoutArguments(): void
95
    {
96
        $container = $this->getContainer();
97
98
        $true = fn () => true;
99
100
        $result = (new Injector($container))->invoke($true);
101
102
        $this->assertTrue($result);
103
    }
104
105
    /**
106
     * Nullable arguments should be searched in container.
107
     */
108
    public function testWithNullableArgument(): void
109
    {
110
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
111
112
        $nullable = fn (?EngineInterface $engine) => $engine;
113
114
        $result = (new Injector($container))->invoke($nullable);
115
116
        $this->assertNotNull($result);
117
    }
118
119
    /**
120
     * Nullable arguments not found in container should be passed as `null`.
121
     */
122
    public function testWithNullableArgumentAndEmptyContainer(): void
123
    {
124
        $container = $this->getContainer();
125
126
        $nullable = fn (?EngineInterface $engine) => $engine;
127
128
        $result = (new Injector($container))->invoke($nullable);
129
130
        $this->assertNull($result);
131
    }
132
133
    /**
134
     * Nullable scalars should be set with `null` if not specified by name explicitly.
135
     */
136
    public function testWithNullableScalarArgument(): void
137
    {
138
        $container = $this->getContainer();
139
140
        $nullableInt = fn (?int $number) => $number;
141
142
        $result = (new Injector($container))->invoke($nullableInt);
143
144
        $this->assertNull($result);
145
    }
146
147
    /**
148
     * Optional scalar arguments should be set with default value if not specified by name explicitly.
149
     */
150
    public function testWithNullableOptionalArgument(): void
151
    {
152
        $container = $this->getContainer();
153
154
        $nullableInt = fn (?int $number = 6) => $number;
155
156
        $result = (new Injector($container))->invoke($nullableInt);
157
158
        $this->assertSame(6, $result);
159
    }
160
161
    /**
162
     * Optional arguments with `null` by default should be set with `null` if other value not specified in parameters
163
     * or container.
164
     */
165
    public function testWithNullableOptionalArgumentThatNull(): void
166
    {
167
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
168
169
        $callable = fn (EngineInterface $engine = null) => $engine;
170
171
        $result = (new Injector($container))->invoke($callable);
172
173
        $this->assertNotNull($result);
174
    }
175
176
    /**
177
     * An object for a typed argument can be specified in parameters without named key and without following the order.
178
     */
179
    public function testCustomDependency(): void
180
    {
181
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
182
        $needleEngine = new EngineZIL130();
183
184
        $getEngineName = fn (EngineInterface $engine) => $engine->getName();
185
186
        $engineName = (new Injector($container))->invoke(
187
            $getEngineName,
188
            [new stdClass(), $needleEngine, new DateTimeImmutable()]
189
        );
190
191
        $this->assertSame(EngineZIL130::NAME, $engineName);
192
    }
193
194
    /**
195
     * In this case, first argument will be set from parameters, and second argument from container.
196
     */
197
    public function testTwoEqualCustomArgumentsWithOneCustom(): void
198
    {
199
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
200
201
        $compareEngines = static function (EngineInterface $engine1, EngineInterface $engine2) {
202
            return $engine1->getPower() <=> $engine2->getPower();
203
        };
204
        $zilEngine = new EngineZIL130();
205
206
        $result = (new Injector($container))->invoke($compareEngines, [$zilEngine]);
207
208
        $this->assertSame(-1, $result);
209
    }
210
211
    /**
212
     * In this case, second argument will be set from parameters by name, and first argument from container.
213
     */
214
    public function testTwoEqualCustomArgumentsWithOneCustomNamedParameter(): void
215
    {
216
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
217
218
        $compareEngines = static function (EngineInterface $engine1, EngineInterface $engine2) {
219
            return $engine1->getPower() <=> $engine2->getPower();
220
        };
221
        $zilEngine = new EngineZIL130();
222
223
        $result = (new Injector($container))->invoke($compareEngines, ['engine2' => $zilEngine]);
224
225
        $this->assertSame(1, $result);
226
    }
227
228
    /**
229
     * Values for arguments are not matched by the greater similarity of parameter types and arguments, but simply pass
230
     * in order as is.
231
     */
232
    public function testExtendedArgumentsWithOneCustomNamedParameter2(): void
233
    {
234
        $container = $this->getContainer([LightEngine::class => new EngineVAZ2101()]);
235
236
        $concatEngineNames = static function (EngineInterface $engine1, LightEngine $engine2) {
237
            return $engine1->getName() . $engine2->getName();
238
        };
239
240
        $result = (new Injector($container))->invoke($concatEngineNames, [
241
            new EngineMarkTwo(), // LightEngine, EngineInterface
242
            new EngineZIL130(), // EngineInterface
243
        ]);
244
245
        $this->assertSame(EngineMarkTwo::NAME . EngineVAZ2101::NAME, $result);
246
    }
247
248
    public function testMissingRequiredTypedParameter(): void
249
    {
250
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
251
252
        $getEngineName = static function (EngineInterface $engine, string $two) {
0 ignored issues
show
Unused Code introduced by
The parameter $two is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

252
        $getEngineName = static function (EngineInterface $engine, /** @scrutinizer ignore-unused */ string $two) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
253
            return $engine->getName();
254
        };
255
256
        $injector = new Injector($container);
257
258
        $this->expectException(MissingRequiredArgumentException::class);
259
        $injector->invoke($getEngineName);
260
    }
261
262
    public function testMissingRequiredNotTypedParameter(): void
263
    {
264
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
265
266
        $getEngineName = static function (EngineInterface $engine, $two) {
0 ignored issues
show
Unused Code introduced by
The parameter $two is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

266
        $getEngineName = static function (EngineInterface $engine, /** @scrutinizer ignore-unused */ $two) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
267
            return $engine->getName();
268
        };
269
        $injector = new Injector($container);
270
271
        $this->expectException(MissingRequiredArgumentException::class);
272
273
        $injector->invoke($getEngineName);
274
    }
275
276
    public function testNotFoundException(): void
277
    {
278
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
279
280
        $getEngineName = static function (EngineInterface $engine, ColorInterface $color) {
0 ignored issues
show
Unused Code introduced by
The parameter $color is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

280
        $getEngineName = static function (EngineInterface $engine, /** @scrutinizer ignore-unused */ ColorInterface $color) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
281
            return $engine->getName();
282
        };
283
284
        $injector = new Injector($container);
285
286
        $this->expectException(NotFoundExceptionInterface::class);
287
        $injector->invoke($getEngineName);
288
    }
289
290
    /**
291
     * A values collection for a variadic argument can be passed as an array in a named parameter.
292
     */
293
    public function testAloneScalarVariadicArgumentAnsNamedParam(): void
294
    {
295
        $container = $this->getContainer();
296
297
        $callable = fn (...$var) => array_sum($var);
298
299
        $result = (new Injector($container))->invoke($callable, ['var' => [1, 2, 3]]);
300
301
        $this->assertSame(6, $result);
302
    }
303
304
    /**
305
     * If type of a variadic argument is a class and named parameter with values collection is not set then injector
306
     * will search for objects by class name among all unnamed parameters.
307
     */
308
    public function testVariadicArgumentUnnamedParams(): void
309
    {
310
        $container = $this->getContainer([DateTimeInterface::class => new DateTimeImmutable()]);
311
312
        $callable = fn (DateTimeInterface $dateTime, EngineInterface ...$engines) => count($engines);
313
314
        $result = (new Injector($container))->invoke(
315
            $callable,
316
            [new EngineZIL130(), new EngineVAZ2101(), new stdClass(), new EngineMarkTwo(), new stdClass()]
317
        );
318
319
        $this->assertSame(3, $result);
320
    }
321
322
    /**
323
     * If calling method have an untyped variadic argument then all remaining unnamed parameters will be passed.
324
     */
325
    public function testVariadicMixedArgumentWithMixedParams(): void
326
    {
327
        $container = $this->getContainer([DateTimeInterface::class => new DateTimeImmutable()]);
328
329
        $callable = fn (...$engines) => $engines;
330
331
        $result = (new Injector($container))->invoke(
332
            $callable,
333
            [new EngineZIL130(), new EngineVAZ2101(), new EngineMarkTwo(), new stdClass()]
334
        );
335
336
        $this->assertCount(4, $result);
337
    }
338
339
    /**
340
     * Any unnamed parameter can only be an object. Scalar, array, null and other values can only be named parameters.
341
     */
342
    public function testVariadicStringArgumentWithUnnamedStringsParams(): void
343
    {
344
        $container = $this->getContainer([DateTimeInterface::class => new DateTimeImmutable()]);
345
346
        $callable = fn (string ...$engines) => $engines;
347
348
        $this->expectException(\Exception::class);
349
350
        (new Injector($container))->invoke($callable, ['str 1', 'str 2', 'str 3']);
351
    }
352
353
    /**
354
     * In the absence of other values to a nullable variadic argument `null` is not passed by default.
355
     */
356
    public function testNullableVariadicArgument(): void
357
    {
358
        $container = $this->getContainer();
359
360
        $callable = fn (?EngineInterface ...$engines) => $engines;
361
362
        $result = (new Injector($container))->invoke($callable, []);
363
364
        $this->assertSame([], $result);
365
    }
366
367
    /**
368
     * Parameters that were passed but were not used are appended to the call so they could be obtained
369
     * with func_get_args().
370
     */
371
    public function testAppendingUnusedParams(): void
372
    {
373
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
374
375
        $callable = fn (?EngineInterface $engine, $id = 'test') => func_num_args();
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

375
        $callable = fn (?EngineInterface $engine, /** @scrutinizer ignore-unused */ $id = 'test') => func_num_args();

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $engine is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

375
        $callable = fn (/** @scrutinizer ignore-unused */ ?EngineInterface $engine, $id = 'test') => func_num_args();

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
376
377
        $result = (new Injector($container))->invoke($callable, [new DateTimeImmutable(), new DateTimeImmutable()]);
378
379
        $this->assertSame(4, $result);
380
    }
381
382
    /**
383
     * Object type may be passed as unnamed parameter
384
     */
385
    public function testInvokeWithObjectType(): void
386
    {
387
        $container = $this->getContainer();
388
        $callable = fn (object $object) => get_class($object);
389
390
        $result = (new Injector($container))->invoke($callable, [new DateTimeImmutable()]);
391
392
        $this->assertSame(DateTimeImmutable::class, $result);
393
    }
394
395
    /**
396
     * Required `object` type should not be requested from the container
397
     */
398
    public function testInvokeWithRequiredObjectTypeWithoutInstance(): void
399
    {
400
        $container = $this->getContainer();
401
        $callable = fn (object $object) => get_class($object);
402
403
        $this->expectException(MissingRequiredArgumentException::class);
404
405
        (new Injector($container))->invoke($callable);
406
    }
407
408
    /**
409
     * Arguments passed by reference
410
     */
411
    public function testInvokeReferencedArguments(): void
412
    {
413
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
414
        $foo = 1;
415
        $bar = new stdClass();
416
        $baz = null;
417
        $callable = static function (
418
            int &$foo,
419
            object &$bar,
420
            &$baz,
421
            ?ColorInterface &$nullable,
422
            EngineInterface &$object, // from container
423
            DateTimeInterface &...$dates // collect all unnamed DateTimeInterface objects
424
        ) {
425
            $return = func_get_args();
426
            $bar = new DateTimeImmutable();
427
            $baz = false;
428
            $foo = count($dates);
429
            return $return;
430
        };
431
        $result = (new Injector($container))
432
            ->invoke($callable, [
433
                new DateTimeImmutable(),
434
                new DateTime(),
435
                new DateTime(),
436
                'foo' => &$foo,
437
                'bar' => $bar,
438
                'baz' => &$baz,
439
            ]);
440
441
        // passed
442
        $this->assertSame(1, $result[0]);
443
        $this->assertInstanceOf(stdClass::class, $result[1]);
444
        $this->assertNull($result[2]);
445
        $this->assertNull($result[3]);
446
        $this->assertInstanceOf(EngineMarkTwo::class, $result[4]);
447
        // transformed
448
        $this->assertSame(3, $foo); // count of DateTimeInterface objects
449
        $this->assertInstanceOf(stdClass::class, $bar);
450
        $this->assertFalse($baz);
451
    }
452
453
    public function testInvokeReferencedArgumentNamedVariadic(): void
454
    {
455
        $container = $this->getContainer();
456
457
        $callable = static function (DateTimeInterface &...$dates) {
458
            $dates[0] = false;
459
            $dates[1] = false;
460
            return count($dates);
461
        };
462
        $foo = new DateTimeImmutable();
463
        $bar = new DateTimeImmutable();
464
        $result = (new Injector($container))
465
            ->invoke($callable, [
466
                $foo,
467
                &$bar,
468
                new DateTime(),
469
            ]);
470
471
        $this->assertSame(3, $result);
472
        $this->assertInstanceOf(DateTimeImmutable::class, $foo);
473
        $this->assertFalse($bar);
474
    }
475
476
    /**
477
     * If argument passed by reference but it is not supported by function
478
     */
479
    public function testInvokeReferencedArgument(): void
480
    {
481
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
482
        $foo = 1;
483
        $callable = fn (int $foo) => ++$foo;
484
        $result = (new Injector($container))->invoke($callable, ['foo' => &$foo]);
485
486
        // $foo has been not changed
487
        $this->assertSame(1, $foo);
488
        $this->assertSame(2, $result);
489
    }
490
491
    public function testWrongNamedParam(): void
492
    {
493
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
494
495
        $callable = fn (EngineInterface $engine) => $engine;
496
497
        $this->expectException(\Throwable::class);
498
499
        (new Injector($container))->invoke($callable, ['engine' => new DateTimeImmutable()]);
500
    }
501
502
    public function testArrayArgumentWithUnnamedType(): void
503
    {
504
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
505
506
        $callable = fn (array $arg) => $arg;
507
508
        $this->expectException(MissingRequiredArgumentException::class);
509
510
        (new Injector($container))->invoke($callable, [['test']]);
511
    }
512
513
    public function testCallableArgumentWithUnnamedType(): void
514
    {
515
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
516
517
        $callable = fn (callable $arg) => $arg();
518
519
        $this->expectException(MissingRequiredArgumentException::class);
520
521
        (new Injector($container))->invoke($callable, [fn () => true]);
522
    }
523
524
    public function testIterableArgumentWithUnnamedType(): void
525
    {
526
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
527
528
        $callable = fn (iterable $arg) => $arg;
529
530
        $this->expectException(MissingRequiredArgumentException::class);
531
532
        (new Injector($container))->invoke($callable, [new \SplStack()]);
533
    }
534
535
    public function testUnnamedScalarParam(): void
536
    {
537
        $container = $this->getContainer();
538
539
        $getEngineName = fn () => 42;
540
541
        $this->expectException(InvalidArgumentException::class);
542
543
        (new Injector($container))->invoke($getEngineName, ['test']);
544
    }
545
546
    public function testInvokeable(): void
547
    {
548
        $container = $this->getContainer();
549
        $result = (new Injector($container))->invoke(new Invokeable());
550
        $this->assertSame(42, $result);
551
    }
552
553
    /**
554
     * Constructor method not defined
555
     */
556
    public function testMakeWithoutConstructor(): void
557
    {
558
        $container = $this->getContainer();
559
560
        $object = (new Injector($container))->make(MakeNoConstructor::class);
561
562
        $this->assertInstanceOf(MakeNoConstructor::class, $object);
563
    }
564
565
    /**
566
     * Constructor without arguments
567
     */
568
    public function testMakeWithoutArguments(): void
569
    {
570
        $container = $this->getContainer();
571
572
        $object = (new Injector($container))->make(MakeEmptyConstructor::class);
573
574
        $this->assertInstanceOf(MakeEmptyConstructor::class, $object);
575
    }
576
577
    /**
578
     * Private constructor unavailable from Injector context
579
     */
580
    public function testMakeWithPrivateConstructor(): void
581
    {
582
        $container = $this->getContainer();
583
584
        $this->expectException(\InvalidArgumentException::class);
585
        $this->expectExceptionMessageMatches('/not instantiable/');
586
587
        (new Injector($container))->make(MakePrivateConstructor::class);
588
    }
589
590
    public function testMakeInvalidClass(): void
591
    {
592
        $container = $this->getContainer();
593
594
        $this->expectException(\ReflectionException::class);
595
        $this->expectExceptionMessageMatches('/does not exist/');
596
597
        (new Injector($container))->make(UndefinedClassThatShouldNotBeDefined::class);
0 ignored issues
show
Bug introduced by
The type Yiisoft\Injector\Tests\U...sThatShouldNotBeDefined was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
598
    }
599
600
    public function testMakeInternalClass(): void
601
    {
602
        $container = $this->getContainer();
603
        $object = (new Injector($container))->make(DateTimeImmutable::class);
604
        $this->assertInstanceOf(DateTimeImmutable::class, $object);
605
    }
606
607
    public function testMakeAbstractClass(): void
608
    {
609
        $container = $this->getContainer();
610
        $this->expectException(\InvalidArgumentException::class);
611
        $this->expectExceptionMessageMatches('/not instantiable/');
612
        (new Injector($container))->make(LightEngine::class);
613
    }
614
615
    public function testMakeInterface(): void
616
    {
617
        $container = $this->getContainer();
618
        $this->expectException(\InvalidArgumentException::class);
619
        $this->expectExceptionMessageMatches('/not instantiable/');
620
        (new Injector($container))->make(EngineInterface::class);
621
    }
622
623
    public function testMakeWithVariadicFromContainer(): void
624
    {
625
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
626
627
        $object = (new Injector($container))->make(MakeEngineCollector::class, []);
628
629
        $this->assertSame(1, count($object->engines));
630
        $this->assertSame([$container->get(EngineInterface::class)], $object->engines);
631
    }
632
633
    public function testMakeWithVariadicFromArguments(): void
634
    {
635
        $container = $this->getContainer();
636
637
        $values = [new EngineMarkTwo(), new EngineVAZ2101()];
638
        $object = (new Injector($container))->make(MakeEngineCollector::class, $values);
639
640
        $this->assertSame($values, $object->engines);
641
    }
642
643
    public function testMakeWithCustomParam(): void
644
    {
645
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
646
647
        $object = (new Injector($container))
648
            ->make(MakeEngineMatherWithParam::class, [new EngineVAZ2101(), 'parameter' => 'power']);
649
650
        $this->assertNotSame($object->engine1, $object->engine2);
651
        $this->assertInstanceOf(EngineVAZ2101::class, $object->engine1);
652
        $this->assertNotSame(EngineMarkTwo::class, $object->engine2);
653
        $this->assertSame($object->parameter, 'power');
654
    }
655
656
    public function testMakeWithInvalidCustomParam(): void
657
    {
658
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
659
660
        $this->expectException(\TypeError::class);
661
662
        (new Injector($container))->make(MakeEngineMatherWithParam::class, ['parameter' => 100500]);
663
    }
664
665
    private function getContainer(array $definitions = []): ContainerInterface
666
    {
667
        return new class($definitions) implements ContainerInterface {
668
            private array $definitions = [];
669
            public function __construct(array $definitions = [])
670
            {
671
                $this->definitions = $definitions;
672
            }
673
            public function get($id)
674
            {
675
                if (!$this->has($id)) {
676
                    throw new class() extends \Exception implements NotFoundExceptionInterface {
677
                    };
678
                }
679
                return $this->definitions[$id];
680
            }
681
            public function has($id)
682
            {
683
                return array_key_exists($id, $this->definitions);
684
            }
685
        };
686
    }
687
}
688