NamedParamMetas   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 195
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 11
Bugs 1 Features 0
Metric Value
eloc 69
dl 0
loc 195
rs 10
c 11
b 1
f 0
ccs 52
cts 52
cp 1
wmc 30

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getDefault() 0 3 2
A getAssistedNames() 0 18 4
A getNames() 0 10 3
A setAssistedAnnotation() 0 7 2
A getAttributeParamMetas() 0 30 5
A getWebContext() 0 12 3
A getAnnotationParamMetas() 0 8 1
A addNamedParams() 0 22 4
A __invoke() 0 12 2
A getParam() 0 8 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 Override;
10
use Ray\Aop\ReflectionMethod;
11
use Ray\Di\Di\Assisted;
12
use Ray\WebContextParam\Annotation\AbstractWebContextParam;
13
use ReflectionAttribute;
14
use ReflectionNamedType;
15
use ReflectionParameter;
16
17
final class NamedParamMetas implements NamedParamMetasInterface
18
{
19
    /**
20
     * {@inheritDoc}
21
     */
22
    #[Override]
23
    public function __invoke(callable $callable): array
24
    {
25 107
        /** @var array{0:object, 1:string} $callable */
26
        $method = new ReflectionMethod($callable[0], $callable[1]);
27 107
        $paramMetas = $this->getAttributeParamMetas($method);
28 107
29 107
        if (! $paramMetas) {
30
            $paramMetas = $this->getAnnotationParamMetas($method);
31 75
        }
32
33 75
        return $paramMetas;
34
    }
35
36 75
    /** @return array<string, AssistedWebContextParam|ParamInterface> */
37 75
    private function getAnnotationParamMetas(ReflectionMethod $method): array
38 75
    {
39 8
        $parameters = $method->getParameters();
40
        $annotations = $method->getAnnotations();
41 72
        $assistedNames = $this->getAssistedNames($annotations);
42 72
        $webContext = $this->getWebContext($annotations);
43 72
44 72
        return $this->addNamedParams($parameters, $assistedNames, $webContext);
45 72
    }
46 72
47 72
    /**
48
     * @return array<string, ParamInterface>
49 72
     *
50
     * @psalm-suppress TooManyTemplateParams $refAttribute
51
     */
52 72
    private function getAttributeParamMetas(ReflectionMethod $method): array
53
    {
54 72
        $parameters = $method->getParameters();
55 72
        $names = $valueParams = [];
56 35
        foreach ($parameters as $parameter) {
57 3
            $refAttribute = $parameter->getAttributes(RequestParamInterface::class, ReflectionAttribute::IS_INSTANCEOF);
58
            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...
59 35
                /** @var ?ResourceParam $resourceParam */
60 35
                $resourceParam = $refAttribute[0]->newInstance();
61
                if ($resourceParam instanceof ResourceParam) {
62
                    $names[$parameter->name] = new AssistedResourceParam($resourceParam);
63
                    continue;
64 72
                }
65
            }
66
67 72
            $refWebContext = $parameter->getAttributes(AbstractWebContextParam::class, ReflectionAttribute::IS_INSTANCEOF);
68
            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...
69 72
                $webParam = $refWebContext[0]->newInstance();
70 72
                $default = $this->getDefault($parameter);
71 35
                $param = new AssistedWebContextParam($webParam, $default);
72 35
                $names[$parameter->name] = $param;
73
                continue;
74
            }
75
76 72
            $valueParams[$parameter->name] = $parameter;
77
        }
78
79 1
        $names = $this->getNames($names, $valueParams);
80
81
        return $names;
82 1
    }
83 1
84
    /**
85
     * @param array<Assisted|object|ResourceParam> $annotations
86 1
     *
87
     * @return array<string, ParamInterface>
88
     */
89
    private function getAssistedNames(array $annotations): array
90
    {
91
        $names = [];
92
        foreach ($annotations as $annotation) {
93
            if ($annotation instanceof ResourceParam) {
94 72
                $names[$annotation->param] = new AssistedResourceParam($annotation);
95
            }
96 72
97 72
            if (! ($annotation instanceof Assisted)) {
98 65
                continue;
99 4
            }
100
101 4
            // @codeCoverageIgnoreStart
102
            $names = $this->setAssistedAnnotation($names, $annotation); // BC for annotation
103 64
            // @codeCoverageIgnoreEnd
104 4
        }
105 4
106
        return $names;
107 4
    }
108
109 62
    /**
110
     * @param array<object> $annotations
111
     *
112 72
     * @return array<string, AbstractWebContextParam>
113
     */
114
    private function getWebContext(array $annotations): array
115 4
    {
116
        $webcontext = [];
117 4
        foreach ($annotations as $annotation) {
118
            if (! ($annotation instanceof AbstractWebContextParam)) {
119
                continue;
120 62
            }
121
122 62
            $webcontext[$annotation->param] = $annotation;
123
        }
124
125
        return $webcontext;
126
    }
127
128
    /**
129
     * @param array<string, ParamInterface> $names
130
     *
131
     * @return array<string, ParamInterface>
132
     *
133
     * @codeCoverageIgnore BC for annotation
134
     */
135
    private function setAssistedAnnotation(array $names, Assisted $assisted): array
136
    {
137
        foreach ($assisted->values as $assistedParam) {
138
            $names[$assistedParam] = new AssistedParam();
139
        }
140
141
        return $names;
142
    }
143
144
    /**
145
     * @param ReflectionParameter[]                  $parameters
146
     * @param array<string, ParamInterface>          $assistedNames
147
     * @param array<string, AbstractWebContextParam> $webcontext
148
     *
149
     * @return (AssistedWebContextParam|ParamInterface)[]
150
     * @psalm-return array<string, AssistedWebContextParam|ParamInterface>
151
     */
152
    private function addNamedParams(array $parameters, array $assistedNames, array $webcontext): array
153
    {
154
        $names = [];
155
        foreach ($parameters as $parameter) {
156
            $name = $parameter->name;
157
            if (isset($assistedNames[$name])) {
158
                $names[$name] = $assistedNames[$parameter->name];
159
160
                continue;
161
            }
162
163
            if (isset($webcontext[$name])) {
164
                $default = $this->getDefault($parameter);
165
                $names[$name] = new AssistedWebContextParam($webcontext[$name], $default);
166
167
                continue;
168
            }
169
170
            $names[$name] = $this->getParam($parameter);
171
        }
172
173
        return $names;
174
    }
175
176
    /** @psalm-return DefaultParam<mixed>|NoDefaultParam */
177
    private function getDefault(ReflectionParameter $parameter): DefaultParam|NoDefaultParam
178
    {
179
        return $parameter->isDefaultValueAvailable() === true ? new DefaultParam($parameter->getDefaultValue()) : new NoDefaultParam();
180
    }
181
182
    /**
183
     * @param array<string, AssistedResourceParam|AssistedWebContextParam> $names
184
     * @param array<string, ReflectionParameter>                           $valueParams
185
     *
186
     * @return array<string, ParamInterface>
187
     */
188
    private function getNames(array $names, array $valueParams): array
189
    {
190
        // if there is more than single attributes
191
        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...
192
            foreach ($valueParams as $paramName => $valueParam) {
193
                $names[$paramName] = $this->getParam($valueParam);
194
            }
195
        }
196
197
        return $names;
198
    }
199
200
    /**
201
     * @return ClassParam|OptionalParam|RequiredParam
202
     * @psalm-return ClassParam|OptionalParam<mixed>|RequiredParam
203
     */
204
    private function getParam(ReflectionParameter $parameter): ParamInterface
205
    {
206
        $type = $parameter->getType();
207
        if ($type instanceof ReflectionNamedType && ! $type->isBuiltin()) {
208
            return new ClassParam($type, $parameter);
209
        }
210
211
        return $parameter->isDefaultValueAvailable() === true ? new OptionalParam($parameter->getDefaultValue()) : new RequiredParam();
212
    }
213
}
214