Passed
Pull Request — master (#10)
by
unknown
01:30
created

php$0 ➔ testMakeInternalClassWithOptionalMiddleArgumentSkipped()   A

Complexity

Conditions 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
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();
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, [
378
            new DateTimeImmutable(),
379
            new DateTimeImmutable(),
380
            new EngineMarkTwo()
381
        ]);
382
383
        $this->assertSame(4, $result);
384
    }
385
386
    /**
387
     * Object type may be passed as unnamed parameter
388
     */
389
    public function testInvokeWithObjectType(): void
390
    {
391
        $container = $this->getContainer();
392
        $callable = fn (object $object) => get_class($object);
393
394
        $result = (new Injector($container))->invoke($callable, [new DateTimeImmutable()]);
395
396
        $this->assertSame(DateTimeImmutable::class, $result);
397
    }
398
399
    /**
400
     * Required `object` type should not be requested from the container
401
     */
402
    public function testInvokeWithRequiredObjectTypeWithoutInstance(): void
403
    {
404
        $container = $this->getContainer();
405
        $callable = fn (object $object) => get_class($object);
406
407
        $this->expectException(MissingRequiredArgumentException::class);
408
409
        (new Injector($container))->invoke($callable);
410
    }
411
412
    /**
413
     * Arguments passed by reference
414
     */
415
    public function testInvokeReferencedArguments(): void
416
    {
417
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
418
        $foo = 1;
419
        $bar = new stdClass();
420
        $baz = null;
421
        $callable = static function (
422
            int &$foo,
423
            object &$bar,
424
            &$baz,
425
            ?ColorInterface &$nullable,
426
            EngineInterface &$object, // from container
427
            DateTimeInterface &...$dates // collect all unnamed DateTimeInterface objects
428
        ) {
429
            $return = func_get_args();
430
            $bar = new DateTimeImmutable();
431
            $baz = false;
432
            $foo = count($dates);
433
            return $return;
434
        };
435
        $result = (new Injector($container))
436
            ->invoke($callable, [
437
                new DateTimeImmutable(),
438
                new DateTime(),
439
                new DateTime(),
440
                'foo' => &$foo,
441
                'bar' => $bar,
442
                'baz' => &$baz,
443
            ]);
444
445
        // passed
446
        $this->assertSame(1, $result[0]);
447
        $this->assertInstanceOf(stdClass::class, $result[1]);
448
        $this->assertNull($result[2]);
449
        $this->assertNull($result[3]);
450
        $this->assertInstanceOf(EngineMarkTwo::class, $result[4]);
451
        // transformed
452
        $this->assertSame(3, $foo); // count of DateTimeInterface objects
453
        $this->assertInstanceOf(stdClass::class, $bar);
454
        $this->assertFalse($baz);
455
    }
456
457
    public function testInvokeReferencedArgumentNamedVariadic(): void
458
    {
459
        $container = $this->getContainer();
460
461
        $callable = static function (DateTimeInterface &...$dates) {
462
            $dates[0] = false;
463
            $dates[1] = false;
464
            return count($dates);
465
        };
466
        $foo = new DateTimeImmutable();
467
        $bar = new DateTimeImmutable();
468
        $result = (new Injector($container))
469
            ->invoke($callable, [
470
                $foo,
471
                &$bar,
472
                new DateTime(),
473
            ]);
474
475
        $this->assertSame(3, $result);
476
        $this->assertInstanceOf(DateTimeImmutable::class, $foo);
477
        $this->assertFalse($bar);
478
    }
479
480
    /**
481
     * If argument passed by reference but it is not supported by function
482
     */
483
    public function testInvokeReferencedArgument(): void
484
    {
485
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
486
        $foo = 1;
487
        $callable = fn (int $foo) => ++$foo;
488
        $result = (new Injector($container))->invoke($callable, ['foo' => &$foo]);
489
490
        // $foo has been not changed
491
        $this->assertSame(1, $foo);
492
        $this->assertSame(2, $result);
493
    }
494
495
    public function testWrongNamedParam(): void
496
    {
497
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
498
499
        $callable = fn (EngineInterface $engine) => $engine;
500
501
        $this->expectException(\Throwable::class);
502
503
        (new Injector($container))->invoke($callable, ['engine' => new DateTimeImmutable()]);
504
    }
505
506
    public function testArrayArgumentWithUnnamedType(): void
507
    {
508
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
509
510
        $callable = fn (array $arg) => $arg;
511
512
        $this->expectException(MissingRequiredArgumentException::class);
513
514
        (new Injector($container))->invoke($callable, [['test']]);
515
    }
516
517
    public function testCallableArgumentWithUnnamedType(): void
518
    {
519
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
520
521
        $callable = fn (callable $arg) => $arg();
522
523
        $this->expectException(MissingRequiredArgumentException::class);
524
525
        (new Injector($container))->invoke($callable, [fn () => true]);
526
    }
527
528
    public function testIterableArgumentWithUnnamedType(): void
529
    {
530
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
531
532
        $callable = fn (iterable $arg) => $arg;
533
534
        $this->expectException(MissingRequiredArgumentException::class);
535
536
        (new Injector($container))->invoke($callable, [new \SplStack()]);
537
    }
538
539
    public function testUnnamedScalarParam(): void
540
    {
541
        $container = $this->getContainer();
542
543
        $getEngineName = fn () => 42;
544
545
        $this->expectException(InvalidArgumentException::class);
546
547
        (new Injector($container))->invoke($getEngineName, ['test']);
548
    }
549
550
    public function testInvokeable(): void
551
    {
552
        $container = $this->getContainer();
553
        $result = (new Injector($container))->invoke(new Invokeable());
554
        $this->assertSame(42, $result);
555
    }
556
557
    /**
558
     * Constructor method not defined
559
     */
560
    public function testMakeWithoutConstructor(): void
561
    {
562
        $container = $this->getContainer();
563
564
        $object = (new Injector($container))->make(MakeNoConstructor::class);
565
566
        $this->assertInstanceOf(MakeNoConstructor::class, $object);
567
    }
568
569
    /**
570
     * Constructor without arguments
571
     */
572
    public function testMakeWithoutArguments(): void
573
    {
574
        $container = $this->getContainer();
575
576
        $object = (new Injector($container))->make(MakeEmptyConstructor::class);
577
578
        $this->assertInstanceOf(MakeEmptyConstructor::class, $object);
579
    }
580
581
    /**
582
     * Private constructor unavailable from Injector context
583
     */
584
    public function testMakeWithPrivateConstructor(): void
585
    {
586
        $container = $this->getContainer();
587
588
        $this->expectException(\InvalidArgumentException::class);
589
        $this->expectExceptionMessageMatches('/not instantiable/');
590
591
        (new Injector($container))->make(MakePrivateConstructor::class);
592
    }
593
594
    public function testMakeInvalidClass(): void
595
    {
596
        $container = $this->getContainer();
597
598
        $this->expectException(\ReflectionException::class);
599
        $this->expectExceptionMessageMatches('/does not exist/');
600
601
        (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...
602
    }
603
604
    public function testMakeInternalClass(): void
605
    {
606
        $container = $this->getContainer();
607
        $object = (new Injector($container))->make(DateTimeImmutable::class);
608
        $this->assertInstanceOf(DateTimeImmutable::class, $object);
609
    }
610
611
    public function testMakeInternalClassWithOptionalMiddleArgumentSkipped(): void
612
    {
613
        $container = $this->getContainer();
614
        $object = (new Injector($container))->make(\SplFileObject::class, [
615
            'file_name' => __FILE__,
616
            'use_include_path' => false
617
        ]);
618
        $this->assertInstanceOf(\SplFileObject::class, $object);
619
    }
620
621
    public function testMakeAbstractClass(): void
622
    {
623
        $container = $this->getContainer();
624
        $this->expectException(\InvalidArgumentException::class);
625
        $this->expectExceptionMessageMatches('/not instantiable/');
626
        (new Injector($container))->make(LightEngine::class);
627
    }
628
629
    public function testMakeInterface(): void
630
    {
631
        $container = $this->getContainer();
632
        $this->expectException(\InvalidArgumentException::class);
633
        $this->expectExceptionMessageMatches('/not instantiable/');
634
        (new Injector($container))->make(EngineInterface::class);
635
    }
636
637
    public function testMakeWithVariadicFromContainer(): void
638
    {
639
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
640
641
        $object = (new Injector($container))->make(MakeEngineCollector::class, []);
642
643
        $this->assertSame(1, count($object->engines));
644
        $this->assertSame([$container->get(EngineInterface::class)], $object->engines);
645
    }
646
647
    public function testMakeWithVariadicFromArguments(): void
648
    {
649
        $container = $this->getContainer();
650
651
        $values = [new EngineMarkTwo(), new EngineVAZ2101()];
652
        $object = (new Injector($container))->make(MakeEngineCollector::class, $values);
653
654
        $this->assertSame($values, $object->engines);
655
    }
656
657
    public function testMakeWithCustomParam(): void
658
    {
659
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
660
661
        $object = (new Injector($container))
662
            ->make(MakeEngineMatherWithParam::class, [new EngineVAZ2101(), 'parameter' => 'power']);
663
664
        $this->assertNotSame($object->engine1, $object->engine2);
665
        $this->assertInstanceOf(EngineVAZ2101::class, $object->engine1);
666
        $this->assertNotSame(EngineMarkTwo::class, $object->engine2);
667
        $this->assertSame($object->parameter, 'power');
668
    }
669
670
    public function testMakeWithInvalidCustomParam(): void
671
    {
672
        $container = $this->getContainer([EngineInterface::class => new EngineMarkTwo()]);
673
674
        $this->expectException(\TypeError::class);
675
676
        (new Injector($container))->make(MakeEngineMatherWithParam::class, ['parameter' => 100500]);
677
    }
678
679
    private function getContainer(array $definitions = []): ContainerInterface
680
    {
681
        return new class($definitions) implements ContainerInterface {
682
            private array $definitions = [];
683
            public function __construct(array $definitions = [])
684
            {
685
                $this->definitions = $definitions;
686
            }
687
            public function get($id)
688
            {
689
                if (!$this->has($id)) {
690
                    throw new class() extends \Exception implements NotFoundExceptionInterface {
691
                    };
692
                }
693
                return $this->definitions[$id];
694
            }
695
            public function has($id)
696
            {
697
                return array_key_exists($id, $this->definitions);
698
            }
699
        };
700
    }
701
}
702