Completed
Push — 1.x ( 1c1988...6b83a8 )
by Akihito
21s queued 13s
created

src/OptionsMethods.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This file is part of the BEAR.Resource package.
4
 *
5
 * @license http://opensource.org/licenses/MIT MIT
6
 */
7
namespace BEAR\Resource;
8
9
use BEAR\Resource\Annotation\JsonSchema;
10
use BEAR\Resource\Annotation\ResourceParam;
11
use Doctrine\Common\Annotations\Reader;
12
use phpDocumentor\Reflection\DocBlockFactory;
13
use Ray\Di\Di\Assisted;
14
use Ray\Di\Di\Named;
15
use Ray\WebContextParam\Annotation\AbstractWebContextParam;
16
use Ray\WebContextParam\Annotation\CookieParam;
17
use Ray\WebContextParam\Annotation\EnvParam;
18
use Ray\WebContextParam\Annotation\FormParam;
19
use Ray\WebContextParam\Annotation\QueryParam;
20
use Ray\WebContextParam\Annotation\ServerParam;
21
22
final class OptionsMethods
23
{
24
    /**
25
     * Constants for annotation name and "in" name
26
     *
27
     * @var array
28
     */
29
    private $webContextName = [
30
        CookieParam::class => 'cookie',
31
        EnvParam::class => 'env',
32
        FormParam::class => 'formData',
33
        QueryParam::class => 'query',
34
        ServerParam::class => 'server'
35
    ];
36
    private $reader;
37
38
    /**
39
     * @var string
40
     */
41
    private $schemaDir;
42
43
    /**
44
     * @Named("schemaDir=json_schema_dir")
45
     */
46 88
    public function __construct(Reader $reader, $schemaDir = '')
47
    {
48 88
        $this->reader = $reader;
49 88
        $this->schemaDir = $schemaDir;
50 88
    }
51
52 6
    public function __invoke(ResourceObject $ro, string $requestMethod) : array
53
    {
54 6
        $method = new \ReflectionMethod($ro, 'on' . $requestMethod);
55 6
        $ins = $this->getInMap($method);
56 6
        $docComment = $method->getDocComment();
57 6
        $doc = $paramDoc = [];
58 6
        if ($docComment) {
59 6
            list($doc, $paramDoc) = $this->docBlock($docComment);
60
        }
61 6
        $parameters = $method->getParameters();
62 6
        list($paramDoc, $required) = $this->getParameterMetas($parameters, $paramDoc, $ins);
63 6
        $paramMetas = [];
64 6
        if ((bool) $paramDoc) {
65 6
            $paramMetas['parameters'] = $paramDoc;
66
        }
67 6
        if ((bool) $required) {
68 6
            $paramMetas['required'] = $required;
69
        }
70 6
        $paramMetas = $this->ignoreAnnotatedPrameter($method, $paramMetas);
71 6
        $schema = $this->getJsonSchema($method);
72 6
        $request = $paramMetas ? ['request' => $paramMetas] : [];
73 6
        if ($schema) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $schema of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
74 1
            return $doc + $request + ['schema' => $schema];
75
        }
76
77 5
        return $doc + $request;
78
    }
79
80 6
    private function getInMap(\ReflectionMethod $method) : array
81
    {
82 6
        $ins = [];
83 6
        $annotations = $this->reader->getMethodAnnotations($method);
84 6
        foreach ($annotations as $annotation) {
85 4
            if ($annotation instanceof AbstractWebContextParam) {
86 4
                $ins[$annotation->param] = $this->webContextName[get_class($annotation)];
87
            }
88
        }
89
90 6
        return $ins;
91
    }
92
93
    /**
94
     * @return array [$docs, $params]
95
     */
96 6
    private function docBlock(string $docComment) : array
97
    {
98 6
        $factory = DocBlockFactory::createInstance();
99 6
        $docblock = $factory->create($docComment);
100 6
        $summary = $docblock->getSummary();
101 6
        $docs = $params = [];
102 6
        if ($summary) {
103 2
            $docs['summary'] = $summary;
104
        }
105 6
        $description = (string) $docblock->getDescription();
106 6
        if ($description) {
107 2
            $docs['description'] = $description;
108
        }
109 6
        $tags = $docblock->getTagsByName('param');
110 6
        $params = $this->docBlogTags($tags, $params);
111
112 6
        return [$docs, $params];
113
    }
114
115
    /**
116
     * @param \ReflectionParameter $parameter
117
     * @param array                $paramDoc
118
     * @param string               $name
119
     *
120
     * @return string|null
121
     */
122 6
    private function getParameterType(\ReflectionParameter $parameter, array $paramDoc, string $name)
123
    {
124 6
        $hasType = method_exists($parameter, 'getType') && $parameter->getType();
125 6
        if ($hasType) {
126 1
            return $this->getType($parameter);
127
        }
128 6
        if (isset($paramDoc[$name]['type'])) {
129 3
            return $paramDoc[$name]['type'];
130
        }
131 3
    }
132
133
    /**
134
     * @param \ReflectionParameter[] $parameters
135
     * @param array                  $paramDoc
136
     *
137
     * @return array [$paramDoc, $required]
138
     */
139 6
    private function getParameterMetas(array $parameters, array $paramDoc, array $ins) : array
140
    {
141 6
        $required = [];
142 6
        foreach ($parameters as $parameter) {
143 6
            if (isset($ins[$parameter->name])) {
144 2
                $paramDoc[$parameter->name]['in'] = $ins[$parameter->name];
145
            }
146 6
            if (! isset($paramDoc[$parameter->name])) {
147 3
                $paramDoc[$parameter->name] = [];
148
            }
149 6
            $paramDoc = $this->paramType($paramDoc, $parameter);
150 6
            if (! $parameter->isOptional()) {
151 6
                $required[] = $parameter->name;
152
            }
153 6
            $paramDoc = $this->paramDefault($paramDoc, $parameter);
154
        }
155
156 6
        return [$paramDoc, $required];
157
    }
158
159 6
    private function paramDefault(array $paramDoc, \ReflectionParameter $parameter) : array
160
    {
161 6
        $hasDefault = $parameter->isDefaultValueAvailable() && $parameter->getDefaultValue() !== null;
162 6
        if ($hasDefault) {
163 2
            $paramDoc[$parameter->name]['default'] = (string) $parameter->getDefaultValue();
164
        }
165
166 6
        return $paramDoc;
167
    }
168
169 6
    private function paramType(array $paramDoc, \ReflectionParameter $parameter) : array
170
    {
171 6
        $type = $this->getParameterType($parameter, $paramDoc, $parameter->name);
172 6
        if (is_string($type)) {
173 3
            $paramDoc[$parameter->name]['type'] = $type;
174
        }
175
176 6
        return $paramDoc;
177
    }
178
179
    /**
180
     * @param \ReflectionParameter $parameter
181
     */
182 1
    private function getType(\ReflectionParameter $parameter) : string
183
    {
184 1
        $type = (string) $parameter->getType();
185 1
        if ($type === 'int') {
186 1
            $type = 'integer';
187
        }
188
189 1
        return $type;
190
    }
191
192 6
    private function docBlogTags(array $tags, array $params) : array
193
    {
194 6
        foreach ($tags as $tag) {
195
            /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Param */
196 3
            $varName = $tag->getVariableName();
197 3
            $tagType = (string) $tag->getType();
198 3
            $type = $tagType === 'int' ? 'integer' : $tagType;
199 3
            $params[$varName] = [
200 3
                'type' => $type
201
            ];
202 3
            $description = (string) $tag->getDescription();
203 3
            if ($description) {
204 3
                $params[$varName]['description'] = $description;
205
            }
206
        }
207
208 6
        return $params;
209
    }
210
211
    /**
212
     * Ignore @ Assisted @ ResourceParam parameter
213
     */
214 6
    private function ignoreAnnotatedPrameter(\ReflectionMethod $method, array $paramMetas) : array
215
    {
216 6
        $annotations = $this->reader->getMethodAnnotations($method);
217 6
        foreach ($annotations as $annotation) {
218 4
            if ($annotation instanceof ResourceParam) {
219 1
                unset($paramMetas['parameters'][$annotation->param]);
220 1
                $paramMetas['required'] = array_values(array_diff($paramMetas['required'], [$annotation->param]));
221
            }
222 4
            if ($annotation instanceof Assisted) {
223 4
                $paramMetas = $this->ignorreAssisted($paramMetas, $annotation);
224
            }
225
        }
226
227 6
        return $paramMetas;
228
    }
229
230
    /**
231
     * Ignore @ Assisted parameter
232
     */
233 1
    private function ignorreAssisted(array $paramMetas, Assisted $annotation) : array
234
    {
235 1
        $paramMetas['required'] = array_values(array_diff($paramMetas['required'], $annotation->values));
236 1
        foreach ($annotation->values as $varName) {
237 1
            unset($paramMetas['parameters'][$varName]);
238
        }
239
240 1
        return $paramMetas;
241
    }
242
243 6
    private function getJsonSchema(\ReflectionMethod $method) : array
244
    {
245 6
        $schema = $this->reader->getMethodAnnotation($method, JsonSchema::class);
246 6
        if (! $schema instanceof JsonSchema) {
247 5
            return [];
248
        }
249 1
        $schemaFile = $this->schemaDir . '/' . $schema->schema;
250 1
        if (! file_exists($schemaFile)) {
251
            return [];
252
        }
253
254 1
        return (array) json_decode(file_get_contents($schemaFile));
255
    }
256
}
257