Completed
Push — 1.x ( ad6346...9a2ed7 )
by Akihito
06:13
created

OptionsMethods::getJsonSchema()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0175

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 13
ccs 7
cts 8
cp 0.875
rs 9.4285
cc 3
eloc 8
nc 3
nop 1
crap 3.0175
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 86
    public function __construct(Reader $reader, $schemaDir = '')
47
    {
48 86
        $this->reader = $reader;
49 86
        $this->schemaDir = $schemaDir;
50 86
    }
51
52
    /**
53
     * @param ResourceObject $ro
54
     * @param string         $requestMethod
55
     *
56
     * @return array
57
     */
58 6
    public function __invoke(ResourceObject $ro, $requestMethod)
59
    {
60 6
        $method = new \ReflectionMethod($ro, 'on' . $requestMethod);
61 6
        $ins = $this->getInMap($method);
62 6
        $docComment = $method->getDocComment();
63 6
        $doc = $paramDoc = [];
64 6
        if ($docComment) {
65 6
            list($doc, $paramDoc) = $this->docBlock($docComment);
66
        }
67 6
        $parameters = $method->getParameters();
68 6
        list($paramDoc, $required) = $this->getParameterMetas($parameters, $paramDoc, $ins);
69 6
        $paramMetas = [];
70 6
        if ((bool) $paramDoc) {
71 6
            $paramMetas['parameters'] = $paramDoc;
72
        }
73 6
        if ((bool) $required) {
74 6
            $paramMetas['required'] = $required;
75
        }
76 6
        $paramMetas = $this->ignoreAnnotatedPrameter($method, $paramMetas);
77 6
        $schema = $this->getJsonSchema($method);
78 6
        $request = $paramMetas ? ['request' => $paramMetas] : [];
79 6
        if ($schema) {
80 1
            return $doc + $request + ['schema' => $schema];
81
        }
82
83 5
        return $doc + $request;
84
    }
85
86 6
    private function getInMap(\ReflectionMethod $method)
87
    {
88 6
        $ins = [];
89 6
        $annotations = $this->reader->getMethodAnnotations($method);
90 6
        foreach ($annotations as $annotation) {
91 4
            if ($annotation instanceof AbstractWebContextParam) {
92 4
                $ins[$annotation->param] = $this->webContextName[get_class($annotation)];
93
            }
94
        }
95
96 6
        return $ins;
97
    }
98
99
    /**
100
     * @param string $docComment
101
     *
102
     * @return array [$docs, $params]
103
     */
104 6
    private function docBlock($docComment)
105
    {
106 6
        $factory = DocBlockFactory::createInstance();
107 6
        $docblock = $factory->create($docComment);
108 6
        $summary = $docblock->getSummary();
109 6
        $docs = $params = [];
110 6
        if ($summary) {
111 2
            $docs['summary'] = $summary;
112
        }
113 6
        $description = (string) $docblock->getDescription();
114 6
        if ($description) {
115 2
            $docs['description'] = $description;
116
        }
117 6
        $tags = $docblock->getTagsByName('param');
118 6
        $params = $this->docBlogTags($tags, $params);
119
120 6
        return [$docs, $params];
121
    }
122
123
    /**
124
     * @param \ReflectionParameter $parameter
125
     * @param array                $paramDoc
126
     * @param string               $name
127
     *
128
     * @return string|null
129
     */
130 6
    private function getParameterType(\ReflectionParameter $parameter, array $paramDoc, $name)
131
    {
132 6
        $hasType = method_exists($parameter, 'getType') && $parameter->getType();
133 6
        if ($hasType) {
134 1
            return $this->getType($parameter);
135
        }
136 6
        if (isset($paramDoc[$name]['type'])) {
137 3
            return $paramDoc[$name]['type'];
138
        }
139 3
    }
140
141
    /**
142
     * @param \ReflectionParameter[] $parameters
143
     * @param array                  $paramDoc
144
     *
145
     * @return array [$paramDoc, $required]
146
     */
147 6
    private function getParameterMetas(array $parameters, array $paramDoc, array $ins)
148
    {
149 6
        $required = [];
150 6
        foreach ($parameters as $parameter) {
151 6
            if (isset($ins[$parameter->name])) {
152 2
                $paramDoc[$parameter->name]['in'] = $ins[$parameter->name];
153
            }
154 6
            if (! isset($paramDoc[$parameter->name])) {
155 3
                $paramDoc[$parameter->name] = [];
156
            }
157 6
            $paramDoc = $this->paramType($paramDoc, $parameter);
158 6
            if (! $parameter->isOptional()) {
159 6
                $required[] = $parameter->name;
160
            }
161 6
            $paramDoc = $this->paramDefault($paramDoc, $parameter);
162
        }
163
164 6
        return [$paramDoc, $required];
165
    }
166
167
    /**
168
     * @return array
169
     */
170 6
    private function paramDefault(array $paramDoc, \ReflectionParameter $parameter)
171
    {
172 6
        $hasDefault = $parameter->isDefaultValueAvailable() && $parameter->getDefaultValue() !== null;
173 6
        if ($hasDefault) {
174 2
            $paramDoc[$parameter->name]['default'] = (string) $parameter->getDefaultValue();
175
        }
176
177 6
        return $paramDoc;
178
    }
179
180
    /**
181
     * @return array
182
     */
183 6
    private function paramType(array $paramDoc, \ReflectionParameter $parameter)
184
    {
185 6
        $type = $this->getParameterType($parameter, $paramDoc, $parameter->name);
186 6
        if (is_string($type)) {
187 3
            $paramDoc[$parameter->name]['type'] = $type;
188
        }
189
190 6
        return $paramDoc;
191
    }
192
193
    /**
194
     * @param \ReflectionParameter $parameter
195
     *
196
     * @return string
197
     */
198 1
    private function getType(\ReflectionParameter $parameter)
199
    {
200 1
        $type = (string) $parameter->getType();
201 1
        if ($type === 'int') {
202 1
            $type = 'integer';
203
        }
204
205 1
        return $type;
206
    }
207
208
    /**
209
     * @return array
210
     */
211 6
    private function docBlogTags(array $tags, array $params)
212
    {
213 6
        foreach ($tags as $tag) {
214
            /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Param */
215 3
            $varName = $tag->getVariableName();
216 3
            $tagType = (string) $tag->getType();
217 3
            $type = $tagType === 'int' ? 'integer' : $tagType;
218 3
            $params[$varName] = [
219 3
                'type' => $type
220
            ];
221 3
            $description = (string) $tag->getDescription();
222 3
            if ($description) {
223 3
                $params[$varName]['description'] = $description;
224
            }
225
        }
226
227 6
        return $params;
228
    }
229
230
    /**
231
     * Ignore @ Assisted @ ResourceParam parameter
232
     *
233
     * @return array
234
     */
235 6
    private function ignoreAnnotatedPrameter(\ReflectionMethod $method, array $paramMetas)
236
    {
237 6
        $annotations = $this->reader->getMethodAnnotations($method);
238 6
        foreach ($annotations as $annotation) {
239 4
            if ($annotation instanceof ResourceParam) {
240 1
                unset($paramMetas['parameters'][$annotation->param]);
241 1
                $paramMetas['required'] = array_values(array_diff($paramMetas['required'], [$annotation->param]));
242
            }
243 4
            if ($annotation instanceof Assisted) {
244 4
                $paramMetas = $this->ignorreAssisted($paramMetas, $annotation);
245
            }
246
        }
247
248 6
        return $paramMetas;
249
    }
250
251
    /**
252
     * Ignore @ Assisted parameter
253
     *
254
     * @return array
255
     */
256 1
    private function ignorreAssisted(array $paramMetas, Assisted $annotation)
257
    {
258 1
        $paramMetas['required'] = array_values(array_diff($paramMetas['required'], $annotation->values));
259 1
        foreach ($annotation->values as $varName) {
260 1
            unset($paramMetas['parameters'][$varName]);
261
        }
262
263 1
        return $paramMetas;
264
    }
265
266 6
    private function getJsonSchema(\ReflectionMethod $method)
267
    {
268 6
        $schema = $this->reader->getMethodAnnotation($method, JsonSchema::class);
269 6
        if (! $schema instanceof JsonSchema) {
270 5
            return false;
271
        }
272 1
        $schemaFile = $this->schemaDir . '/' . $schema->schema;
273 1
        if (! file_exists($schemaFile)) {
274
            return false;
275
        }
276
277 1
        return (array) json_decode(file_get_contents($schemaFile));
278
    }
279
}
280