Passed
Pull Request — 1.x (#322)
by Akihito
05:02 queued 02:37
created

OptionsMethods::getInsFromMethodAnnotations()   B

Complexity

Conditions 8
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
cc 8
eloc 9
nc 3
nop 2
dl 0
loc 17
rs 8.4444
c 0
b 0
f 0
ccs 0
cts 0
cp 0
crap 72
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource;
6
7
use BEAR\Resource\Annotation\Embed;
8
use BEAR\Resource\Annotation\JsonSchema;
9
use BEAR\Resource\Annotation\Link;
10
use Ray\Aop\ReflectionMethod;
11
use Ray\Di\Di\Named;
12
use Ray\WebContextParam\Annotation\AbstractWebContextParam;
13
use Ray\WebContextParam\Annotation\CookieParam;
14
use Ray\WebContextParam\Annotation\EnvParam;
15
use Ray\WebContextParam\Annotation\FilesParam;
16
use Ray\WebContextParam\Annotation\FormParam;
17
use Ray\WebContextParam\Annotation\QueryParam;
18
use Ray\WebContextParam\Annotation\ServerParam;
19
20
use function assert;
21
use function class_exists;
22
use function file_exists;
23
use function file_get_contents;
24
use function json_decode;
25
26
use const JSON_THROW_ON_ERROR;
27
28
final class OptionsMethods
29
{
30
    /**
31
     * Constants for annotation name and "in" name
32
     */
33
    private const WEB_CONTEXT_NAME = [
34
        CookieParam::class => 'cookie',
35
        EnvParam::class => 'env',
36
        FormParam::class => 'formData',
37
        QueryParam::class => 'query',
38
        ServerParam::class => 'server',
39
        FilesParam::class => 'files',
40
    ];
41
42 99
    public function __construct(
43
        #[Named('json_schema_dir')]
44 99
        private readonly string $schemaDir = '',
45 99
    ) {
46 99
    }
47
48 7
    /**
49
     * return array{summary?: string, description?: string, request: array, links: array, embed: array}
50 7
     *
51 7
     * @return array<int|string, array<mixed>|string>
52 7
     */
53 7
    public function __invoke(ResourceObject $ro, string $requestMethod): array
54 7
    {
55 7
        $method = new ReflectionMethod($ro::class, 'on' . $requestMethod);
56 7
        $ins = $this->getInMap($method);
57 1
        [$doc, $paramDoc] = (new OptionsMethodDocBolck())($method);
58
        $methodOption = $doc;
59
        $paramMetas = (new OptionsMethodRequest())($method, $paramDoc, $ins);
60 6
        $schema = $this->getJsonSchema($method);
61
        $request = $paramMetas ? ['request' => $paramMetas] : [];
62
        $methodOption += $request;
63 7
        if (! empty($schema)) {
64
            $methodOption += ['schema' => $schema];
65 7
        }
66 7
67 7
        $extras = $this->getMethodExtras($method);
68 5
        if (! empty($extras)) {
69 5
            $methodOption += $extras;
70
        }
71
72
        return $methodOption;
73 7
    }
74
75
    /**
76 7
     * @return (Embed|Link)[][]
77
     * @psalm-return array{links?: non-empty-list<Link>, embed?: non-empty-list<Embed>}
78 7
     * @phpstan-return (Embed|Link)[][]
79 7
     */
80 5
    private function getMethodExtras(ReflectionMethod $method): array
81
    {
82 2
        $extras = [];
83 2
        $annotations = $method->getAnnotations();
84 1
        foreach ($annotations as $annotation) {
85
            if ($annotation instanceof Link) {
86
                $extras['links'][] = $annotation;
87 1
            }
88
89
            if (! ($annotation instanceof Embed)) {
90
                continue;
91
            }
92
93
            $extras['embed'][] = $annotation;
94
        }
95
96
        return $extras;
97
    }
98
99
    /** @return array<array-key, array-key> */
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, array-key> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, array-key>.
Loading history...
100
    private function getInMap(ReflectionMethod $method): array
101
    {
102
        $ins = [];
103
        bc_for_annotation: {
104
            // @codeCoverageIgnoreStart
105
            $annotations = $method->getAnnotations();
106
            $ins = $this->getInsFromMethodAnnotations($annotations, $ins);
107
        if ($ins) {
108
            return $ins;
109
        }
110
            // @codeCoverageIgnoreEnd
111
        }
112
113
        /** @var array<string, string> $insParam */
114
        $insParam = $this->getInsFromParameterAttributes($method, $ins);
115
116
        return $insParam;
117
    }
118
119
    /** @return array<array-key, mixed> */
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, mixed> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, mixed>.
Loading history...
120
    private function getJsonSchema(ReflectionMethod $method): array
121
    {
122
        $schema = $method->getAnnotation(JsonSchema::class);
123
        if (! $schema instanceof JsonSchema) {
124
            return [];
125
        }
126
127
        $schemaFile = $this->schemaDir . '/' . $schema->schema;
128
        if (! file_exists($schemaFile)) {
129
            return [];
130
        }
131
132
        return (array) json_decode((string) file_get_contents($schemaFile), null, 512, JSON_THROW_ON_ERROR);
133
    }
134
135
    /**
136
     * @param array<object>            $annotations
137
     * @param array<array-key, string> $ins
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, string> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, string>.
Loading history...
138
     *
139
     * @return array<array-key, array-key>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, array-key> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, array-key>.
Loading history...
140
     *
141
     * @codeCoverageIgnore BC for annotation
142
     */
143
    public function getInsFromMethodAnnotations(array $annotations, array $ins): array
144
    {
145
        foreach ($annotations as $annotation) {
146
            if (! ($annotation instanceof AbstractWebContextParam)) {
147
                continue;
148
            }
149
150
            $class = $annotation::class;
151
            assert(class_exists($class));
152
            /** @var array-key $webKey */
153
            assert($class === CookieParam::class || $class === EnvParam::class || $class === FormParam::class || $class === QueryParam::class || $class === ServerParam::class || $class === FilesParam::class);
154
            $webKey = self::WEB_CONTEXT_NAME[$class];
155
156
            $ins[$annotation->param] = $webKey;
157
        }
158
159
        return $ins;
160
    }
161
162
    /**
163
     * @param array<array-key, array-key> $ins
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, array-key> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, array-key>.
Loading history...
164
     *
165
     * @return array<array-key, array-key>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, array-key> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, array-key>.
Loading history...
166
     */
167
    public function getInsFromParameterAttributes(ReflectionMethod $method, array $ins): array|null
168
    {
169
        $parameters = $method->getParameters();
170
        foreach ($parameters as $parameter) {
171
            $attributes = $parameter->getAttributes();
172
            foreach ($attributes as $attribute) {
173
                $instance = $attribute->newInstance();
174
                if (! ($instance instanceof AbstractWebContextParam)) {
175
                    continue;
176
                }
177
178
                /** @var array-key $class */
179
                $class = $instance::class;
180
                $ins[$parameter->name] = self::WEB_CONTEXT_NAME[$class];
181
            }
182
        }
183
184
        return $ins;
185
    }
186
}
187