Passed
Pull Request — 1.x (#286)
by Akihito
11:53
created

NamedParamMetas   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 15
Bugs 4 Features 0
Metric Value
eloc 68
dl 0
loc 190
ccs 52
cts 52
cp 1
rs 10
c 15
b 4
f 0
wmc 30

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getDefault() 0 3 2
A getAssistedNames() 0 16 4
A getNames() 0 10 3
A __invoke() 0 11 2
A setAssistedAnnotation() 0 7 2
A getAttributeParamMetas() 0 30 5
A getWebContext() 0 12 3
A getAnnotationParamMetas() 0 8 1
A getParam() 0 8 4
A addNamedParams() 0 22 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource;
6
7
use BEAR\Resource\Annotation\RequestParamInterface;
8
use BEAR\Resource\Annotation\ResourceParam;
9
use Ray\Aop\ReflectionMethod;
10
use Ray\Di\Di\Assisted;
11
use Ray\WebContextParam\Annotation\AbstractWebContextParam;
12
use ReflectionAttribute;
13
use ReflectionNamedType;
14
use ReflectionParameter;
15
16
final class NamedParamMetas implements NamedParamMetasInterface
17
{
18
    /**
19
     * {@inheritDoc}
20
     */
21
    public function __invoke(callable $callable): array
22
    {
23
        /** @var array{0:object, 1:string} $callable */
24
        $method = new ReflectionMethod($callable[0], $callable[1]);
25 107
        $paramMetas = $this->getAttributeParamMetas($method);
26
27 107
        if (! $paramMetas) {
28 107
            $paramMetas = $this->getAnnotationParamMetas($method);
29 107
        }
30
31 75
        return $paramMetas;
32
    }
33 75
34
    /** @return array<string, AssistedWebContextParam|ParamInterface> */
35
    private function getAnnotationParamMetas(ReflectionMethod $method): array
36 75
    {
37 75
        $parameters = $method->getParameters();
38 75
        $annotations = $method->getAnnotations();
39 8
        $assistedNames = $this->getAssistedNames($annotations);
40
        $webContext = $this->getWebContext($annotations);
41 72
42 72
        return $this->addNamedParams($parameters, $assistedNames, $webContext);
43 72
    }
44 72
45 72
    /**
46 72
     * @return array<string, ParamInterface>
47 72
     *
48
     * @psalm-suppress TooManyTemplateParams $refAttribute
49 72
     */
50
    private function getAttributeParamMetas(ReflectionMethod $method): array
51
    {
52 72
        $parameters = $method->getParameters();
53
        $names = $valueParams = [];
54 72
        foreach ($parameters as $parameter) {
55 72
            $refAttribute = $parameter->getAttributes(RequestParamInterface::class, ReflectionAttribute::IS_INSTANCEOF);
56 35
            if ($refAttribute) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $refAttribute of type ReflectionAttribute[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
57 3
                /** @var ?ResourceParam $resourceParam */
58
                $resourceParam = $refAttribute[0]->newInstance();
59 35
                if ($resourceParam instanceof ResourceParam) {
60 35
                    $names[$parameter->name] = new AssistedResourceParam($resourceParam);
61
                    continue;
62
                }
63
            }
64 72
65
            $refWebContext = $parameter->getAttributes(AbstractWebContextParam::class, ReflectionAttribute::IS_INSTANCEOF);
66
            if ($refWebContext) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $refWebContext of type ReflectionAttribute[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
67 72
                $webParam = $refWebContext[0]->newInstance();
68
                $default = $this->getDefault($parameter);
69 72
                $param = new AssistedWebContextParam($webParam, $default);
70 72
                $names[$parameter->name] = $param;
71 35
                continue;
72 35
            }
73
74
            $valueParams[$parameter->name] = $parameter;
75
        }
76 72
77
        $names = $this->getNames($names, $valueParams);
78
79 1
        return $names;
80
    }
81
82 1
    /**
83 1
     * @param array<Assisted|object|ResourceParam> $annotations
84
     *
85
     * @return array<string, ParamInterface>
86 1
     */
87
    private function getAssistedNames(array $annotations): array
88
    {
89
        $names = [];
90
        foreach ($annotations as $annotation) {
91
            if ($annotation instanceof ResourceParam) {
92
                $names[$annotation->param] = new AssistedResourceParam($annotation);
93
            }
94 72
95
            if (! ($annotation instanceof Assisted)) {
96 72
                continue;
97 72
            }
98 65
99 4
            $names = $this->setAssistedAnnotation($names, $annotation);
100
        }
101 4
102
        return $names;
103 64
    }
104 4
105 4
    /**
106
     * @param array<object> $annotations
107 4
     *
108
     * @return array<string, AbstractWebContextParam>
109 62
     */
110
    private function getWebContext(array $annotations): array
111
    {
112 72
        $webcontext = [];
113
        foreach ($annotations as $annotation) {
114
            if (! ($annotation instanceof AbstractWebContextParam)) {
115 4
                continue;
116
            }
117 4
118
            $webcontext[$annotation->param] = $annotation;
119
        }
120 62
121
        return $webcontext;
122 62
    }
123
124
    /**
125
     * @param array<string, ParamInterface> $names
126
     *
127
     * @return array<string, ParamInterface>
128
     */
129
    private function setAssistedAnnotation(array $names, Assisted $assisted): array
130
    {
131
        foreach ($assisted->values as $assistedParam) {
132
            $names[$assistedParam] = new AssistedParam();
133
        }
134
135
        return $names;
136
    }
137
138
    /**
139
     * @param ReflectionParameter[]                  $parameters
140
     * @param array<string, ParamInterface>          $assistedNames
141
     * @param array<string, AbstractWebContextParam> $webcontext
142
     *
143
     * @return (AssistedWebContextParam|ParamInterface)[]
144
     * @psalm-return array<string, AssistedWebContextParam|ParamInterface>
145
     */
146
    private function addNamedParams(array $parameters, array $assistedNames, array $webcontext): array
147
    {
148
        $names = [];
149
        foreach ($parameters as $parameter) {
150
            $name = $parameter->name;
151
            if (isset($assistedNames[$name])) {
152
                $names[$name] = $assistedNames[$parameter->name];
153
154
                continue;
155
            }
156
157
            if (isset($webcontext[$name])) {
158
                $default = $this->getDefault($parameter);
159
                $names[$name] = new AssistedWebContextParam($webcontext[$name], $default);
160
161
                continue;
162
            }
163
164
            $names[$name] = $this->getParam($parameter);
165
        }
166
167
        return $names;
168
    }
169
170
    /** @psalm-return DefaultParam<mixed>|NoDefaultParam */
171
    private function getDefault(ReflectionParameter $parameter): DefaultParam|NoDefaultParam
172
    {
173
        return $parameter->isDefaultValueAvailable() === true ? new DefaultParam($parameter->getDefaultValue()) : new NoDefaultParam();
174
    }
175
176
    /**
177
     * @param array<string, AssistedResourceParam|AssistedWebContextParam> $names
178
     * @param array<string, ReflectionParameter>                           $valueParams
179
     *
180
     * @return array<string, ParamInterface>
181
     */
182
    private function getNames(array $names, array $valueParams): array
183
    {
184
        // if there is more than single attributes
185
        if ($names) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $names of type array<string,BEAR\Resour...ssistedWebContextParam> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
186
            foreach ($valueParams as $paramName => $valueParam) {
187
                $names[$paramName] = $this->getParam($valueParam);
188
            }
189
        }
190
191
        return $names;
192
    }
193
194
    /**
195
     * @return ClassParam|OptionalParam|RequiredParam
196
     * @psalm-return ClassParam|OptionalParam<mixed>|RequiredParam
197
     */
198
    private function getParam(ReflectionParameter $parameter): ParamInterface
199
    {
200
        $type = $parameter->getType();
201
        if ($type instanceof ReflectionNamedType && ! $type->isBuiltin()) {
202
            return new ClassParam($type, $parameter);
203
        }
204
205
        return $parameter->isDefaultValueAvailable() === true ? new OptionalParam($parameter->getDefaultValue()) : new RequiredParam();
206
    }
207
}
208