1
|
|
|
<?php |
2
|
|
|
namespace PhpBoot\Docgen\Swagger; |
3
|
|
|
|
4
|
|
|
use PhpBoot\Application; |
5
|
|
|
use PhpBoot\Controller\ControllerContainer; |
6
|
|
|
use PhpBoot\Controller\ExceptionRenderer; |
7
|
|
|
use PhpBoot\Controller\Route; |
8
|
|
|
use PhpBoot\Docgen\Swagger\Schemas\ArraySchemaObject; |
9
|
|
|
use PhpBoot\Docgen\Swagger\Schemas\BodyParameterObject; |
10
|
|
|
use PhpBoot\Docgen\Swagger\Schemas\OperationObject; |
11
|
|
|
use PhpBoot\Docgen\Swagger\Schemas\OtherParameterObject; |
12
|
|
|
use PhpBoot\Docgen\Swagger\Schemas\PrimitiveSchemaObject; |
13
|
|
|
use PhpBoot\Docgen\Swagger\Schemas\RefSchemaObject; |
14
|
|
|
use PhpBoot\Docgen\Swagger\Schemas\ResponseObject; |
15
|
|
|
use PhpBoot\Docgen\Swagger\Schemas\SimpleModelSchemaObject; |
16
|
|
|
use PhpBoot\Docgen\Swagger\Schemas\SwaggerObject; |
17
|
|
|
use PhpBoot\Docgen\Swagger\Schemas\TagObject; |
18
|
|
|
use PhpBoot\Entity\ArrayContainer; |
19
|
|
|
use PhpBoot\Entity\EntityContainer; |
20
|
|
|
use PhpBoot\Entity\ScalarTypeContainer; |
21
|
|
|
use PhpBoot\Entity\TypeContainerInterface; |
22
|
|
|
use PhpBoot\Metas\ParamMeta; |
23
|
|
|
use PhpBoot\Metas\ReturnMeta; |
24
|
|
|
use PhpBoot\Utils\ArrayHelper; |
25
|
|
|
use PhpBoot\Validator\Validator; |
26
|
|
|
use Symfony\Component\HttpKernel\Exception\HttpException; |
27
|
|
|
|
28
|
|
|
class Swagger extends SwaggerObject |
29
|
|
|
{ |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @param Application $app |
33
|
|
|
* @param ControllerContainer[] $controllers |
34
|
|
|
*/ |
35
|
1 |
|
public function appendControllers(Application $app, $controllers) |
36
|
|
|
{ |
37
|
1 |
|
foreach ($controllers as $controller) { |
38
|
1 |
|
$this->appendController($app, $controller); |
39
|
1 |
|
} |
40
|
1 |
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @param Application $app |
44
|
|
|
* @param ControllerContainer $controller |
45
|
|
|
*/ |
46
|
1 |
|
public function appendController(Application $app, ControllerContainer $controller) |
47
|
|
|
{ |
48
|
|
|
//tags |
49
|
1 |
|
$tag = new TagObject(); |
50
|
1 |
|
$tag->name = $controller->getSummary(); |
51
|
1 |
|
$tag->description = $controller->getDescription(); |
52
|
1 |
|
$this->tags[] = $tag; |
53
|
|
|
|
54
|
1 |
|
foreach ($controller->getRoutes() as $action => $route) { |
55
|
1 |
|
$op = new OperationObject(); |
56
|
1 |
|
$op->tags = [$controller->getSummary()]; |
57
|
1 |
|
$op->summary = $route->getSummary(); |
58
|
1 |
|
$op->description = $route->getDescription(); |
59
|
|
|
|
60
|
1 |
|
$op->parameters = $this->getParamsSchema($app, $controller, $action, $route); |
61
|
1 |
|
if($this->hasFileParam($route)){ |
62
|
1 |
|
$op->consumes = ['multipart/form-data']; |
63
|
1 |
|
} |
64
|
|
|
|
65
|
1 |
|
if ($returnSchema = $this->getReturnSchema($app, $controller, $action, $route)) { |
66
|
1 |
|
$op->responses['200'] = $returnSchema; |
67
|
1 |
|
} |
68
|
1 |
|
$op->responses += $this->getExceptionsSchema($app, $controller, $action, $route); |
69
|
1 |
|
$uri = $app->getFullUri($route->getUri()); |
70
|
1 |
|
if (!isset($this->paths[$uri])) { |
71
|
1 |
|
$this->paths[$uri] = []; |
72
|
1 |
|
} |
73
|
1 |
|
$method = strtolower($route->getMethod()); |
74
|
1 |
|
$this->paths[$uri][$method] = $op; |
75
|
1 |
|
} |
76
|
1 |
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @return string |
80
|
|
|
*/ |
81
|
|
|
public function toJson() |
82
|
|
|
{ |
83
|
|
|
$json = $this->toArray(); |
84
|
|
|
return json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* @return array |
89
|
|
|
*/ |
90
|
1 |
|
public function toArray() |
91
|
|
|
{ |
92
|
1 |
|
return self::objectToArray($this); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @param $object |
97
|
|
|
* @return array |
98
|
|
|
*/ |
99
|
1 |
|
static public function objectToArray($object) |
100
|
|
|
{ |
101
|
1 |
|
if (is_object($object)) { |
102
|
1 |
|
$object = get_object_vars($object); |
103
|
1 |
|
} |
104
|
1 |
|
$res = []; |
105
|
1 |
|
foreach ($object as $k => $v) { |
106
|
1 |
|
if ($v === null) { |
107
|
1 |
|
continue; |
108
|
|
|
} |
109
|
1 |
|
if (is_array($v) || is_object($v)) { |
110
|
1 |
|
$res[$k] = self::objectToArray($v); |
111
|
1 |
|
} else { |
112
|
1 |
|
$res[$k] = $v; |
113
|
|
|
} |
114
|
1 |
|
} |
115
|
1 |
|
return $res; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* @param Application $app |
120
|
|
|
* @param ControllerContainer $controller |
121
|
|
|
* @param $action |
122
|
|
|
* @param Route $route |
123
|
|
|
* @return array |
124
|
|
|
*/ |
125
|
1 |
|
public function getExceptionsSchema(Application $app, |
126
|
|
|
ControllerContainer $controller, |
|
|
|
|
127
|
|
|
$action, |
|
|
|
|
128
|
|
|
Route $route) |
129
|
|
|
{ |
130
|
1 |
|
$handler = $route->getExceptionHandler(); |
131
|
1 |
|
if (!$handler) { |
132
|
|
|
return []; |
133
|
|
|
} |
134
|
1 |
|
$schemas = []; |
135
|
1 |
|
foreach ($handler->getExceptions() as $exception) { |
136
|
1 |
|
list($name, $desc) = $exception; |
137
|
|
|
|
138
|
1 |
|
$ins = null; |
139
|
|
|
try{ |
140
|
1 |
|
$ins = $app->make($name); |
141
|
1 |
|
}catch (\Exception $e){ |
142
|
|
|
try{ |
143
|
|
|
$ins = new $name(""); |
144
|
|
|
}catch (\Exception $e){ |
|
|
|
|
145
|
|
|
|
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
//TODO status 重复怎么办 |
150
|
1 |
|
if ($ins instanceof HttpException) { |
151
|
1 |
|
$status = $ins->getStatusCode(); |
152
|
1 |
|
} else { |
153
|
|
|
$status = 500; |
154
|
|
|
} |
155
|
1 |
|
if (isset($schemas[$status])) { |
156
|
|
|
//$this->warnings[] = "status response $status has been used for $name, $desc"; |
157
|
|
|
$res = $schemas[$status]; |
158
|
|
|
} else { |
159
|
1 |
|
$res = new ResponseObject(); |
160
|
|
|
} |
161
|
1 |
|
$shortName = self::getShortClassName($name); |
162
|
1 |
|
$desc = "$shortName: $desc"; |
163
|
1 |
|
$res->description = self::implode("\n", [$res->description, $desc]); |
164
|
1 |
|
if($ins){ |
165
|
1 |
|
$error = $app->get(ExceptionRenderer::class)->render($ins)->getContent(); |
166
|
1 |
|
if($error){ |
167
|
|
|
$res->examples = [$shortName => $error]; |
168
|
|
|
} |
169
|
1 |
|
} |
170
|
|
|
//$res->schema = new RefSchemaObject("#/definitions/$name"); |
171
|
1 |
|
$schemas[$status] = $res; |
172
|
|
|
|
173
|
1 |
|
} |
174
|
1 |
|
return $schemas; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* @param Application $app |
179
|
|
|
* @param ControllerContainer $controller |
180
|
|
|
* @param $action |
181
|
|
|
* @param Route $route |
182
|
|
|
* @return null|ResponseObject |
183
|
|
|
*/ |
184
|
1 |
|
public function getReturnSchema(Application $app, |
185
|
|
|
ControllerContainer $controller, |
186
|
|
|
$action, |
187
|
|
|
Route $route) |
188
|
|
|
{ |
189
|
1 |
|
$response = $route->getResponseHandler(); |
190
|
1 |
|
if (!$response) { |
191
|
|
|
return null; |
192
|
|
|
} |
193
|
1 |
|
$mappings = $response->getMappings(); |
194
|
1 |
|
$output = []; |
195
|
1 |
|
$schema = new ResponseObject(); |
196
|
1 |
|
foreach ($mappings as $key => $map) { |
197
|
1 |
View Code Duplication |
if (substr($key, 0, strlen('response.')) == 'response.') { |
|
|
|
|
198
|
1 |
|
$key = substr($key, strlen('response.')); |
199
|
1 |
|
} |
200
|
1 |
|
ArrayHelper::set($output, $key, $map); |
201
|
1 |
|
} |
202
|
|
|
//TODO 支持 header、status 等 |
203
|
1 |
|
if (isset($output['content'])) { |
204
|
1 |
|
$content = $output['content']; |
205
|
1 |
|
if ($content instanceof ReturnMeta) { |
206
|
1 |
|
$schema->description = $content->description; |
207
|
1 |
|
$schema->schema = $this->getAnySchema($app, $controller, $action, $route, $content->container); |
|
|
|
|
208
|
1 |
|
} elseif (is_array($content)) { |
209
|
1 |
|
$tmpSchema = $this->makeTempSchema($app, $controller, $action, $route, $content, 'Res'); |
210
|
1 |
|
$schema->schema = $tmpSchema; |
211
|
|
|
|
212
|
1 |
|
} |
213
|
|
|
//$schema->examples = ['application/json'=>$this->makeExample($content)]; |
214
|
1 |
|
return $schema; |
215
|
|
|
} |
216
|
|
|
return null; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* @param $content |
221
|
|
|
*/ |
222
|
|
|
public function makeExample($content) |
223
|
|
|
{ |
224
|
|
|
if ($content instanceof ReturnMeta || $content instanceof ParamMeta) { |
225
|
|
|
return $this->makeExample($content->container); |
226
|
|
|
}elseif ($content instanceof TypeContainerInterface){ |
227
|
|
|
return $content->makeExample(); |
228
|
|
|
}elseif(is_array($content)) { |
229
|
|
|
$res = []; |
230
|
|
|
foreach ($content as $k => $v) { |
231
|
|
|
$res[$k] = $this->makeExample($v); |
232
|
|
|
} |
233
|
|
|
return $res; |
234
|
|
|
} |
235
|
|
|
return null; |
236
|
|
|
} |
237
|
|
|
/** |
238
|
|
|
* @param Application $app |
239
|
|
|
* @param ControllerContainer $controller |
240
|
|
|
* @param $action |
241
|
|
|
* @param Route $route |
242
|
|
|
* @param array $arr |
243
|
|
|
* @param string $suffix |
244
|
|
|
* @return RefSchemaObject |
245
|
|
|
*/ |
246
|
1 |
|
public function makeTempSchema(Application $app, |
247
|
|
|
ControllerContainer $controller, |
248
|
|
|
$action, |
249
|
|
|
Route $route, |
250
|
|
|
array $arr, $suffix) |
251
|
|
|
{ |
252
|
1 |
|
$className = self::getShortClassName($controller->getClassName()); |
253
|
1 |
|
$name = $className . ucfirst($action) . $suffix; |
254
|
|
|
|
255
|
1 |
|
$schema = new SimpleModelSchemaObject(); |
256
|
|
|
|
257
|
1 |
|
foreach ($arr as $k => $v) { |
258
|
1 |
|
if (is_array($v)) { |
259
|
1 |
|
$schema->properties[$k] = $this->makeTempSchema($app, $controller, $action, $route, $v, $suffix); |
260
|
1 |
|
} elseif ($v instanceof ReturnMeta) { |
261
|
1 |
|
$sub = $this->getAnySchema($app, $controller, $action, $route, $v->container); |
|
|
|
|
262
|
1 |
|
if($sub){ |
263
|
1 |
|
$sub->description = $v->description; |
264
|
1 |
|
} |
265
|
1 |
|
$schema->properties[$k] = $sub; |
266
|
1 |
|
} elseif ($v instanceof ParamMeta) { |
267
|
1 |
View Code Duplication |
if ($v->container instanceof ArrayContainer) { |
|
|
|
|
268
|
1 |
|
$sub = $this->getArraySchema($app, $controller, $action, $route, $v->container); |
269
|
|
|
//TODO array for validation |
270
|
1 |
|
} elseif ($v->container instanceof EntityContainer) { |
271
|
1 |
|
$sub = $this->getRefSchema($app, $controller, $action, $route, $v->container); |
272
|
|
|
//TODO array for validation |
273
|
1 |
|
} else { |
274
|
1 |
|
$sub = new PrimitiveSchemaObject(); |
275
|
1 |
|
$sub->type = self::mapType($v->type); |
276
|
1 |
|
self::mapValidation($v->validation, $sub); |
277
|
1 |
|
unset($sub->required); |
278
|
|
|
} |
279
|
1 |
|
if($sub){ |
280
|
1 |
|
$sub->description = $v->description; |
281
|
1 |
|
$sub->default = $v->default; |
282
|
1 |
|
} |
283
|
1 |
|
if (!$v->isOptional) { |
284
|
1 |
|
$schema->required[] = $k; |
285
|
1 |
|
} |
286
|
1 |
|
$schema->properties[$k] = $sub; |
287
|
1 |
|
} else { |
288
|
|
|
//TODO how to do? |
289
|
|
|
} |
290
|
1 |
|
} |
291
|
1 |
|
$unused = $name; |
292
|
1 |
|
$tempId = 0; |
293
|
1 |
|
while (isset($this->definitions[$unused])) { |
294
|
1 |
|
$unused = $name . $tempId; |
295
|
1 |
|
$tempId++; |
296
|
1 |
|
} |
297
|
1 |
|
$this->definitions[$unused] = $schema; |
298
|
1 |
|
return new RefSchemaObject("#/definitions/$unused"); |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* @param Application $app |
303
|
|
|
* @param ControllerContainer $controller |
304
|
|
|
* @param $action |
305
|
|
|
* @param Route $route |
306
|
|
|
* @param EntityContainer $container |
307
|
|
|
* @return RefSchemaObject |
308
|
|
|
*/ |
309
|
1 |
|
public function getRefSchema(Application $app, |
310
|
|
|
ControllerContainer $controller, |
311
|
|
|
$action, |
312
|
|
|
Route $route, |
313
|
|
|
EntityContainer $container) |
314
|
|
|
{ |
315
|
1 |
|
$name = $container->getClassName(); |
316
|
1 |
|
if (!isset($this->definitions[$name])) { |
317
|
1 |
|
$this->definitions[$name] = $this->getObjectSchema($app, $controller, $action, $route, $container); |
318
|
1 |
|
} |
319
|
1 |
|
return new RefSchemaObject("#/definitions/$name"); |
320
|
|
|
} |
321
|
|
|
|
322
|
1 |
|
public function getParamsSchema(Application $app, |
323
|
|
|
ControllerContainer $controller, |
324
|
|
|
$action, |
325
|
|
|
Route $route) |
326
|
|
|
{ |
327
|
1 |
|
$params = $route->getRequestHandler()->getParamMetas(); |
328
|
1 |
|
$parameters = []; |
329
|
1 |
|
$body = []; |
330
|
1 |
|
$in = 'query'; |
331
|
|
|
|
332
|
1 |
|
$bodyType = 'body'; // 当有文件上传时, 必须是formData方式 |
333
|
1 |
|
if($this->hasFileParam($route)){ |
334
|
1 |
|
$bodyType = 'formData'; |
335
|
1 |
|
} |
336
|
|
|
|
337
|
1 |
|
foreach ($params as $name => $param) { |
338
|
1 |
|
$isFile = false; |
339
|
1 |
|
if ($param->isPassedByReference) { |
340
|
1 |
|
continue; |
341
|
|
|
} |
342
|
1 |
|
if ($param->source == 'request.request') { |
343
|
|
|
$in = $bodyType; |
344
|
|
|
$name = ''; |
345
|
1 |
View Code Duplication |
} elseif (strpos($param->source, 'request.request.') === 0 |
|
|
|
|
346
|
1 |
|
|| $param->source == 'request.request' |
347
|
1 |
|
) { |
348
|
1 |
|
$in = $bodyType; |
349
|
1 |
|
$name = substr($param->source, strlen('request.request.')); |
350
|
1 |
|
} elseif (strpos($param->source, 'request.query.') === 0) { |
351
|
1 |
|
$in = 'query'; |
352
|
1 |
|
$name = substr($param->source, strlen('request.query.')); |
353
|
1 |
|
} elseif (strpos($param->source, 'request.cookies.') === 0) { |
354
|
1 |
|
$in = 'cookie'; |
355
|
1 |
|
$name = substr($param->source, strlen('request.cookies.')); |
356
|
1 |
|
} elseif (strpos($param->source, 'request.headers.') === 0) { |
357
|
1 |
|
$in = 'header'; |
358
|
1 |
|
$name = substr($param->source, strlen('request.headers.')); |
359
|
1 |
View Code Duplication |
} elseif (strpos($param->source, 'request.files.') === 0) { |
|
|
|
|
360
|
1 |
|
$isFile = true; |
361
|
1 |
|
$in = $bodyType; |
362
|
1 |
|
$name = substr($param->source, strlen('request.files.')); |
363
|
1 |
|
} elseif (strpos($param->source, 'request.') === 0) { |
364
|
1 |
|
$name = substr($param->source, strlen('request.')); |
365
|
1 |
|
if ($route->hasPathParam($param->name)) { |
366
|
1 |
|
$in = 'path'; |
367
|
1 |
|
} elseif ($route->getMethod() == 'POST' |
368
|
1 |
|
|| $route->getMethod() == 'PUT' |
369
|
1 |
|
|| $route->getMethod() == 'PATCH' |
370
|
1 |
|
) { |
371
|
1 |
|
$in = $bodyType; |
372
|
1 |
|
} else { |
373
|
1 |
|
$in = 'query'; |
374
|
|
|
} |
375
|
1 |
|
} |
376
|
1 |
|
if ($in != 'body') { |
377
|
1 |
View Code Duplication |
if ($param->container instanceof ArrayContainer) { |
|
|
|
|
378
|
1 |
|
$paramSchema = $this->getArraySchema($app, $controller, $action, $route, $param->container); |
379
|
|
|
//TODO array for validation |
380
|
1 |
|
} elseif ($param->container instanceof EntityContainer) { |
381
|
1 |
|
$paramSchema = $this->getRefSchema($app, $controller, $action, $route, $param->container); |
382
|
|
|
//TODO array for validation |
383
|
1 |
|
} else { |
384
|
1 |
|
$paramSchema = new PrimitiveSchemaObject(); |
385
|
1 |
|
if($isFile){ |
386
|
1 |
|
$paramSchema->type = 'file'; |
387
|
1 |
|
}else{ |
388
|
1 |
|
$paramSchema->type = self::mapType($param->type); |
389
|
1 |
|
self::mapValidation($param->validation, $paramSchema); |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
} |
393
|
1 |
|
$paramSchema->in = $in; |
394
|
1 |
|
$paramSchema->name = $name; |
395
|
1 |
|
$paramSchema->description = $param->description; |
396
|
1 |
|
$paramSchema->default = $param->default; |
397
|
1 |
|
$paramSchema->required = !$param->isOptional; |
398
|
1 |
|
$parameters[] = $paramSchema; |
399
|
1 |
|
} else { |
400
|
1 |
|
if (!$name) { |
401
|
|
|
$body = $param; |
402
|
|
|
} else { |
403
|
1 |
|
ArrayHelper::set($body, $name, $param); |
|
|
|
|
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
} |
407
|
1 |
|
} |
408
|
1 |
|
if ($body && $bodyType == 'body') { |
409
|
|
|
|
410
|
1 |
|
$paramSchema = new BodyParameterObject(); |
411
|
1 |
|
$paramSchema->name = 'body'; |
412
|
1 |
|
$paramSchema->in = 'body'; |
413
|
1 |
|
if (is_array($body)) { |
414
|
1 |
|
$paramSchema->schema = $this->makeTempSchema($app, $controller, $action, $route, $body, 'Req'); |
415
|
1 |
|
} else { |
416
|
|
|
$paramSchema->schema = $this->getAnySchema($app, $controller, $action, $route, $body->container); |
417
|
|
|
} |
418
|
|
|
|
419
|
1 |
|
$parameters[] = $paramSchema; |
420
|
1 |
|
} |
421
|
|
|
|
422
|
1 |
|
return $parameters; |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
/** |
426
|
|
|
* @param Application $app |
427
|
|
|
* @param ControllerContainer $controller |
428
|
|
|
* @param $action |
429
|
|
|
* @param Route $route |
430
|
|
|
* @param TypeContainerInterface $container |
431
|
|
|
* @return ArraySchemaObject|PrimitiveSchemaObject|RefSchemaObject |
432
|
|
|
*/ |
433
|
1 |
|
public function getAnySchema(Application $app, ControllerContainer $controller, $action, Route $route, $container) |
434
|
|
|
{ |
435
|
1 |
|
if ($container instanceof EntityContainer) { |
436
|
1 |
|
$schema = $this->getRefSchema($app, $controller, $action, $route, $container); |
437
|
1 |
|
} elseif ($container instanceof ArrayContainer) { |
438
|
1 |
|
$schema = $this->getArraySchema($app, $controller, $action, $route, $container); |
439
|
1 |
|
} elseif ($container instanceof ScalarTypeContainer) { |
440
|
1 |
|
$schema = new PrimitiveSchemaObject(); |
441
|
1 |
|
$schema->type = self::mapType($container->getType()); |
442
|
1 |
|
} elseif($container == null){ |
443
|
|
|
$schema = null ;//new PrimitiveSchemaObject(); |
444
|
|
|
//$schema->type = null; |
445
|
|
|
}else { |
446
|
1 |
|
$schema = new PrimitiveSchemaObject(); |
447
|
|
|
//$schema->type = 'mixed'; |
448
|
|
|
} |
449
|
1 |
|
return $schema; |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
/** |
453
|
|
|
* @param Application $app |
454
|
|
|
* @param ControllerContainer $controller |
455
|
|
|
* @param $action |
456
|
|
|
* @param Route $route |
457
|
|
|
* @param ArrayContainer $container |
458
|
|
|
* @return ArraySchemaObject |
459
|
|
|
*/ |
460
|
1 |
|
public function getArraySchema(Application $app, |
461
|
|
|
ControllerContainer $controller, |
462
|
|
|
$action, |
463
|
|
|
Route $route, |
464
|
|
|
ArrayContainer $container) |
465
|
|
|
{ |
466
|
1 |
|
$schema = new ArraySchemaObject(); |
467
|
1 |
|
$itemContainer = $container->getContainer(); |
468
|
1 |
|
if ($itemContainer instanceof EntityContainer) { |
469
|
1 |
|
$itemSchema = $this->getRefSchema($app, $controller, $action, $route, $itemContainer); |
470
|
1 |
|
} elseif ($itemContainer instanceof ArrayContainer) { |
471
|
|
|
$itemSchema = $this->getArraySchema($app, $controller, $action, $route, $itemContainer); |
472
|
1 |
|
} elseif ($itemContainer instanceof ScalarTypeContainer) { |
473
|
1 |
|
$itemSchema = new PrimitiveSchemaObject(); |
474
|
1 |
|
$itemSchema->type = self::mapType($itemContainer->getType()); |
475
|
1 |
|
} else { |
476
|
|
|
$itemSchema = new PrimitiveSchemaObject(); |
477
|
|
|
//$itemSchema->type = 'mixed'; |
478
|
|
|
} |
479
|
1 |
|
$schema->items = $itemSchema; |
480
|
1 |
|
return $schema; |
481
|
|
|
} |
482
|
|
|
|
483
|
1 |
|
public function getObjectSchema(Application $app, |
484
|
|
|
ControllerContainer $controller, |
485
|
|
|
$action, |
486
|
|
|
Route $route, |
487
|
|
|
EntityContainer $container) |
488
|
|
|
{ |
489
|
1 |
|
$schema = new SimpleModelSchemaObject(); |
490
|
1 |
|
$schema->description = self::implode("\n", [$container->getSummary(), $container->getDescription()]); |
491
|
|
|
|
492
|
1 |
|
foreach ($container->getProperties() as $property) { |
493
|
|
|
|
494
|
1 |
|
if (!$property->isOptional) { |
495
|
1 |
|
$schema->required[] = $property->name; |
496
|
1 |
|
} |
497
|
1 |
|
if ($property->container instanceof EntityContainer) { |
498
|
1 |
|
$propertySchema = $this->getRefSchema($app, $controller, $action, $route, $property->container); |
499
|
1 |
|
} elseif ($property->container instanceof ArrayContainer) { |
500
|
1 |
|
$propertySchema = $this->getArraySchema($app, $controller, $action, $route, $property->container); |
501
|
1 |
|
} else { |
502
|
1 |
|
$propertySchema = new PrimitiveSchemaObject(); |
503
|
1 |
|
$propertySchema->type = self::mapType($property->type); |
504
|
1 |
|
$propertySchema->description = self::implode("\n", [$property->summary, $property->description]); |
505
|
1 |
|
self::mapValidation($property->validation, $propertySchema); |
|
|
|
|
506
|
1 |
|
unset($propertySchema->required); |
507
|
|
|
} |
508
|
1 |
|
$schema->properties[$property->name] = $propertySchema; |
509
|
1 |
|
} |
510
|
|
|
|
511
|
1 |
|
return $schema; |
512
|
|
|
} |
513
|
|
|
|
514
|
1 |
|
public function hasFileParam(Route $route) |
515
|
|
|
{ |
516
|
1 |
|
$params = $route->getRequestHandler()->getParamMetas(); |
517
|
1 |
|
foreach ($params as $name => $param) { |
518
|
1 |
|
if(strpos($param->source, 'request.files.')===0){ |
519
|
1 |
|
return true; |
520
|
|
|
} |
521
|
1 |
|
} |
522
|
1 |
|
return false; |
523
|
|
|
} |
524
|
|
|
/** |
525
|
|
|
* @param string $v |
526
|
|
|
* @param PrimitiveSchemaObject $schemaObject |
527
|
|
|
* @return PrimitiveSchemaObject |
528
|
|
|
*/ |
529
|
1 |
|
static public function mapValidation($v, PrimitiveSchemaObject $schemaObject) |
530
|
|
|
{ |
531
|
1 |
|
if(!$v){ |
532
|
1 |
|
return $schemaObject; |
533
|
|
|
} |
534
|
1 |
|
$rules = explode('|', $v); |
535
|
1 |
|
foreach ($rules as $r) { |
536
|
1 |
|
$params = explode(':', trim($r)); |
537
|
1 |
|
$rule = $params[0]; |
538
|
1 |
|
$params = isset($params[1]) ? explode(',', $params[1]) : []; |
539
|
|
|
|
540
|
1 |
|
if ($rule == 'required') { |
541
|
|
|
$schemaObject->required = true; |
542
|
1 |
|
} elseif ($rule == 'in') { |
543
|
|
|
$schemaObject->enum = $params; |
544
|
1 |
|
} elseif ($rule == 'lengthBetween' && isset($params[0]) && isset($params[1])) { |
545
|
|
|
$schemaObject->minLength = intval($params[0]); |
546
|
|
|
$schemaObject->maxLength = intval($params[1]); |
547
|
1 |
|
} elseif ($rule == 'lengthMin'&& isset($params[0])) { |
548
|
|
|
$schemaObject->minLength = intval($params[0]); |
549
|
1 |
|
} elseif ($rule == 'lengthMax'&& isset($params[0])) { |
550
|
|
|
$schemaObject->maxLength = intval($params[0]); |
551
|
1 |
|
} elseif ($rule == 'min'&& isset($params[0])) { |
552
|
1 |
|
$schemaObject->minimum = floatval($params[0]); |
|
|
|
|
553
|
1 |
|
} elseif ($rule == 'max'&& isset($params[0])) { |
554
|
1 |
|
$schemaObject->maximum = floatval($params[0]); |
|
|
|
|
555
|
1 |
|
} elseif ($rule == 'regex'&& isset($params[0])) { |
556
|
|
|
$schemaObject->pattern = $params[0]; |
557
|
|
|
} elseif ($rule == 'optional') { |
558
|
|
|
$schemaObject->required = false; |
559
|
|
|
} |
560
|
1 |
|
} |
561
|
1 |
|
return $schemaObject; |
562
|
|
|
} |
563
|
|
|
|
564
|
|
|
/** |
565
|
|
|
* @param string $type |
566
|
|
|
* @return string |
567
|
|
|
*/ |
568
|
1 |
|
static public function mapType($type) |
569
|
|
|
{ |
570
|
|
|
//TODO 如何处理 file、mixed 类型 |
571
|
|
|
$map = [ |
572
|
1 |
|
'int' => 'integer', |
573
|
1 |
|
'bool' => 'boolean', |
574
|
1 |
|
'float' => 'number', |
575
|
1 |
|
'mixed' => null, |
576
|
1 |
|
]; |
577
|
1 |
|
if (array_key_exists($type, $map)) { |
578
|
1 |
|
return $map[$type]; |
579
|
|
|
} |
580
|
1 |
|
return $type; |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
/** |
584
|
|
|
* @param $className |
585
|
|
|
* @return string |
586
|
|
|
*/ |
587
|
1 |
|
static public function getShortClassName($className) |
588
|
|
|
{ |
589
|
1 |
|
$className = explode('\\', $className); |
590
|
1 |
|
$className = $className[count($className) - 1]; |
591
|
1 |
|
return $className; |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
static public function implode($glue , array $pieces ) |
595
|
|
|
{ |
596
|
|
|
$pieces = array_filter($pieces, function($i){return trim($i) !== '';}); |
597
|
1 |
|
return implode($glue, $pieces); |
598
|
|
|
} |
599
|
|
|
} |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.