Test Failed
Pull Request — master (#1221)
by Aleksei
12:29
created

Invoker   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 72
Duplicated Lines 0 %

Test Coverage

Coverage 85.19%

Importance

Changes 0
Metric Value
wmc 15
eloc 37
dl 0
loc 72
ccs 23
cts 27
cp 0.8519
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
C invoke() 0 50 14
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Core\Internal;
6
7
use Psr\Container\ContainerInterface;
8
use Spiral\Core\Exception\Container\ContainerException;
9
use Spiral\Core\Exception\Container\NotCallableException;
10
use Spiral\Core\Internal\Common\DestructorTrait;
11
use Spiral\Core\Internal\Common\Registry;
12
use Spiral\Core\InvokerInterface;
13
use Spiral\Core\Options;
14
use Spiral\Core\ResolverInterface;
15
16
/**
17
 * @psalm-type TResolver = class-string|non-empty-string|callable|array{class-string, non-empty-string}
18
 *
19
 * @internal
20
 */
21
final class Invoker implements InvokerInterface
22
{
23
    use DestructorTrait;
24
25
    private ContainerInterface $container;
26
    private ResolverInterface $resolver;
27
    private Options $options;
28
    private Actor $actor;
29 1434
30
    public function __construct(Registry $constructor)
31 1434
    {
32
        $constructor->set('invoker', $this);
33 1434
34 1434
        $this->container = $constructor->get('container', ContainerInterface::class);
35 1434
        $this->resolver = $constructor->get('resolver', ResolverInterface::class);
36
        $this->actor = $constructor->get('actor', Actor::class);
37
        $this->options = $constructor->getOptions();
38
    }
39
40
    /**
41 799
     * @psalm-param TResolver $target
42
     */
43 799
    public function invoke(mixed $target, array $parameters = []): mixed
44
    {
45 600
        if (\is_array($target) && isset($target[1])) {
46
            // In a form of resolver and method
47
            [$resolver, $method] = $target;
48 600
49 588
            // Resolver instance or class name if the method is static (i.e. [ClassName::class, 'method'])
50
            if (\is_string($resolver)) {
51
                // Detect return type
52
                $type = $this->actor->resolveType($resolver, $binding, $singleton, $injector);
53 599
54
                if ($singleton === null) {
55
                    $type ??= $injector === null && $binding === null ? $resolver : null;
56
                    $resolver = \is_callable([$type, $method]) ? $type : $this->container->get($resolver);
57
                } else {
58
                    $resolver = $singleton;
59 599
                }
60 599
            }
61 599
62 599
            try {
63
                $method = new \ReflectionMethod($resolver, $method);
64
            } catch (\ReflectionException $e) {
65 779
                throw new ContainerException($e->getMessage(), $e->getCode(), $e);
66 2
            }
67
68
            // Invoking factory method with resolved arguments
69 779
            return $method->invokeArgs(
70
                $method->isStatic() ? null : $resolver,
71 777
                $this->resolver->resolveArguments($method, $parameters),
72
            );
73
        }
74
75
        if (\is_string($target) && \is_callable($target)) {
76
            $target = $target(...);
77 777
        }
78 777
79 777
        if ($target instanceof \Closure) {
80
            try {
81
                $reflection = new \ReflectionFunction($target);
82 2
            } catch (\ReflectionException $e) {
83
                throw new ContainerException($e->getMessage(), $e->getCode(), $e);
84
            }
85
86
            // Invoking Closure with resolved arguments
87
            return $reflection->invokeArgs(
88
                $this->resolver->resolveArguments($reflection, $parameters, $this->options->validateArguments),
89
            );
90
        }
91
92
        throw new NotCallableException('Unsupported callable.');
93
    }
94
}
95