Completed
Push — 2.x ( 8f6930...447f85 )
by Akihito
01:14
created

AssistedInjectInterceptor   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 107
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 5
dl 0
loc 107
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A invoke() 0 21 4
A getNamedArguments() 0 15 3
A getDependency() 0 10 4
A getName() 0 17 3
A getCustomeInject() 0 13 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Di;
6
7
use Ray\Aop\MethodInterceptor;
8
use Ray\Aop\MethodInvocation;
9
use Ray\Di\Di\Assisted;
10
use Ray\Di\Di\Inject;
11
use Ray\Di\Di\InjectInterface;
12
use Ray\Di\Di\Named;
13
use ReflectionAttribute;
14
use ReflectionNamedType;
15
use ReflectionParameter;
16
17
use function assert;
18
use function call_user_func_array;
19
use function get_class;
20
use function in_array;
21
use function is_callable;
22
23
/**
24
 * Assisted injection interceptor for #[Inject] attributed parameter
25
 */
26
final class AssistedInjectInterceptor implements MethodInterceptor
27
{
28
    /** @var InjectorInterface */
29
    private $injector;
30
31
    /** @var MethodInvocationProvider */
32
    private $methodInvocationProvider;
33
34
    public function __construct(InjectorInterface $injector, MethodInvocationProvider $methodInvocationProvider)
35
    {
36
        $this->injector = $injector;
37
        $this->methodInvocationProvider = $methodInvocationProvider;
38
    }
39
40
    /**
41
     * @return mixed
42
     */
43
    public function invoke(MethodInvocation $invocation)
44
    {
45
        $this->methodInvocationProvider->set($invocation);
46
        $params = $invocation->getMethod()->getParameters();
47
        $namedArguments = $this->getNamedArguments($invocation);
48
        foreach ($params as $param) {
49
            /** @var list<ReflectionAttribute> $inject */
0 ignored issues
show
Documentation introduced by
The doc-type list<ReflectionAttribute> could not be parsed: Expected "|" or "end of type", but got "<" at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
50
            $inject = $param->getAttributes(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF);
51
            /** @var list<ReflectionAttribute> $assisted */
0 ignored issues
show
Documentation introduced by
The doc-type list<ReflectionAttribute> could not be parsed: Expected "|" or "end of type", but got "<" at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
52
            $assisted = $param->getAttributes(Assisted::class);
53
            if (isset($assisted[0]) || isset($inject[0])) {
54
                /** @psalm-suppress MixedAssignment */
55
                $namedArguments[$param->getName()] = $this->getDependency($param);
0 ignored issues
show
Bug introduced by
Consider using $param->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
56
            }
57
        }
58
59
        $callable = [$invocation->getThis(), $invocation->getMethod()->getName()];
0 ignored issues
show
Bug introduced by
Consider using $invocation->getMethod()->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
60
        assert(is_callable($callable));
61
62
        return call_user_func_array($callable, $namedArguments); // @phpstan-ignore-line PHP8 named arguments
63
    }
64
65
    /**
66
     * @return array<string, mixed>
0 ignored issues
show
Documentation introduced by
The doc-type array<string, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
67
     */
68
    private function getNamedArguments(MethodInvocation $invocation): array
69
    {
70
        $args = $invocation->getArguments();
71
        $params = $invocation->getMethod()->getParameters();
72
        $namedParams = [];
73
        foreach ($params as $param) {
74
            $pos = $param->getPosition();
75
            if (isset($args[$pos])) {
76
                /** @psalm-suppress MixedAssignment */
77
                $namedParams[$param->getName()] = $args[$pos];
0 ignored issues
show
Bug introduced by
Consider using $param->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
78
            }
79
        }
80
81
        return $namedParams;
82
    }
83
84
    /**
85
     * @return mixed
86
     */
87
    private function getDependency(ReflectionParameter $param)
88
    {
89
        $named = (string) $this->getName($param);
90
        $type = $param->getType();
91
        assert($type instanceof ReflectionNamedType || $type === null);
0 ignored issues
show
Bug introduced by
The class ReflectionNamedType does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
92
        $typeName = $type ? $type->getName() : '';
93
        $interface = in_array($typeName, Argument::UNBOUND_TYPE) ? '' : $typeName;
94
95
        return $this->injector->getInstance($interface, $named);
96
    }
97
98
    private function getName(ReflectionParameter $param): ?string
99
    {
100
        /** @var list<ReflectionAttribute> $nameds */
0 ignored issues
show
Documentation introduced by
The doc-type list<ReflectionAttribute> could not be parsed: Expected "|" or "end of type", but got "<" at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
101
        $nameds = $param->getAttributes(Named::class);
102
        if (isset($nameds[0])) {
103
            $named = $nameds[0]->newInstance();
104
            assert($named instanceof Named);
105
106
            return $named->value;
107
        }
108
109
        if ($param->getAttributes(Inject::class)) {
110
            return null;
111
        }
112
113
        return $this->getCustomeInject($param);
114
    }
115
116
    /**
117
     * @return ?class-string
0 ignored issues
show
Documentation introduced by
The doc-type ?class-string could not be parsed: Unknown type name "?class-string" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
118
     */
119
    private function getCustomeInject(ReflectionParameter $param): ?string
120
    {
121
        /** @var list<ReflectionAttribute> $injects */
0 ignored issues
show
Documentation introduced by
The doc-type list<ReflectionAttribute> could not be parsed: Expected "|" or "end of type", but got "<" at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
122
        $injects = $param->getAttributes(InjectInterface::class, ReflectionAttribute::IS_INSTANCEOF);
123
        if (! $injects) {
124
            return null;
125
        }
126
127
        $inject = $injects[0]->newInstance();
128
        assert($inject instanceof InjectInterface);
129
130
        return get_class($inject);
131
    }
132
}
133