Passed
Push — master ( 96cf3f...2f1f5f )
by Maxim
06:45 queued 22s
created

Invoker   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 62
Duplicated Lines 0 %

Test Coverage

Coverage 85.19%

Importance

Changes 0
Metric Value
wmc 10
eloc 30
dl 0
loc 62
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 7 1
B invoke() 0 42 9
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
29 1319
    public function __construct(Registry $constructor)
30
    {
31 1319
        $constructor->set('invoker', $this);
32
33 1319
        $this->container = $constructor->get('container', ContainerInterface::class);
34 1319
        $this->resolver = $constructor->get('resolver', ResolverInterface::class);
35 1319
        $this->options = $constructor->getOptions();
36
    }
37
38
    /**
39
     * @psalm-param TResolver $target
40
     */
41 686
    public function invoke(mixed $target, array $parameters = []): mixed
42
    {
43 686
        if (\is_array($target) && isset($target[1])) {
44
            // In a form of resolver and method
45 491
            [$resolver, $method] = $target;
46
47
            // Resolver instance (i.e. [ClassName::class, 'method'])
48 491
            if (\is_string($resolver)) {
49 455
                $resolver = $this->container->get($resolver);
50
            }
51
52
            try {
53 490
                $method = new \ReflectionMethod($resolver, $method);
54
            } catch (\ReflectionException $e) {
55
                throw new ContainerException($e->getMessage(), $e->getCode(), $e);
56
            }
57
58
            // Invoking factory method with resolved arguments
59 490
            return $method->invokeArgs(
60 490
                $resolver,
61 490
                $this->resolver->resolveArguments($method, $parameters),
62 490
            );
63
        }
64
65 641
        if (\is_string($target) && \is_callable($target)) {
66 2
            $target = $target(...);
67
        }
68
69 641
        if ($target instanceof \Closure) {
70
            try {
71 639
                $reflection = new \ReflectionFunction($target);
72
            } catch (\ReflectionException $e) {
73
                throw new ContainerException($e->getMessage(), $e->getCode(), $e);
74
            }
75
76
            // Invoking Closure with resolved arguments
77 639
            return $reflection->invokeArgs(
78 639
                $this->resolver->resolveArguments($reflection, $parameters, $this->options->validateArguments),
79 639
            );
80
        }
81
82 2
        throw new NotCallableException('Unsupported callable.');
83
    }
84
}
85