Issues (108)

src/OptionsMethodRequest.php (1 issue)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource;
6
7
use ReflectionException;
8
use ReflectionMethod;
9
use ReflectionNamedType;
10
use ReflectionParameter;
11
12
use function assert;
13
use function is_array;
14
use function is_string;
15
use function method_exists;
16
17
/**
18
 * @psalm-import-type OptionsResponse from Types
19
 * @psalm-import-type InsMap from Types
20
 */
21
final class OptionsMethodRequest
22
{
23
    /**
24
     * Parameter #2 $paramMetas of method BEAR\Resource\OptionsMethodRequest::ignoreAnnotatedPrameter() expects array('parameters' => array<string, array('type' =>
25
     *
26
     * @param array<array-key, array{type: string, description?: string}> $paramDoc
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, array{t... description?: string}> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, array{type: string, description?: string}>.
Loading history...
27
     * @param InsMap                                                      $ins
28
     *
29
     * @return OptionsResponse
30
     */
31
    public function __invoke(ReflectionMethod $method, array $paramDoc, array $ins): array
32
    {
33
        return $this->getParamMetas($method->getParameters(), $paramDoc, $ins);
34
    }
35
36
    /**
37
     * @param array<string, array{type?: string, description?: string}> $paramDoc
38
     *
39
     * @psalm-suppress RedundantCondition for BC
40
     */
41
    private function getParameterType(ReflectionParameter $parameter, array $paramDoc, string $name): string|null
42
    {
43
        $hasType = method_exists($parameter, 'getType') && $parameter->getType();
44
        if ($hasType) {
45
            return $this->getType($parameter);
46
        }
47
48
        return $paramDoc[$name]['type'] ?? null;
49
    }
50
51
    /**
52
     * @param array<ReflectionParameter>                               $parameters
53
     * @param array<string, array{type: string, description?: string}> $paramDoc
54
     * @param InsMap                                                   $ins
55
     *
56
     * @return OptionsResponse
57
     */
58
    private function getParamMetas(array $parameters, array $paramDoc, array $ins): array
59
    {
60
        foreach ($parameters as $parameter) {
61
            $name = (string) $parameter->name;
62
            if (isset($ins[$name])) {
63
                $paramDoc[$name]['in'] = $ins[$parameter->name];
64
            }
65
66
            if (! isset($paramDoc[$parameter->name])) {
67
                $paramDoc[$name] = [];
68
            }
69
70
            $paramDoc = $this->paramType($paramDoc, $parameter);
71
            $paramDoc = $this->paramDefault($paramDoc, $parameter);
72
        }
73
74
        $required = $this->getRequired($parameters);
75
76
        return $this->setParamMetas($paramDoc, $required);
77
    }
78
79
    /**
80
     * @param array<ReflectionParameter> $parameters
81
     *
82
     * @return string[]
83
     * @psalm-return list<string>
84
     */
85
    private function getRequired(array $parameters): array
86
    {
87
        $required = [];
88
        foreach ($parameters as $parameter) {
89
            if ($parameter->isOptional()) {
90
                continue;
91
            }
92
93
            $required[] = $parameter->name;
94
        }
95
96
        return $required;
97
    }
98
99
    /**
100
     * @param array<string, array{type?: string, description?: string}> $paramDoc
101
     *
102
     * @return array<string, array{type?: string, description?: string, default?: string}>
103
     *
104
     * @throws ReflectionException
105
     */
106
    private function paramDefault(array $paramDoc, ReflectionParameter $parameter): array
107
    {
108
        $hasDefault = $parameter->isDefaultValueAvailable() && $parameter->getDefaultValue() !== null;
109
        if ($hasDefault) {
110
            $default = $parameter->getDefaultValue();
111
            $paramDoc[(string) $parameter->name]['default'] = is_array($default) ? '[]' : (string) $parameter->getDefaultValue(); // @phpstan-ignore-lines
112
        }
113
114
        return $paramDoc;
115
    }
116
117
    /**
118
     * @param array<string, array{type?: string, description?: string, default?: string, in?: string}> $paramDoc
119
     *
120
     * @return array<string, array{type?: string, description?: string, default?: string, in?: string}>
121
     */
122
    private function paramType(array $paramDoc, ReflectionParameter $parameter): array
123
    {
124
        $type = $this->getParameterType($parameter, $paramDoc, $parameter->name);
125
        if (is_string($type)) {
126
            $paramDoc[(string) $parameter->name]['type'] = $type; // override type parameter by reflection over phpdoc param type
127
        }
128
129
        return $paramDoc;
130
    }
131
132
    private function getType(ReflectionParameter $parameter): string
133
    {
134
        $namedType = $parameter->getType();
135
        assert($namedType instanceof ReflectionNamedType);
136
        $type = $namedType->getName();
137
        if ($type === 'int') {
138
            $type = 'integer';
139
        }
140
141
        return $type;
142
    }
143
144
    /**
145
     * @param array<string, array{type?: string}> $paramDoc
146
     * @param list<string>                        $required
147
     *
148
     * @return OptionsResponse
149
     */
150
    private function setParamMetas(array $paramDoc, array $required): array
151
    {
152
        $paramMetas = [];
153
        if ((bool) $paramDoc) {
154
            $paramMetas['parameters'] = $paramDoc;
155
        }
156
157
        if ((bool) $required) {
158
            $paramMetas['required'] = $required;
159
        }
160
161
        return $paramMetas;
162
    }
163
}
164