Passed
Pull Request — 1.x (#342)
by Akihito
01:38
created

OptionsMethodRequest::expandInputParameter()   C

Complexity

Conditions 13
Paths 40

Size

Total Lines 56
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 13

Importance

Changes 0
Metric Value
cc 13
eloc 30
nc 40
nop 2
dl 0
loc 56
ccs 14
cts 14
cp 1
crap 13
rs 6.6166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource;
6
7
use Ray\InputQuery\Attribute\Input;
8
use ReflectionAttribute;
9
use ReflectionClass;
10
use ReflectionException;
11
use ReflectionMethod;
12
use ReflectionNamedType;
13
use ReflectionParameter;
14
15 7
use function assert;
16
use function class_exists;
17 7
use function is_array;
18 7
use function is_string;
19
use function method_exists;
20 7
21
/**
22 7
 * @psalm-import-type OptionsResponse from Types
23
 * @psalm-import-type InsMap from Types
24 7
 * @psalm-import-type ParameterMetadata from Types
25
 * @psalm-import-type ParametersMap from Types
26
 * @psalm-import-type RequiredParameterList from Types
27
 * @psalm-import-type ReflectionParameterList from Types
28
 */
29
final class OptionsMethodRequest
30 7
{
31
    /**
32 7
     * @param ParametersMap $paramDoc
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\ParametersMap was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33 7
     * @param InsMap        $ins
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\InsMap was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
34 7
     *
35
     * @return OptionsResponse
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\OptionsResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
36 3
     */
37 2
    public function __invoke(ReflectionMethod $method, array $paramDoc, array $ins): array
38
    {
39 1
        return $this->getParamMetas($method->getParameters(), $paramDoc, $ins);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getParamMe...ers(), $paramDoc, $ins) returns the type array which is incompatible with the documented return type BEAR\Resource\OptionsResponse.
Loading history...
40
    }
41 7
42
    /**
43 7
     * @param ParametersMap $paramDoc
44 7
     *
45 2
     * @psalm-suppress RedundantCondition for BC
46
     */
47 7
    private function getParameterType(ReflectionParameter $parameter, array $paramDoc, string $name): string|null
48 4
    {
49
        /** @phpstan-ignore function.alreadyNarrowedType */
50 7
        $hasType = method_exists($parameter, 'getType') && $parameter->getType();
51 7
        if ($hasType) {
52
            return $this->getType($parameter);
53 7
        }
54
55 7
        return $paramDoc[$name]['type'] ?? null;
56
    }
57
58
    /**
59
     * @param ReflectionParameterList $parameters
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\ReflectionParameterList was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
60
     * @param ParametersMap           $paramDoc
61 7
     * @param InsMap                  $ins
62
     *
63 7
     * @return OptionsResponse
64 7
     */
65 7
    private function getParamMetas(array $parameters, array $paramDoc, array $ins): array
66 7
    {
67
        $expandedParameters = [];
68
        $expandedRequired = [];
69
70 7
        foreach ($parameters as $parameter) {
71
            // Check for #[Input] attribute with object type
72
            $inputAttributes = $parameter->getAttributes(Input::class, ReflectionAttribute::IS_INSTANCEOF);
73 7
            if ($inputAttributes) {
74
                $inputResult = $this->expandInputParameter($parameter, $ins);
75 7
                if ($inputResult !== null) {
76 7
                    [$inputParamDoc, $inputRequired] = $inputResult;
77 2
                    $expandedParameters += $inputParamDoc;
78
                    $expandedRequired = [...$expandedRequired, ...$inputRequired];
79
                    continue;
80 7
                }
81
            }
82
83 7
            $name = (string) $parameter->name;
84
            if (isset($ins[$name])) {
85 7
                $paramDoc[$name]['in'] = $ins[$parameter->name];
86 7
            }
87 7
88
            if (! isset($paramDoc[$parameter->name])) {
89
                $paramDoc[$name] = [];
90 7
            }
91
92
            $paramDoc = $this->paramType($paramDoc, $parameter);
93 7
            $paramDoc = $this->paramDefault($paramDoc, $parameter);
94
        }
95 7
96 7
        // Merge expanded parameters with regular parameters
97 4
        $paramDoc = $expandedParameters + $paramDoc;
98
99
        $required = $this->getRequired($parameters);
100 7
        // Replace required with expanded required if we have Input parameters
101
        if ($expandedRequired !== []) {
102
            $required = $expandedRequired;
103
        }
104
105
        return $this->setParamMetas($paramDoc, $required);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->setParamMetas($paramDoc, $required) returns the type array which is incompatible with the documented return type BEAR\Resource\OptionsResponse.
Loading history...
106 7
    }
107
108 7
    /**
109 7
     * Expand #[Input] parameter to its constructor properties
110 5
     *
111 1
     * @param InsMap $ins
112 1
     *
113
     * @return array{0: ParametersMap, 1: RequiredParameterList}|null
114 5
     */
115 5
    private function expandInputParameter(ReflectionParameter $parameter, array $ins): array|null
116
    {
117
        $type = $parameter->getType();
118
        if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) {
119 7
            return null;
120
        }
121
122
        $className = $type->getName();
123
        if (! class_exists($className)) {
124
            return null;
125 1
        }
126
127 1
        $refClass = new ReflectionClass($className);
128 1
        $constructor = $refClass->getConstructor();
129 1
        if ($constructor === null) {
130
            return null;
131
        }
132 1
133
        $paramDoc = [];
134
        $required = [];
135 7
136
        foreach ($constructor->getParameters() as $ctorParam) {
137 7
            $name = $ctorParam->getName();
138 7
            $paramDoc[$name] = [];
139 7
140
            // Set type
141 7
            $ctorType = $ctorParam->getType();
142 7
            if ($ctorType instanceof ReflectionNamedType) {
143
                $typeName = $ctorType->getName();
144
                if ($typeName === 'int') {
145 7
                    $typeName = 'integer';
146
                }
147
148
                $paramDoc[$name]['type'] = $typeName;
149
            }
150
151
            // Set default if available
152
            if ($ctorParam->isDefaultValueAvailable() && $ctorParam->getDefaultValue() !== null) {
153
                $default = $ctorParam->getDefaultValue();
154
                $paramDoc[$name]['default'] = is_array($default) ? '[]' : (string) $default;
155
            }
156
157
            // Check if required
158
            if (! $ctorParam->isOptional()) {
159
                $required[] = $name;
160
            }
161
162
            // Check for "in" mapping
163
            if (! isset($ins[$name])) {
164
                continue;
165
            }
166
167
            $paramDoc[$name]['in'] = $ins[$name];
168
        }
169
170
        return [$paramDoc, $required];
171
    }
172
173
    /**
174
     * @param ReflectionParameterList $parameters
175
     *
176
     * @return RequiredParameterList
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\RequiredParameterList was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
177
     */
178
    private function getRequired(array $parameters): array
179
    {
180
        $required = [];
181
        foreach ($parameters as $parameter) {
182
            if ($parameter->isOptional()) {
183
                continue;
184
            }
185
186
            $required[] = $parameter->name;
187
        }
188
189
        return $required;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $required returns the type array which is incompatible with the documented return type BEAR\Resource\RequiredParameterList.
Loading history...
190
    }
191
192
    /**
193
     * @param ParametersMap $paramDoc
194
     *
195
     * @return ParametersMap
196
     *
197
     * @throws ReflectionException
198
     */
199
    private function paramDefault(array $paramDoc, ReflectionParameter $parameter): array
200
    {
201
        $hasDefault = $parameter->isDefaultValueAvailable() && $parameter->getDefaultValue() !== null;
202
        if ($hasDefault) {
203
            $default = $parameter->getDefaultValue();
204
            $paramDoc[(string) $parameter->name]['default'] = is_array($default) ? '[]' : (string) $parameter->getDefaultValue(); // @phpstan-ignore-lines
205
        }
206
207
        return $paramDoc;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $paramDoc returns the type array which is incompatible with the documented return type BEAR\Resource\ParametersMap.
Loading history...
208
    }
209
210
    /**
211
     * @param ParametersMap $paramDoc
212
     *
213
     * @return ParametersMap
214
     */
215
    private function paramType(array $paramDoc, ReflectionParameter $parameter): array
216
    {
217
        $type = $this->getParameterType($parameter, $paramDoc, $parameter->name);
218
        if (is_string($type)) {
219
            $paramDoc[(string) $parameter->name]['type'] = $type; // override type parameter by reflection over phpdoc param type
220
        }
221
222
        return $paramDoc;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $paramDoc returns the type array which is incompatible with the documented return type BEAR\Resource\ParametersMap.
Loading history...
223
    }
224
225
    private function getType(ReflectionParameter $parameter): string
226
    {
227
        $namedType = $parameter->getType();
228
        assert($namedType instanceof ReflectionNamedType);
229
        $type = $namedType->getName();
230
        if ($type === 'int') {
231
            $type = 'integer';
232
        }
233
234
        return $type;
235
    }
236
237
    /**
238
     * @param ParametersMap         $paramDoc
239
     * @param RequiredParameterList $required
240
     *
241
     * @return OptionsResponse
242
     */
243
    private function setParamMetas(array $paramDoc, array $required): array
244
    {
245
        $paramMetas = [];
246
        if ((bool) $paramDoc) {
247
            $paramMetas['parameters'] = $paramDoc;
248
        }
249
250
        if ((bool) $required) {
251
            $paramMetas['required'] = $required;
252
        }
253
254
        return $paramMetas;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $paramMetas returns the type array which is incompatible with the documented return type BEAR\Resource\OptionsResponse.
Loading history...
255
    }
256
}
257