OptionsMethods::getInsFromMethodAnnotations()   B
last analyzed

Complexity

Conditions 8
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 9
c 0
b 0
f 0
nc 3
nop 2
dl 0
loc 17
rs 8.4444
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
/**
29
 * @psalm-import-type Body from Types
30
 * @psalm-import-type InsMap from Types
31
 * @psalm-import-type OptionsMethodsResponse from Types
32
 * @psalm-import-type OptionsResponse from Types
33
 */
34
final class OptionsMethods
35
{
36
    /**
37
     * Constants for annotation name and "in" name
38
     */
39
    private const WEB_CONTEXT_NAME = [
40
        CookieParam::class => 'cookie',
41
        EnvParam::class => 'env',
42
        FormParam::class => 'formData',
43
        QueryParam::class => 'query',
44
        ServerParam::class => 'server',
45
        FilesParam::class => 'files',
46
    ];
47
48
    public function __construct(
49
        #[Named('json_schema_dir')]
50
        private readonly string $schemaDir = '',
51
    ) {
52
    }
53
54
    /** @return OptionsMethodsResponse */
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\OptionsMethodsResponse 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...
55
    public function __invoke(ResourceObject $ro, string $requestMethod): array
56
    {
57
        $method = new ReflectionMethod($ro::class, 'on' . $requestMethod);
58
        $ins = $this->getInMap($method);
59
        [$doc, $paramDoc] = (new OptionsMethodDocBolck())($method);
60
        $methodOption = $doc;
61
        $paramMetas = (new OptionsMethodRequest())($method, $paramDoc, $ins);
62
        $schema = $this->getJsonSchema($method);
63
        $request = ! empty($paramMetas) ? ['request' => $paramMetas] : [];
64
        $methodOption += $request;
65
        if (! empty($schema)) {
66
            $methodOption += ['schema' => $schema];
67
        }
68
69
        $extras = $this->getMethodExtras($method);
70
        if (! empty($extras)) {
71
            $methodOption += $extras;
72
        }
73
74
        return $methodOption; // @phpstan-ignore-line
75
    }
76
77
    /**
78
     * @return (Embed|Link)[][]
79
     * @psalm-return array{links?: non-empty-list<Link>, embed?: non-empty-list<Embed>}
80
     * @phpstan-return (Embed|Link)[][]
81
     */
82
    private function getMethodExtras(ReflectionMethod $method): array
83
    {
84
        $extras = [];
85
        $annotations = $method->getAnnotations();
86
        foreach ($annotations as $annotation) {
87
            if ($annotation instanceof Link) {
88
                $extras['links'][] = $annotation;
89
            }
90
91
            if (! ($annotation instanceof Embed)) {
92
                continue;
93
            }
94
95
            $extras['embed'][] = $annotation;
96
        }
97
98
        return $extras;
99
    }
100
101
    /** @return InsMap */
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...
102
    private function getInMap(ReflectionMethod $method): array
103
    {
104
        $ins = [];
105
        bc_for_annotation: {
106
            // @codeCoverageIgnoreStart
107
            $annotations = $method->getAnnotations();
108
            $ins = $this->getInsFromMethodAnnotations($annotations, $ins);
109
        if ($ins) {
110
            return $ins;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ins returns the type array which is incompatible with the documented return type BEAR\Resource\InsMap.
Loading history...
111
        }
112
            // @codeCoverageIgnoreEnd
113
        }
114
115
        return $this->getInsFromParameterAttributes($method, $ins);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getInsFrom...tributes($method, $ins) returns the type array which is incompatible with the documented return type BEAR\Resource\InsMap.
Loading history...
116
    }
117
118
    /** @return Body */
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\Body 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...
119
    private function getJsonSchema(ReflectionMethod $method): array
120
    {
121
        $schema = $method->getAnnotation(JsonSchema::class);
122
        if (! $schema instanceof JsonSchema) {
123
            return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type BEAR\Resource\Body.
Loading history...
124
        }
125
126
        $schemaFile = $this->schemaDir . '/' . $schema->schema;
127
        if (! file_exists($schemaFile)) {
128
            return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type BEAR\Resource\Body.
Loading history...
129
        }
130
131
        return (array) json_decode((string) file_get_contents($schemaFile), null, 512, JSON_THROW_ON_ERROR);
0 ignored issues
show
Bug Best Practice introduced by
The expression return (array)json_decod...2, JSON_THROW_ON_ERROR) returns the type array which is incompatible with the documented return type BEAR\Resource\Body.
Loading history...
132
    }
133
134
    /**
135
     * @param array<object> $annotations
136
     * @param InsMap        $ins
137
     *
138
     * @return InsMap
139
     *
140
     * @codeCoverageIgnore BC for annotation
141
     */
142
    public function getInsFromMethodAnnotations(array $annotations, array $ins): array
143
    {
144
        foreach ($annotations as $annotation) {
145
            if (! ($annotation instanceof AbstractWebContextParam)) {
146
                continue;
147
            }
148
149
            $class = $annotation::class;
150
            assert(class_exists($class));
151
            /** @var array-key $webKey */
152
            assert($class === CookieParam::class || $class === EnvParam::class || $class === FormParam::class || $class === QueryParam::class || $class === ServerParam::class || $class === FilesParam::class);
153
            $webKey = self::WEB_CONTEXT_NAME[$class];
154
155
            $ins[$annotation->param] = $webKey;
156
        }
157
158
        return $ins;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ins returns the type array which is incompatible with the documented return type BEAR\Resource\InsMap.
Loading history...
159
    }
160
161
    /**
162
     * @param InsMap $ins
163
     *
164
     * @return InsMap
165
     *
166
     * @psalm-suppress InvalidArrayOffset, MixedAssignment, MixedReturnTypeCoercion
167
     */
168
    public function getInsFromParameterAttributes(ReflectionMethod $method, array $ins): array
169
    {
170
        $parameters = $method->getParameters();
171
        foreach ($parameters as $parameter) {
172
            $attributes = $parameter->getAttributes();
173
            foreach ($attributes as $attribute) {
174
                $instance = $attribute->newInstance();
175
                if (! ($instance instanceof AbstractWebContextParam)) {
176
                    continue;
177
                }
178
179
                /** @var array-key $class */
180
                /** @phpstan-ignore varTag.nativeType */
181
                $class = $instance::class;
182
                $ins[$parameter->name] = self::WEB_CONTEXT_NAME[$class];
183
            }
184
        }
185
186
        return $ins;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ins returns the type array which is incompatible with the documented return type BEAR\Resource\InsMap.
Loading history...
187
    }
188
}
189