Issues (141)

src/OptionsMethods.php (1 issue)

Labels
Severity
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 file_exists;
21
use function file_get_contents;
22
use function json_decode;
23
24
use const JSON_THROW_ON_ERROR;
25
26
/**
27
 * @psalm-import-type Body from Types
28
 * @psalm-import-type InsMap from Types
29
 * @psalm-import-type OptionsMethodsResponse from Types
30
 * @psalm-import-type OptionsResponse from Types
31
 */
32
final readonly class OptionsMethods
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_READONLY, expecting T_CLASS on line 32 at column 6
Loading history...
33
{
34
    /**
35
     * Constants for annotation name and "in" name
36
     */
37
    private const WEB_CONTEXT_NAME = [
38
        CookieParam::class => 'cookie',
39
        EnvParam::class => 'env',
40
        FormParam::class => 'formData',
41
        QueryParam::class => 'query',
42
        ServerParam::class => 'server',
43
        FilesParam::class => 'files',
44
    ];
45
46
    public function __construct(
47
        #[Named('json_schema_dir')]
48
        private string $schemaDir = '',
49
    ) {
50
    }
51
52
    /** @return OptionsMethodsResponse */
53
    public function __invoke(ResourceObject $ro, string $requestMethod): array
54
    {
55
        $method = new ReflectionMethod($ro::class, 'on' . $requestMethod);
56
        $ins = $this->getInMap($method);
57
        [$doc, $paramDoc] = (new OptionsMethodDocBolck())($method);
58
        $methodOption = $doc;
59
        $paramMetas = (new OptionsMethodRequest())($method, $paramDoc, $ins);
60
        $schema = $this->getJsonSchema($method);
61
        $request = ! empty($paramMetas) ? ['request' => $paramMetas] : [];
62
        $methodOption += $request;
63
        if (! empty($schema)) {
64
            $methodOption += ['schema' => $schema];
65
        }
66
67
        $extras = $this->getMethodExtras($method);
68
        if (! empty($extras)) {
69
            $methodOption += $extras;
70
        }
71
72
        return $methodOption; // @phpstan-ignore-line
73
    }
74
75
    /**
76
     * @return (Embed|Link)[][]
77
     * @psalm-return array{links?: non-empty-list<Link>, embed?: non-empty-list<Embed>}
78
     * @phpstan-return (Embed|Link)[][]
79
     */
80
    private function getMethodExtras(ReflectionMethod $method): array
81
    {
82
        $extras = [];
83
        $annotations = $method->getAnnotations();
84
        foreach ($annotations as $annotation) {
85
            if ($annotation instanceof Link) {
86
                $extras['links'][] = $annotation;
87
            }
88
89
            if (! ($annotation instanceof Embed)) {
90
                continue;
91
            }
92
93
            $extras['embed'][] = $annotation;
94
        }
95
96
        return $extras;
97
    }
98
99
    /** @return InsMap */
100
    private function getInMap(ReflectionMethod $method): array
101
    {
102
        $ins = [];
103
104
        return $this->getInsFromParameterAttributes($method, $ins);
105
    }
106
107
    /** @return Body */
108
    private function getJsonSchema(ReflectionMethod $method): array
109
    {
110
        $schema = $method->getAnnotation(JsonSchema::class);
111
        if (! $schema instanceof JsonSchema) {
112
            return [];
113
        }
114
115
        $schemaFile = $this->schemaDir . '/' . $schema->schema;
116
        if (! file_exists($schemaFile)) {
117
            return [];
118
        }
119
120
        return (array) json_decode((string) file_get_contents($schemaFile), null, 512, JSON_THROW_ON_ERROR);
121
    }
122
123
    /**
124
     * @param InsMap $ins
125
     *
126
     * @return InsMap
127
     *
128
     * @psalm-suppress InvalidArrayOffset, MixedAssignment, MixedReturnTypeCoercion
129
     */
130
    public function getInsFromParameterAttributes(ReflectionMethod $method, array $ins): array
131
    {
132
        $parameters = $method->getParameters();
133
        foreach ($parameters as $parameter) {
134
            $attributes = $parameter->getAttributes();
135
            foreach ($attributes as $attribute) {
136
                $instance = $attribute->newInstance();
137
                if (! ($instance instanceof AbstractWebContextParam)) {
138
                    continue;
139
                }
140
141
                /** @var array-key $class */
142
                /** @phpstan-ignore varTag.nativeType */
143
                $class = $instance::class;
144
                $ins[$parameter->name] = self::WEB_CONTEXT_NAME[$class];
145
            }
146
        }
147
148
        return $ins;
149
    }
150
}
151