1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace BEAR\Resource; |
6
|
|
|
|
7
|
|
|
use Doctrine\Common\Annotations\Reader; |
8
|
|
|
use ReflectionException; |
9
|
|
|
use ReflectionMethod; |
10
|
|
|
use ReflectionNamedType; |
11
|
|
|
use ReflectionParameter; |
12
|
|
|
|
13
|
|
|
use function assert; |
14
|
|
|
use function is_array; |
15
|
|
|
use function is_string; |
16
|
|
|
use function method_exists; |
17
|
|
|
|
18
|
|
|
final class OptionsMethodRequest |
19
|
|
|
{ |
20
|
|
|
public function __construct( |
21
|
|
|
private Reader $reader, |
22
|
|
|
) { |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Parameter #2 $paramMetas of method BEAR\Resource\OptionsMethodRequest::ignoreAnnotatedPrameter() expects array('parameters' => array<string, array('type' => |
27
|
|
|
* |
28
|
|
|
* @param array<string, array{type: string, description?: string}> $paramDoc |
29
|
|
|
* @param array<string, string> $ins |
30
|
|
|
* |
31
|
|
|
* @return array{parameters?: array<string, array{type?: string, description?: string, default?: string}>, required?: array<int, string>} |
32
|
|
|
*/ |
33
|
|
|
public function __invoke(ReflectionMethod $method, array $paramDoc, array $ins): array |
34
|
|
|
{ |
35
|
|
|
return $this->getParamMetas($method->getParameters(), $paramDoc, $ins); |
36
|
|
|
|
37
|
|
|
return $this->ignoreAnnotatedPrameter($method, $paramMetas); |
|
|
|
|
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @param array<string, array{type?: string, description?: string}> $paramDoc |
42
|
|
|
* |
43
|
|
|
* @psalm-suppress RedundantCondition for BC |
44
|
|
|
*/ |
45
|
|
|
private function getParameterType(ReflectionParameter $parameter, array $paramDoc, string $name): string|null |
46
|
|
|
{ |
47
|
|
|
$hasType = method_exists($parameter, 'getType') && $parameter->getType(); |
48
|
|
|
if ($hasType) { |
49
|
|
|
return $this->getType($parameter); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
if (isset($paramDoc[$name]['type'])) { |
53
|
|
|
return $paramDoc[$name]['type']; |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
return null; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @param array<ReflectionParameter> $parameters |
61
|
|
|
* @param array<string, array{type: string, description?: string}> $paramDoc |
62
|
|
|
* @param array<string, string> $ins |
63
|
|
|
* |
64
|
|
|
* @return array{parameters?: array<string, array{type?: string}>, required?: array<int, string>} |
65
|
|
|
*/ |
66
|
|
|
private function getParamMetas(array $parameters, array $paramDoc, array $ins): array |
67
|
|
|
{ |
68
|
|
|
foreach ($parameters as $parameter) { |
69
|
|
|
$name = (string) $parameter->name; |
70
|
|
|
if (isset($ins[$name])) { |
71
|
|
|
$paramDoc[$name]['in'] = $ins[$parameter->name]; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
if (! isset($paramDoc[$parameter->name])) { |
75
|
|
|
$paramDoc[$name] = []; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
$paramDoc = $this->paramType($paramDoc, $parameter); |
79
|
|
|
$paramDoc = $this->paramDefault($paramDoc, $parameter); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
$required = $this->getRequired($parameters); |
83
|
|
|
|
84
|
|
|
return $this->setParamMetas($paramDoc, $required); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* @param array<ReflectionParameter> $parameters |
89
|
|
|
* |
90
|
|
|
* @return string[] |
91
|
|
|
* @psalm-return list<string> |
92
|
|
|
*/ |
93
|
|
|
private function getRequired(array $parameters): array |
94
|
|
|
{ |
95
|
|
|
$required = []; |
96
|
|
|
foreach ($parameters as $parameter) { |
97
|
|
|
if ($parameter->isOptional()) { |
98
|
|
|
continue; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
$required[] = $parameter->name; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
return $required; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @param array<string, array{type?: string, description?: string}> $paramDoc |
109
|
|
|
* |
110
|
|
|
* @return array<string, array{type?: string, description?: string, default?: string}> |
111
|
|
|
* |
112
|
|
|
* @throws ReflectionException |
113
|
|
|
*/ |
114
|
|
|
private function paramDefault(array $paramDoc, ReflectionParameter $parameter): array |
115
|
|
|
{ |
116
|
|
|
$hasDefault = $parameter->isDefaultValueAvailable() && $parameter->getDefaultValue() !== null; |
117
|
|
|
if ($hasDefault) { |
118
|
|
|
$default = $parameter->getDefaultValue(); |
119
|
|
|
$paramDoc[(string) $parameter->name]['default'] = is_array($default) ? '[]' : (string) $parameter->getDefaultValue(); // @phpstan-ignore-lines |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
return $paramDoc; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @param array<string, array{type?: string, description?: string, default?: string, in?: string}> $paramDoc |
127
|
|
|
* |
128
|
|
|
* @return array<string, array{type?: string, description?: string, default?: string, in?: string}> |
129
|
|
|
*/ |
130
|
|
|
private function paramType(array $paramDoc, ReflectionParameter $parameter): array |
131
|
|
|
{ |
132
|
|
|
$type = $this->getParameterType($parameter, $paramDoc, $parameter->name); |
133
|
|
|
if (is_string($type)) { |
134
|
|
|
$paramDoc[(string) $parameter->name]['type'] = $type; // override type parameter by reflection over phpdoc param type |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
return $paramDoc; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
private function getType(ReflectionParameter $parameter): string |
141
|
|
|
{ |
142
|
|
|
$namedType = $parameter->getType(); |
143
|
|
|
assert($namedType instanceof ReflectionNamedType); |
144
|
|
|
$type = $namedType->getName(); |
145
|
|
|
if ($type === 'int') { |
146
|
|
|
$type = 'integer'; |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
return $type; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* @param array<string, array{type?: string}> $paramDoc |
154
|
|
|
* @param list<string> $required |
|
|
|
|
155
|
|
|
* |
156
|
|
|
* @return array{parameters?: array<string, array{type?: string}>, required?: array<int, string>} |
157
|
|
|
*/ |
158
|
|
|
private function setParamMetas(array $paramDoc, array $required): array |
159
|
|
|
{ |
160
|
|
|
$paramMetas = []; |
161
|
|
|
if ((bool) $paramDoc) { |
162
|
|
|
$paramMetas['parameters'] = $paramDoc; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
if ((bool) $required) { |
166
|
|
|
$paramMetas['required'] = $required; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
return $paramMetas; |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.