Passed
Push — master ( 14f6bb...8d1d43 )
by Fran
07:59 queued 04:09
created

DocumentorService::extractDescription()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 4
Ratio 26.67 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 9
nc 2
nop 1
dl 4
loc 15
ccs 0
cts 9
cp 0
crap 30
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace PSFS\services;
4
5
use Propel\Runtime\Map\ColumnMap;
6
use PSFS\base\config\Config;
7
use PSFS\base\Logger;
8
use PSFS\base\Request;
9
use PSFS\base\Router;
10
use PSFS\base\Service;
11
use PSFS\base\types\helpers\GeneratorHelper;
12
use PSFS\base\types\helpers\InjectorHelper;
13
use PSFS\base\types\helpers\RouterHelper;
14
use Symfony\Component\Finder\Finder;
15
16
/**
17
 * Class DocumentorService
18
 * @package PSFS\services
19
 */
20
class DocumentorService extends Service
21
{
22
    public static $nativeMethods = [
23
        'modelList', // Api list
24
        'get', // Api get
25
        'post', // Api post
26
        'put', // Api put
27
        'delete', // Api delete
28
    ];
29
30
    const DTO_INTERFACE = '\\PSFS\\base\\dto\\Dto';
31
    const MODEL_INTERFACE = '\\Propel\\Runtime\\ActiveRecord\\ActiveRecordInterface';
32
33
    /**
34
     * @Inyectable
35
     * @var \PSFS\base\Router route
36
     */
37
    protected $route;
38
39
    /**
40
     * Method that extract all modules
41
     * @param string $requestModule
42
     * @return array
43
     */
44
    public function getModules($requestModule)
45
    {
46
        $modules = [];
47
        $domains = $this->route->getDomains();
48
        if (count($domains)) {
49
            foreach ($domains as $module => $info) {
50
                try {
51
                    $module = preg_replace('/(@|\/)/', '', $module);
52
                    if (!preg_match('/^ROOT/i', $module) && $module == $requestModule) {
53
                        $modules = [
54
                            'name' => $module,
55
                            'path' => realpath($info['template'] . DIRECTORY_SEPARATOR . '..'),
56
                        ];
57
                    }
58
                } catch (\Exception $e) {
59
                    $modules[] = $e->getMessage();
60
                }
61
            }
62
        }
63
64
        return $modules;
65
    }
66
67
    /**
68
     * Method that extract all endpoints for each module
69
     *
70
     * @param array $module
71
     *
72
     * @return array
73
     */
74
    public function extractApiEndpoints(array $module)
75
    {
76
        $module_path = $module['path'] . DIRECTORY_SEPARATOR . 'Api';
77
        $module_name = $module['name'];
78
        $endpoints = [];
79
        if (file_exists($module_path)) {
80
            $finder = new Finder();
81
            $finder->files()->in($module_path)->depth(0)->name('*.php');
82
            if (count($finder)) {
83
                /** @var \SplFileInfo $file */
84
                foreach ($finder as $file) {
85
                    $namespace = "\\{$module_name}\\Api\\" . str_replace('.php', '', $file->getFilename());
86
                    $info = $this->extractApiInfo($namespace, $module_name);
87
                    if (!empty($info)) {
88
                        $endpoints[$namespace] = $info;
89
                    }
90
                }
91
            }
92
        }
93
        return $endpoints;
94
    }
95
96
    /**
97
     * Method that extract all the endpoit information by reflection
98
     *
99
     * @param string $namespace
100
     *
101
     * @return array
102
     */
103
    public function extractApiInfo($namespace, $module)
104
    {
105
        $info = [];
106
        if (class_exists($namespace)) {
107
            $reflection = new \ReflectionClass($namespace);
108
            foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
109
                try {
110
                    $mInfo = $this->extractMethodInfo($namespace, $method, $reflection, $module);
111
                    if (NULL !== $mInfo) {
112
                        $info[] = $mInfo;
113
                    }
114
                } catch (\Exception $e) {
115
                    Logger::getInstance()->errorLog($e->getMessage());
116
                }
117
            }
118
        }
119
        return $info;
120
    }
121
122
    /**
123
     * Extract route from doc comments
124
     *
125
     * @param string $comments
126
     *
127
     * @return string
128
     */
129
    protected function extractRoute($comments = '')
130
    {
131
        $route = '';
132
        preg_match('/@route\ (.*)\n/i', $comments, $route);
133
134
        return $route[1];
135
    }
136
137
    /**
138
     * Extract api from doc comments
139
     *
140
     * @param string $comments
141
     *
142
     * @return string
143
     */
144
    protected function extractApi($comments = '')
145
    {
146
        $api = '';
147
        preg_match('/@api\ (.*)\n/i', $comments, $api);
148
149
        return $api[1];
150
    }
151
152
    /**
153
     * Extract api from doc comments
154
     *
155
     * @param string $comments
156
     *
157
     * @return boolean
158
     */
159
    protected function checkDeprecated($comments = '')
160
    {
161
        return false != preg_match('/@deprecated\n/i', $comments);
162
    }
163
164
    /**
165
     * Extract visibility from doc comments
166
     *
167
     * @param string $comments
168
     *
169
     * @return boolean
170
     */
171
    protected function extractVisibility($comments = '')
172
    {
173
        $visible = TRUE;
174
        preg_match('/@visible\ (true|false)\n/i', $comments, $visibility);
175
        if (count($visibility)) {
176
            $visible = !('false' == $visibility[1]);
177
        }
178
179
        return $visible;
180
    }
181
182
    /**
183
     * Method that extract the description for the endpoint
184
     *
185
     * @param string $comments
186
     *
187
     * @return string
188
     */
189
    protected function extractDescription($comments = '')
190
    {
191
        $description = '';
192
        $docs = explode("\n", $comments);
193
        if (count($docs)) {
194
            foreach ($docs as &$doc) {
195 View Code Duplication
                if (!preg_match('/(\*\*|\@)/i', $doc) && preg_match('/\*\ /i', $doc)) {
196
                    $doc = explode('* ', $doc);
197
                    $description = $doc[1];
198
                }
199
            }
200
        }
201
202
        return $description;
203
    }
204
205
    /**
206
     * Method that extract the type of a variable
207
     *
208
     * @param string $comments
209
     *
210
     * @return string
211
     */
212
    public static function extractVarType($comments = '')
213
    {
214
        $type = 'string';
215
        preg_match('/@var\ (.*) (.*)\n/i', $comments, $varType);
216
        if (count($varType)) {
217
            $aux = trim($varType[1]);
218
            $type = str_replace(' ', '', strlen($aux) > 0 ? $varType[1] : $varType[2]);
219
        }
220
221
        return $type;
222
    }
223
224
    /**
225
     * Method that extract the payload for the endpoint
226
     *
227
     * @param string $model
228
     * @param string $comments
229
     *
230
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string|array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
231
     */
232
    protected function extractPayload($model, $comments = '')
233
    {
234
        $payload = [];
235
        preg_match('/@payload\ (.*)\n/i', $comments, $doc);
236
        if (count($doc)) {
237
            $namespace = str_replace('{__API__}', $model, $doc[1]);
238
            $payload = $this->extractModelFields($namespace);
239
            $reflector = new \ReflectionClass($namespace);
240
            $namespace = $reflector->getShortName();
241
        } else {
242
            $namespace = $model;
243
        }
244
245
        return [$namespace, $payload];
246
    }
247
248
    /**
249
     * Extract all the properties from Dto class
250
     *
251
     * @param string $class
252
     *
253
     * @return array
254
     */
255
    protected function extractDtoProperties($class)
256
    {
257
        $properties = [];
258
        $reflector = new \ReflectionClass($class);
259
        if ($reflector->isSubclassOf(self::DTO_INTERFACE)) {
260
            $properties = array_merge($properties, InjectorHelper::extractVariables($reflector));
261
        }
262
263
        return $properties;
264
    }
265
266
    /**
267
     * Extract return class for api endpoint
268
     *
269
     * @param string $model
270
     * @param string $comments
271
     *
272
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
273
     */
274
    protected function extractReturn($model, $comments = '')
275
    {
276
        $modelDto = [];
277
        preg_match('/\@return\ (.*)\((.*)\)\n/i', $comments, $returnTypes);
278
        if (count($returnTypes)) {
279
            // Extract principal DTO information
280
            if (array_key_exists(1, $returnTypes)) {
281
                $modelDto = $this->extractDtoProperties($returnTypes[1]);
282
            }
283
            if (array_key_exists(2, $returnTypes)) {
284
                $subDtos = preg_split('/,?\ /', str_replace('{__API__}', $model, $returnTypes[2]));
285
                if (count($subDtos)) {
286
                    foreach ($subDtos as $subDto) {
287
                        $isArray = false;
288
                        list($field, $dtoName) = explode('=', $subDto);
289
                        if (false !== strpos($dtoName, '[') && false !== strpos($dtoName, ']')) {
290
                            $dtoName = str_replace(']', '', str_replace('[', '', $dtoName));
291
                            $isArray = true;
292
                        }
293
                        $dto = $this->extractModelFields($dtoName);
294
                        $modelDto[$field] = ($isArray) ? [$dto] : $dto;
295
                        $modelDto['objects'][$dtoName] = $dto;
296
                    }
297
                }
298
            }
299
        }
300
301
        return $modelDto;
302
    }
303
304
    /**
305
     * Extract all fields from a ActiveResource model
306
     *
307
     * @param string $namespace
308
     *
309
     * @return mixed
310
     */
311
    protected function extractModelFields($namespace)
312
    {
313
        $payload = [];
314
        try {
315
            $reflector = new \ReflectionClass($namespace);
316
            // Checks if reflector is a subclass of propel ActiveRecords
317
            if (NULL !== $reflector && $reflector->isSubclassOf(self::MODEL_INTERFACE)) {
318
                $tableMap = $namespace::TABLE_MAP;
319
                $tableMap = $tableMap::getTableMap();
320
                /** @var ColumnMap $field */
321
                foreach ($tableMap->getColumns() as $field) {
322
                    list($type, $format) = DocumentorService::translateSwaggerFormats($field->getType());
323
                    $info = [
324
                        "type" => $type,
325
                        "required" => $field->isNotNull(),
326
                        'format' => $format,
327
                    ];
328
                    $payload[$field->getPhpName()] = $info;
329
                }
330
            } elseif (null !== $reflector && $reflector->isSubclassOf(self::DTO_INTERFACE)) {
331
                $payload = $this->extractDtoProperties($namespace);
332
            }
333
        } catch (\Exception $e) {
334
            Logger::getInstance()->errorLog($e->getMessage());
335
        }
336
337
        return $payload;
338
    }
339
340
    /**
341
     * Method that extract all the needed info for each method in each API
342
     *
343
     * @param string $namespace
344
     * @param \ReflectionMethod $method
345
     * @param \ReflectionClass $reflection
346
     * @param string $module
347
     *
348
     * @return array
349
     */
350
    protected function extractMethodInfo($namespace, \ReflectionMethod $method, \ReflectionClass $reflection, $module)
351
    {
352
        $methodInfo = NULL;
353
        $docComments = $method->getDocComment();
354
        if (FALSE !== $docComments && preg_match('/\@route\ /i', $docComments)) {
355
            $api = self::extractApi($reflection->getDocComment());
356
            list($route, $info) = RouterHelper::extractRouteInfo($method, $api, $module);
357
            $route = explode('#|#', $route);
358
            $modelNamespace = str_replace('Api', 'Models', $namespace);
359
            if ($info['visible'] && !self::checkDeprecated($docComments)) {
360
                try {
361
                    $return = $this->extractReturn($modelNamespace, $docComments);
362
                    $url = array_pop($route);
363
                    $methodInfo = [
364
                        'url' => str_replace("/" . $module . "/api", '', $url),
365
                        'method' => $info['http'],
366
                        'description' => $info['label'],
367
                        'return' => $return,
368
                        'objects' => $return['objects'],
369
                        'class' => $reflection->getShortName(),
370
                    ];
371
                    unset($methodInfo['return']['objects']);
372
                    $this->setRequestParams($method, $methodInfo, $modelNamespace, $docComments);
373
                    $this->setQueryParams($method, $methodInfo);
374
                    $this->setRequestHeaders($reflection, $methodInfo);
375
                } catch (\Exception $e) {
376
                    jpre($e->getMessage());
377
                    Logger::getInstance()->errorLog($e->getMessage());
378
                }
379
            }
380
        }
381
382
        return $methodInfo;
383
    }
384
385
    /**
386
     * Translator from php types to swagger types
387
     * @param string $format
388
     *
389
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string[].

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
390
     */
391 1
    public static function translateSwaggerFormats($format)
392
    {
393 1
        switch (strtolower($format)) {
394 1
            case 'bool':
395 1
            case 'boolean':
396
                $swaggerType = 'boolean';
397
                $swaggerFormat = '';
398
                break;
399
            default:
400 1
            case 'string':
401 1
            case 'varchar':
402 1
                $swaggerType = 'string';
403 1
                $swaggerFormat = '';
404 1
                break;
405 1
            case 'binary':
406 1
            case 'varbinary':
407
                $swaggerType = 'string';
408
                $swaggerFormat = 'binary';
409
                break;
410 1
            case 'int':
411 1
            case 'integer':
412 1
                $swaggerType = 'integer';
413 1
                $swaggerFormat = 'int32';
414 1
                break;
415
            case 'float':
416
            case 'double':
417
                $swaggerType = 'number';
418
                $swaggerFormat = strtolower($format);
419
                break;
420
            case 'date':
421
                $swaggerType = 'string';
422
                $swaggerFormat = 'date';
423
                break;
424
            case 'datetime':
425
                $swaggerType = 'string';
426
                $swaggerFormat = 'date-time';
427
                break;
428
429
        }
430 1
        return [$swaggerType, $swaggerFormat];
431
    }
432
433
    /**
434
     * Method that parse the definitions for the api's
435
     * @param string $name
436
     * @param array $fields
437
     *
438
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array<string,string|array>>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
439
     */
440
    public static function extractSwaggerDefinition($name, array $fields)
441
    {
442
        $definition = [
443
            $name => [
444
                "type" => "object",
445
                "properties" => [],
446
            ],
447
        ];
448
        foreach ($fields as $field => $info) {
449
            list($type, $format) = self::translateSwaggerFormats($info['type']);
450
            $dto['properties'][$field] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
$dto was never initialized. Although not strictly required by PHP, it is generally a good practice to add $dto = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
451
                "type" => $type,
452
                "required" => $info['required'],
453
            ];
454
            $definition[$name]['properties'][$field] = [
455
                "type" => $type,
456
                "required" => $info['required'],
457
            ];
458
            if (strlen($format)) {
459
                $definition[$name]['properties'][$field]['format'] = $format;
460
            }
461
        }
462
        return $definition;
463
    }
464
465
    /**
466
     * @return array
467
     */
468
    private static function swaggerResponses()
469
    {
470
        $codes = [200, 400, 404, 500];
471
        $responses = [];
472
        foreach ($codes as $code) {
473
            switch ($code) {
474
                default:
475
                case 200:
476
                    $message = _('Successful response');
477
                    break;
478
                case 400:
479
                    $message = _('Client error in request');
480
                    break;
481
                case 404:
482
                    $message = _('Service not found');
483
                    break;
484
                case 500:
485
                    $message = _('Server error');
486
                    break;
487
            }
488
            $responses[$code] = [
489
                'description' => $message,
490
                'schema' => [
491
                    'type' => 'object',
492
                    'properties' => [
493
                        'success' => [
494
                            'type' => 'boolean'
495
                        ],
496
                        'data' => [
497
                            'type' => 'boolean',
498
                        ],
499
                        'total' => [
500
                            'type' => 'integer',
501
                            'format' => 'int32',
502
                        ],
503
                        'pages' => [
504
                            'type' => 'integer',
505
                            'format' => 'int32',
506
                        ]
507
                    ]
508
                ]
509
            ];
510
        }
511
        return $responses;
512
    }
513
514
    /**
515
     * Method that export
516
     * @param array $module
517
     *
518
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string|array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
519
     */
520
    public static function swaggerFormatter(array $module)
521
    {
522
        $formatted = [
523
            "swagger" => "2.0",
524
            "host" => preg_replace('/^(http|https)\:\/\/(.*)\/$/i', '$2', Router::getInstance()->getRoute('', true)),
525
            "basePath" => '/' . $module['name'] . '/api',
526
            "schemes" => [Request::getInstance()->getServer('HTTPS') == 'on' ? "https" : "http"],
527
            "info" => [
528
                "title" => _('Documentación API módulo ') . $module['name'],
529
                "version" => Config::getParam('api.version', '1.0'),
530
                "contact" => [
531
                    "name" => Config::getParam("author", "Fran López"),
532
                    "email" => Config::getParam("author_email", "[email protected]"),
533
                ]
534
            ]
535
        ];
536
        $dtos = $paths = [];
537
        $endpoints = DocumentorService::getInstance()->extractApiEndpoints($module);
538
        foreach ($endpoints as $model) {
539
            foreach ($model as $endpoint) {
540
                if (!preg_match('/^\/(admin|api)\//i', $endpoint['url']) && strlen($endpoint['url'])) {
541
                    $url = preg_replace('/\/' . $module['name'] . '\/api/i', '', $endpoint['url']);
542
                    $description = $endpoint['description'];
543
                    $method = strtolower($endpoint['method']);
544
                    $paths[$url][$method] = [
545
                        'summary' => $description,
546
                        'produces' => ['application/json'],
547
                        'consumes' => ['application/json'],
548
                        'responses' => self::swaggerResponses(),
549
                        'parameters' => [],
550
                    ];
551
                    if (array_key_exists('parameters', $endpoint)) {
552
                        foreach ($endpoint['parameters'] as $parameter => $type) {
553
                            list($type, $format) = self::translateSwaggerFormats($type);
554
                            $paths[$url][$method]['parameters'][] = [
555
                                'in' => 'path',
556
                                'required' => true,
557
                                'name' => $parameter,
558
                                'type' => $type,
559
                                'format' => $format,
560
                            ];
561
                        }
562
                    }
563 View Code Duplication
                    if (array_key_exists('query', $endpoint)) {
564
                        foreach ($endpoint['query'] as $query) {
565
                            $paths[$url][$method]['parameters'][] = $query;
566
                        }
567
                    }
568 View Code Duplication
                    if (array_key_exists('headers', $endpoint)) {
569
                        foreach ($endpoint['headers'] as $query) {
570
                            $paths[$url][$method]['parameters'][] = $query;
571
                        }
572
                    }
573
                    foreach ($endpoint['objects'] as $name => $object) {
574
                        if (class_exists($name)) {
575
                            $class = GeneratorHelper::extractClassFromNamespace($name);
576
                            if(array_key_exists('data', $endpoint['return']) && count(array_keys($object)) === count(array_keys($endpoint['return']['data']))) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 160 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
577
                                $classDefinition = [
578
                                    'type' => 'object',
579
                                    '$ref' => '#/definitions/' . $class,
580
                                ];
581
                            } else {
582
                                $classDefinition = [
583
                                    'type' => 'array',
584
                                    'items' => [
585
                                        '$ref' => '#/definitions/' . $class,
586
                                    ],
587
                                ];
588
                            }
589
590
                            $paths[$url][$method]['responses'][200]['schema']['properties']['data'] = $classDefinition;
591
                            $dtos += self::extractSwaggerDefinition($class, $object);
592
                            if (array_key_exists('payload', $endpoint)) {
593
                                $dtos[$endpoint['payload']['type']] = [
594
                                    'type' => 'object',
595
                                    'properties' => $endpoint['payload']['properties'],
596
                                ];
597
                                $paths[$url][$method]['parameters'][] = [
598
                                    'in' => 'body',
599
                                    'name' => $endpoint['payload']['type'],
600
                                    'required' => true,
601
                                    'schema' => [
602
                                        'type' => 'object',
603
                                        '$ref' => '#/definitions/' . $endpoint['payload']['type'],
604
                                    ],
605
                                ];
606
                            }
607
                        }
608
                        if (!isset($paths[$url][$method]['tags']) || !in_array($endpoint['class'], $paths[$url][$method]['tags'])) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 132 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
609
                            $paths[$url][$method]['tags'][] = $endpoint['class'];
610
                        }
611
                    }
612
                }
613
            }
614
        }
615
        $formatted['definitions'] = $dtos;
616
        $formatted['paths'] = $paths;
617
        return $formatted;
618
    }
619
620
    /**
621
     * Method that extract the Dto class for the api documentation
622
     * @param string $dto
623
     * @param boolean $isArray
624
     *
625
     * @return string
626
     */
627
    protected function extractDtoName($dto, $isArray = false)
628
    {
629
        $dto = explode('\\', $dto);
630
        $modelDto = array_pop($dto) . "Dto";
631
        if ($isArray) {
632
            $modelDto .= "List";
633
        }
634
635
        return $modelDto;
636
    }
637
638
    /**
639
     * @param \ReflectionMethod $method
640
     * @param $methodInfo
641
     */
642
    protected function setQueryParams(\ReflectionMethod $method, &$methodInfo)
643
    {
644
        if (in_array($methodInfo['method'], ['GET']) && in_array($method->getShortName(), self::$nativeMethods)) {
645
            $methodInfo['query'] = [];
646
            $methodInfo['query'][] = [
647
                "name" => "__limit",
648
                "in" => "query",
649
                "description" => _("Límite de registros a devolver, -1 para devolver todos los registros"),
650
                "required" => false,
651
                "type" => "integer",
652
            ];
653
            $methodInfo['query'][] = [
654
                "name" => "__page",
655
                "in" => "query",
656
                "description" => _("Página a devolver"),
657
                "required" => false,
658
                "type" => "integer",
659
            ];
660
            $methodInfo['query'][] = [
661
                "name" => "__fields",
662
                "in" => "query",
663
                "description" => _("Campos a devolver"),
664
                "required" => false,
665
                "type" => "array",
666
                "items" => [
667
                    "type" => "string",
668
                ]
669
            ];
670
        }
671
    }
672
    /**
673
     * @param \ReflectionClass $reflection
674
     * @param $methodInfo
675
     */
676
    protected function setRequestHeaders(\ReflectionClass $reflection, &$methodInfo)
677
    {
678
679
        $methodInfo['headers'] = [];
680
        foreach($reflection->getProperties() as $property) {
681
            $doc = $property->getDocComment();
682
            preg_match('/@header\ (.*)\n/i', $doc, $headers);
683
            if(count($headers)) {
684
                $header = [
685
                    "name" => $headers[1],
686
                    "in" => "header",
687
                    "required" => true,
688
                ];
689
690
                // Extract var type
691
                $header['type'] = $this->extractVarType($doc);
692
693
                // Extract description
694
                preg_match('/@label\ (.*)\n/i', $doc, $label);
695
                if(count($label)) {
696
                    $header['description'] = _($label[1]);
697
                }
698
699
                // Extract default value
700
                preg_match('/@default\ (.*)\n/i', $doc, $default);
701
                if(count($default)) {
702
                    $header['default'] = $default[1];
703
                }
704
                $methodInfo['headers'][] = $header;
705
            }
706
        }
707
    }
708
709
    /**
710
     * @param \ReflectionMethod $method
711
     * @param array $methodInfo
712
     * @param string $modelNamespace
713
     * @param string $docComments
714
     */
715
    protected function setRequestParams(\ReflectionMethod $method, &$methodInfo, $modelNamespace, $docComments)
716
    {
717
        if (in_array($methodInfo['method'], ['POST', 'PUT'])) {
718
            list($payloadNamespace, $payloadDto) = $this->extractPayload($modelNamespace, $docComments);
719
            if (count($payloadDto)) {
720
                $methodInfo['payload'] = [
721
                    'type' => $payloadNamespace,
722
                    'properties' => $payloadDto,
723
                ];
724
            }
725
        }
726
        if ($method->getNumberOfParameters() > 0) {
727
            $methodInfo['parameters'] = [];
728
            foreach ($method->getParameters() as $parameter) {
729
                $parameterName = $parameter->getName();
730
                $types = [];
731
                preg_match_all('/\@param\ (.*)\ \$' . $parameterName . '$/im', $docComments, $types);
732
                if (count($types) > 1) {
733
                    $methodInfo['parameters'][$parameterName] = $types[1][0];
734
                }
735
            }
736
        }
737
    }
738
}
739