Passed
Push — master ( f17d15...6b8654 )
by Fran
02:50
created

DocumentorService::extractDescription()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 2
nop 1
dl 0
loc 14
ccs 0
cts 9
cp 0
crap 30
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
namespace PSFS\services;
4
5
use Propel\Runtime\Map\ColumnMap;
6
use Propel\Runtime\Map\TableMap;
7
use PSFS\base\config\Config;
8
use PSFS\base\Logger;
9
use PSFS\base\Request;
10
use PSFS\base\Router;
11
use PSFS\base\Service;
12
use PSFS\base\types\helpers\DocumentorHelper;
13
use PSFS\base\types\helpers\I18nHelper;
14
use PSFS\base\types\helpers\InjectorHelper;
15
use PSFS\base\types\helpers\RouterHelper;
16
use Symfony\Component\Finder\Finder;
17
18
/**
19
 * Class DocumentorService
20
 * @package PSFS\services
21
 */
22
class DocumentorService extends Service
23
{
24
    public static $nativeMethods = [
25
        'modelList', // Api list
26
        'get', // Api get
27
        'post', // Api post
28
        'put', // Api put
29
        'delete', // Api delete
30
    ];
31
32
    const DTO_INTERFACE = '\\PSFS\\base\\dto\\Dto';
33
    const MODEL_INTERFACE = '\\Propel\\Runtime\\ActiveRecord\\ActiveRecordInterface';
34
35
    /**
36
     * @Injectable
37
     * @var \PSFS\base\Router route
38
     */
39
    protected $route;
40
41
42
    /**
43
     * Method that extract all modules
44
     * @param string $requestModule
45
     * @return array
46
     */
47
    public function getModules($requestModule)
48
    {
49
        $modules = [];
50
        $domains = $this->route->getDomains();
51
        if (count($domains)) {
52
            foreach ($domains as $module => $info) {
53
                try {
54
                    $module = preg_replace('/(@|\/)/', '', $module);
55
                    if ($module === $requestModule && 0 === stripos($module, 'ROOT')) {
56
                        $modules = [
57
                            'name' => $module,
58
                            'path' => dirname($info['template'] . DIRECTORY_SEPARATOR . '..'),
59
                        ];
60
                    }
61
                } catch (\Exception $e) {
62
                    $modules[] = $e->getMessage();
63
                }
64
            }
65
        }
66
67
        return $modules;
68
    }
69
70
    /**
71
     * Method that extract all endpoints for each module
72
     *
73
     * @param array $module
74
     *
75
     * @return array
76
     */
77
    public function extractApiEndpoints(array $module)
78
    {
79
        $modulePath = $module['path'] . DIRECTORY_SEPARATOR . 'Api';
80
        $moduleName = $module['name'];
81
        $endpoints = [];
82
        if (file_exists($modulePath)) {
83
            $finder = new Finder();
84
            $finder->files()->in($modulePath)->depth(0)->name('*.php');
85
            if (count($finder)) {
86
                /** @var \SplFileInfo $file */
87
                foreach ($finder as $file) {
88
                    $namespace = "\\{$moduleName}\\Api\\" . str_replace('.php', '', $file->getFilename());
89
                    $info = $this->extractApiInfo($namespace, $moduleName);
90
                    if (!empty($info)) {
91
                        $endpoints[$namespace] = $info;
92
                    }
93
                }
94
            }
95
        }
96
        return $endpoints;
97
    }
98
99
    /**
100
     * @param $namespace
101
     * @param $module
102
     * @return array
103
     * @throws \ReflectionException
104
     */
105
    public function extractApiInfo($namespace, $module)
106
    {
107
        $info = [];
108
        if (Router::exists($namespace) && !I18nHelper::checkI18Class($namespace)) {
109
            $reflection = new \ReflectionClass($namespace);
110
            $visible = InjectorHelper::checkIsVisible($reflection->getDocComment());
111
            if($visible) {
112
                foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
113
                    try {
114
                        $mInfo = $this->extractMethodInfo($namespace, $method, $reflection, $module);
115
                        if (NULL !== $mInfo) {
116
                            $info[] = $mInfo;
117
                        }
118
                    } catch (\Exception $e) {
119
                        Logger::log($e->getMessage(), LOG_ERR);
120
                    }
121
                }
122
            }
123
        }
124
        return $info;
125
    }
126
127
    /**
128
     * Extract route from doc comments
129
     *
130
     * @param string $comments
131
     *
132
     * @return string
133
     */
134
    protected function extractRoute($comments = '')
135
    {
136
        $route = '';
137
        preg_match('/@route\ (.*)\n/i', $comments, $route);
0 ignored issues
show
Bug introduced by
$route of type string is incompatible with the type array|null expected by parameter $matches of preg_match(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

137
        preg_match('/@route\ (.*)\n/i', $comments, /** @scrutinizer ignore-type */ $route);
Loading history...
138
139
        return $route[1];
140
    }
141
142
    /**
143
     * Extract api from doc comments
144
     *
145
     * @param string $comments
146
     *
147
     * @return string
148
     */
149
    protected function extractApi($comments = '')
150
    {
151
        $api = '';
152
        preg_match('/@api\ (.*)\n/i', $comments, $api);
0 ignored issues
show
Bug introduced by
$api of type string is incompatible with the type array|null expected by parameter $matches of preg_match(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

152
        preg_match('/@api\ (.*)\n/i', $comments, /** @scrutinizer ignore-type */ $api);
Loading history...
153
154
        return $api[1];
155
    }
156
157
    /**
158
     * Extract api from doc comments
159
     *
160
     * @param string $comments
161
     *
162
     * @return boolean
163
     */
164
    protected function checkDeprecated($comments = '')
165
    {
166
        return false !== preg_match('/@deprecated\n/i', $comments);
167
    }
168
169
    /**
170
     * Extract visibility from doc comments
171
     *
172
     * @param string $comments
173
     *
174
     * @return boolean
175
     */
176
    protected function extractVisibility($comments = '')
177
    {
178
        $visible = TRUE;
179
        preg_match('/@visible\ (true|false)\n/i', $comments, $visibility);
180
        if (count($visibility)) {
181
            $visible = !('false' === $visibility[1]);
182
        }
183
184
        return $visible;
185
    }
186
187
    /**
188
     * Method that extract the description for the endpoint
189
     *
190
     * @param string $comments
191
     *
192
     * @return string
193
     */
194
    protected function extractDescription($comments = '')
195
    {
196
        $description = '';
197
        $docs = explode("\n", $comments);
198
        if (count($docs)) {
199
            foreach ($docs as &$doc) {
200
                if (!preg_match('/(\*\*|\@)/', $doc) && preg_match('/\*\ /', $doc)) {
201
                    $doc = explode('* ', $doc);
202
                    $description = $doc[1];
203
                }
204
            }
205
        }
206
207
        return $description;
208
    }
209
210
    /**
211
     * Method that extract the type of a variable
212
     *
213
     * @param string $comments
214
     *
215
     * @return string
216
     */
217
    public static function extractVarType($comments = '')
218
    {
219
        $type = 'string';
220
        preg_match('/@var\ (.*) (.*)\n/i', $comments, $varType);
221
        if (count($varType)) {
222
            $aux = trim($varType[1]);
223
            $type = str_replace(' ', '', strlen($aux) > 0 ? $varType[1] : $varType[2]);
224
        }
225
226
        return $type;
227
    }
228
229
    /**
230
     * Method that extract the payload for the endpoint
231
     *
232
     * @param string $model
233
     * @param string $comments
234
     *
235
     * @return array
236
     */
237
    protected function extractPayload($model, $comments = '')
238
    {
239
        $payload = [];
240
        preg_match('/@payload\ (.*)\n/i', $comments, $doc);
241
        $isArray = false;
242
        if (count($doc)) {
243
            $namespace = str_replace('{__API__}', $model, $doc[1]);
244
            if (false !== strpos($namespace, '[') && false !== strpos($namespace, ']')) {
245
                $namespace = str_replace(']', '', str_replace('[', '', $namespace));
246
                $isArray = true;
247
            }
248
            $payload = $this->extractModelFields($namespace);
249
            $reflector = new \ReflectionClass($namespace);
250
            $shortName = $reflector->getShortName();
251
        } else {
252
            $namespace = $model;
253
            $shortName = $model;
254
        }
255
256
        return [$namespace, $shortName, $payload, $isArray];
257
    }
258
259
    /**
260
     * Extract all the properties from Dto class
261
     *
262
     * @param string $class
263
     *
264
     * @return array
265
     */
266
    protected function extractDtoProperties($class)
267
    {
268
        $properties = [];
269
        $reflector = new \ReflectionClass($class);
270
        if ($reflector->isSubclassOf(self::DTO_INTERFACE)) {
271
            $properties = array_merge($properties, InjectorHelper::extractVariables($reflector));
272
        }
273
274
        return $properties;
275
    }
276
277
    /**
278
     * Extract return class for api endpoint
279
     *
280
     * @param string $model
281
     * @param string $comments
282
     *
283
     * @return array
284
     */
285
    protected function extractReturn($model, $comments = '')
286
    {
287
        $modelDto = [];
288
        preg_match('/\@return\ (.*)\((.*)\)\n/i', $comments, $returnTypes);
289
        if (count($returnTypes)) {
290
            // Extract principal DTO information
291
            if (array_key_exists(1, $returnTypes)) {
292
                $modelDto = $this->extractDtoProperties($returnTypes[1]);
293
            }
294
            if (array_key_exists(2, $returnTypes)) {
295
                $subDtos = preg_split('/,?\ /', str_replace('{__API__}', $model, $returnTypes[2]));
296
                if (count($subDtos)) {
0 ignored issues
show
Bug introduced by
It seems like $subDtos can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

296
                if (count(/** @scrutinizer ignore-type */ $subDtos)) {
Loading history...
297
                    foreach ($subDtos as $subDto) {
298
                        list($field, $dtoName) = explode('=', $subDto);
299
                        $isArray = false;
300
                        if (false !== strpos($dtoName, '[') && false !== strpos($dtoName, ']')) {
301
                            $dtoName = str_replace(']', '', str_replace('[', '', $dtoName));
302
                            $isArray = true;
303
                        }
304
                        $dto = $this->extractModelFields($dtoName);
305
                        $modelDto[$field] = $isArray ? [$dto] : $dto;
306
                        $modelDto['objects'][$dtoName] = $dto;
307
                        $modelDto = $this->checkDtoAttributes($dto, $modelDto, $dtoName);
308
                    }
309
                }
310
            }
311
        }
312
313
        return $modelDto;
314
    }
315
316
    /**
317
     * Extract all fields from a ActiveResource model
318
     *
319
     * @param string $namespace
320
     *
321
     * @return mixed
322
     */
323
    protected function extractModelFields($namespace)
324
    {
325
        $payload = [];
326
        try {
327
            $reflector = new \ReflectionClass($namespace);
328
            // Checks if reflector is a subclass of propel ActiveRecords
329
            if (NULL !== $reflector && $reflector->isSubclassOf(self::MODEL_INTERFACE)) {
330
                $tableMap = $namespace::TABLE_MAP;
331
                $tableMap = $tableMap::getTableMap();
332
                /** @var ColumnMap $field */
333
                foreach ($tableMap->getColumns() as $field) {
334
                    list($type, $format) = DocumentorHelper::translateSwaggerFormats($field->getType());
335
                    $info = [
336
                        "type" => $type,
337
                        "required" => $field->isNotNull(),
338
                        'format' => $format,
339
                    ];
340
                    if(count($field->getValueSet())) {
341
                        $info['enum'] = array_values($field->getValueSet());
342
                    }
343
                    if(null !== $field->getDefaultValue()) {
344
                        $info['default'] = $field->getDefaultValue();
345
                    }
346
                    switch(Config::getParam('api.field.type', TableMap::TYPE_PHPNAME)) {
347
                        case 'UpperCamelCase':
348
                        case TableMap::TYPE_PHPNAME:
349
                            $payload[$field->getPhpName()] = $info;
350
                            break;
351
                        case 'camelCase':
352
                        case 'lowerCamelCase':
353
                        case TableMap::TYPE_CAMELNAME:
354
                            $payload[lcfirst($field->getPhpName())] = $info;
355
                            break;
356
                        case 'dbColumn':
357
                        case TableMap::TYPE_COLNAME:
358
                            $payload[$field->getFullyQualifiedName()] = $info;
359
                            break;
360
                        case TableMap::TYPE_FIELDNAME:
361
                            $payload[$field->getName()] = $info;
362
                            break;
363
                    }
364
                }
365
            } elseif (null !== $reflector && $reflector->isSubclassOf(self::DTO_INTERFACE)) {
366
                $payload = $this->extractDtoProperties($namespace);
367
            }
368
        } catch (\Exception $e) {
369
            Logger::log($e->getMessage(), LOG_ERR);
370
        }
371
372
        return $payload;
373
    }
374
375
    /**
376
     * Method that extract all the needed info for each method in each API
377
     *
378
     * @param string $namespace
379
     * @param \ReflectionMethod $method
380
     * @param \ReflectionClass $reflection
381
     * @param string $module
382
     *
383
     * @return array
384
     */
385
    protected function extractMethodInfo($namespace, \ReflectionMethod $method, \ReflectionClass $reflection, $module)
386
    {
387
        $methodInfo = NULL;
388
        $docComments = $method->getDocComment();
389
        if (FALSE !== $docComments && preg_match('/\@route\ /i', $docComments)) {
0 ignored issues
show
Bug introduced by
It seems like $docComments can also be of type true; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

389
        if (FALSE !== $docComments && preg_match('/\@route\ /i', /** @scrutinizer ignore-type */ $docComments)) {
Loading history...
390
            $api = $this->extractApi($reflection->getDocComment());
391
            list($route, $info) = RouterHelper::extractRouteInfo($method, $api, $module);
392
            $route = explode('#|#', $route);
393
            $modelNamespace = str_replace('Api', 'Models', $namespace);
394
            if ($info['visible'] && !$this->checkDeprecated($docComments)) {
0 ignored issues
show
Bug introduced by
It seems like $docComments can also be of type true; however, parameter $comments of PSFS\services\DocumentorService::checkDeprecated() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

394
            if ($info['visible'] && !$this->checkDeprecated(/** @scrutinizer ignore-type */ $docComments)) {
Loading history...
395
                try {
396
                    $return = $this->extractReturn($modelNamespace, $docComments);
0 ignored issues
show
Bug introduced by
It seems like $docComments can also be of type true; however, parameter $comments of PSFS\services\DocumentorService::extractReturn() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

396
                    $return = $this->extractReturn($modelNamespace, /** @scrutinizer ignore-type */ $docComments);
Loading history...
397
                    $url = array_pop($route);
398
                    $methodInfo = [
399
                        'url' => str_replace('/' . $module . '/api', '', $url),
400
                        'method' => $info['http'],
401
                        'description' => $info['label'],
402
                        'return' => $return,
403
                        'objects' => array_key_exists('objects', $return) ? $return['objects'] : [],
404
                        'class' => $reflection->getShortName(),
405
                    ];
406
                    unset($methodInfo['return']['objects']);
407
                    $this->setRequestParams($method, $methodInfo, $modelNamespace, $docComments);
0 ignored issues
show
Bug introduced by
It seems like $docComments can also be of type true; however, parameter $docComments of PSFS\services\Documentor...ice::setRequestParams() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

407
                    $this->setRequestParams($method, $methodInfo, $modelNamespace, /** @scrutinizer ignore-type */ $docComments);
Loading history...
408
                    $this->setQueryParams($method, $methodInfo);
409
                    $this->setRequestHeaders($reflection, $methodInfo);
410
                } catch (\Exception $e) {
411
                    Logger::log($e->getMessage(), LOG_ERR);
412
                }
413
            }
414
        }
415
416
        return $methodInfo;
417
    }
418
419
    /**
420
     * @return array
421
     */
422
    private static function swaggerResponses()
423
    {
424
        $codes = [200, 400, 404, 500];
425
        $responses = [];
426
        foreach ($codes as $code) {
427
            switch ($code) {
428
                default:
429
                case 200:
430
                    $message = t('Successful response');
431
                    break;
432
                case 400:
433
                    $message = t('Client error in request');
434
                    break;
435
                case 404:
436
                    $message = t('Service not found');
437
                    break;
438
                case 500:
439
                    $message = t('Server error');
440
                    break;
441
            }
442
            $responses[$code] = [
443
                'description' => $message,
444
                'schema' => [
445
                    'type' => 'object',
446
                    'properties' => [
447
                        'success' => [
448
                            'type' => 'boolean'
449
                        ],
450
                        'data' => [
451
                            'type' => 'boolean',
452
                        ],
453
                        'total' => [
454
                            'type' => 'integer',
455
                            'format' => 'int32',
456
                        ],
457
                        'pages' => [
458
                            'type' => 'integer',
459
                            'format' => 'int32',
460
                        ]
461
                    ]
462
                ]
463
            ];
464
        }
465
        return $responses;
466
    }
467
468
    /**
469
     * Method that export
470
     * @param array $module
471
     *
472
     * @return array
473
     */
474
    public static function swaggerFormatter(array $module)
475
    {
476
        $formatted = [
477
            "swagger" => "2.0",
478
            "host" => preg_replace('/^(http|https)\:\/\/(.*)\/$/i', '$2', Router::getInstance()->getRoute('', true)),
479
            "basePath" => '/' . $module['name'] . '/api',
480
            "schemes" => [Request::getInstance()->getServer('HTTPS') === 'on' ? 'https' : 'http'],
481
            "info" => [
482
                "title" => t('Documentación API módulo ') . $module['name'],
483
                "version" => Config::getParam('api.version', '1.0.0'),
484
                "contact" => [
485
                    "name" => Config::getParam("author", "Fran López"),
486
                    "email" => Config::getParam("author.email", "[email protected]"),
487
                ]
488
            ]
489
        ];
490
        $dtos = $paths = [];
491
        $endpoints = DocumentorService::getInstance()->extractApiEndpoints($module);
492
        foreach ($endpoints as $model) {
493
            foreach ($model as $endpoint) {
494
                if (!preg_match('/^\/(admin|api)\//i', $endpoint['url']) && strlen($endpoint['url'])) {
495
                    $url = preg_replace('/\/' . $module['name'] . '\/api/i', '', $endpoint['url']);
496
                    $description = $endpoint['description'];
497
                    $method = strtolower($endpoint['method']);
498
                    $paths[$url][$method] = [
499
                        'summary' => $description,
500
                        'produces' => ['application/json'],
501
                        'consumes' => ['application/json'],
502
                        'responses' => self::swaggerResponses(),
503
                        'parameters' => [],
504
                    ];
505
                    if (array_key_exists('parameters', $endpoint)) {
506
                        foreach ($endpoint['parameters'] as $parameter => $type) {
507
                            list($type, $format) = DocumentorHelper::translateSwaggerFormats($type);
508
                            $paths[$url][$method]['parameters'][] = [
509
                                'in' => 'path',
510
                                'required' => true,
511
                                'name' => $parameter,
512
                                'type' => $type,
513
                                'format' => $format,
514
                            ];
515
                        }
516
                    }
517
                    if (array_key_exists('query', $endpoint)) {
518
                        foreach ($endpoint['query'] as $query) {
519
                            $paths[$url][$method]['parameters'][] = $query;
520
                        }
521
                    }
522
                    if (array_key_exists('headers', $endpoint)) {
523
                        foreach ($endpoint['headers'] as $query) {
524
                            $paths[$url][$method]['parameters'][] = $query;
525
                        }
526
                    }
527
                    $isReturn = true;
528
                    foreach ($endpoint['objects'] as $name => $object) {
529
                        DocumentorHelper::parseObjects($paths, $dtos, $name, $endpoint, $object, $url, $method, $isReturn);
530
                        $isReturn = false;
531
                    }
532
                }
533
            }
534
        }
535
        ksort($dtos);
536
        uasort($paths, function($path1, $path2) {
537
            $key1 = array_keys($path1)[0];
538
            $key2 = array_keys($path2)[0];
539
            return strcmp($path1[$key1]['tags'][0], $path2[$key2]['tags'][0]);
540
        });
541
        $formatted['definitions'] = $dtos;
542
        $formatted['paths'] = $paths;
543
        return $formatted;
544
    }
545
546
    /**
547
     * Method that extract the Dto class for the api documentation
548
     * @param string $dto
549
     * @param boolean $isArray
550
     *
551
     * @return string
552
     */
553
    protected function extractDtoName($dto, $isArray = false)
554
    {
555
        $dto = explode('\\', $dto);
556
        $modelDto = array_pop($dto) . "Dto";
557
        if ($isArray) {
558
            $modelDto .= "List";
559
        }
560
561
        return $modelDto;
562
    }
563
564
    /**
565
     * @param \ReflectionMethod $method
566
     * @param $methodInfo
567
     */
568
    protected function setQueryParams(\ReflectionMethod $method, &$methodInfo)
569
    {
570
        if (in_array($methodInfo['method'], ['GET']) && in_array($method->getShortName(), self::$nativeMethods)) {
571
            $methodInfo['query'] = [];
572
            $methodInfo['query'][] = [
573
                "name" => "__limit",
574
                "in" => "query",
575
                "description" => t("Límite de registros a devolver, -1 para devolver todos los registros"),
576
                "required" => false,
577
                "type" => "integer",
578
            ];
579
            $methodInfo['query'][] = [
580
                "name" => "__page",
581
                "in" => "query",
582
                "description" => t("Página a devolver"),
583
                "required" => false,
584
                "type" => "integer",
585
            ];
586
            $methodInfo['query'][] = [
587
                "name" => "__fields",
588
                "in" => "query",
589
                "description" => t("Campos a devolver"),
590
                "required" => false,
591
                "type" => "array",
592
                "items" => [
593
                    "type" => "string",
594
                ]
595
            ];
596
        }
597
    }
598
    /**
599
     * @param \ReflectionClass $reflection
600
     * @param $methodInfo
601
     */
602
    protected function setRequestHeaders(\ReflectionClass $reflection, &$methodInfo)
603
    {
604
605
        $methodInfo['headers'] = [];
606
        foreach($reflection->getProperties() as $property) {
607
            $doc = $property->getDocComment();
608
            preg_match('/@header\ (.*)\n/i', $doc, $headers);
609
            if(count($headers)) {
610
                $header = [
611
                    "name" => $headers[1],
612
                    "in" => "header",
613
                    "required" => true,
614
                ];
615
616
                // Extract var type
617
                $header['type'] = $this->extractVarType($doc);
618
619
                // Extract description
620
                $header['description'] = InjectorHelper::getLabel($doc);
621
622
                // Extract default value
623
                $header['default'] = InjectorHelper::getDefaultValue($doc);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $header['default'] is correct as PSFS\base\types\helpers\...::getDefaultValue($doc) targeting PSFS\base\types\helpers\...lper::getDefaultValue() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
624
625
                $methodInfo['headers'][] = $header;
626
            }
627
        }
628
    }
629
630
    /**
631
     * @param \ReflectionMethod $method
632
     * @param array $methodInfo
633
     * @param string $modelNamespace
634
     * @param string $docComments
635
     */
636
    protected function setRequestParams(\ReflectionMethod $method, &$methodInfo, $modelNamespace, $docComments)
637
    {
638
        if (in_array($methodInfo['method'], ['POST', 'PUT'])) {
639
            list($payloadNamespace, $payloadNamespaceShortName, $payloadDto, $isArray) = $this->extractPayload($modelNamespace, $docComments);
640
            if (count($payloadDto)) {
641
                $methodInfo['payload'] = [
642
                    'type' => $payloadNamespaceShortName,
643
                    'properties' => $payloadDto,
644
                    'is_array' => $isArray,
645
                ];
646
                $methodInfo = $this->checkDtoAttributes($payloadDto, $methodInfo, $payloadNamespace);
647
            }
648
        }
649
        if ($method->getNumberOfParameters() > 0) {
650
            $methodInfo['parameters'] = [];
651
            foreach ($method->getParameters() as $parameter) {
652
                $parameterName = $parameter->getName();
653
                $types = [];
654
                preg_match_all('/\@param\ (.*)\ \$' . $parameterName . '$/im', $docComments, $types);
655
                if (count($types) > 1 && count($types[1]) > 0) {
656
                    $methodInfo['parameters'][$parameterName] = $types[1][0];
657
                }
658
            }
659
        }
660
    }
661
662
    /**
663
     * @param $dto
664
     * @param $modelDto
665
     * @param $dtoName
666
     * @return array
667
     */
668
    protected function checkDtoAttributes($dto, $modelDto, $dtoName)
669
    {
670
        foreach ($dto as $param => &$info) {
671
            if (array_key_exists('class', $info)) {
672
                if ($info['is_array']) {
673
                    $modelDto['objects'][$dtoName][$param] = [
674
                        'type' => 'array',
675
                        'items' => [
676
                            '$ref' => '#/definitions/' . $info['type'],
677
                        ]
678
                    ];
679
                } else {
680
                    $modelDto['objects'][$dtoName][$param] = [
681
                        'type' => 'object',
682
                        '$ref' => '#/definitions/' . $info['type'],
683
                    ];
684
                }
685
                $modelDto['objects'][$info['class']] = $info['properties'];
686
                $paramDto = $this->checkDtoAttributes($info['properties'], $info['properties'], $info['class']);
687
                if(array_key_exists('objects', $paramDto)) {
688
                    $modelDto['objects'] = array_merge($modelDto['objects'], $paramDto['objects']);
689
                }
690
            } else {
691
                $modelDto['objects'][$dtoName][$param] = $info;
692
            }
693
        }
694
        return $modelDto;
695
    }
696
}
697