Passed
Push — master ( 5318ba...a2ab8e )
by Fran
03:30
created

DocumentorService::setRequestHeaders()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 35
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
eloc 20
nc 10
nop 2
dl 0
loc 35
ccs 0
cts 19
cp 0
crap 42
rs 8.439
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\Api;
12
use PSFS\base\types\helpers\GeneratorHelper;
13
use PSFS\base\types\helpers\InjectorHelper;
14
use PSFS\base\types\helpers\RouterHelper;
15
use Symfony\Component\Finder\Finder;
16
17
/**
18
 * Class DocumentorService
19
 * @package PSFS\services
20
 */
21
class DocumentorService extends Service
22
{
23
    public static $nativeMethods = [
24
        'modelList', // Api list
25
        'get', // Api get
26
        'post', // Api post
27
        'put', // Api put
28
        'delete', // Api delete
29
    ];
30
31
    const DTO_INTERFACE = '\\PSFS\\base\\dto\\Dto';
32
    const MODEL_INTERFACE = '\\Propel\\Runtime\\ActiveRecord\\ActiveRecordInterface';
33
34
    /**
35
     * @Inyectable
36
     * @var \PSFS\base\Router route
37
     */
38
    protected $route;
39
40
    /**
41
     * Method that extract all modules
42
     * @param string $requestModule
43
     * @return array
44
     */
45
    public function getModules($requestModule)
46
    {
47
        $modules = [];
48
        $domains = $this->route->getDomains();
49
        if (count($domains)) {
50
            foreach ($domains as $module => $info) {
51
                try {
52
                    $module = preg_replace('/(@|\/)/', '', $module);
53
                    if (!preg_match('/^ROOT/i', $module) && $module == $requestModule) {
54
                        $modules = [
55
                            'name' => $module,
56
                            'path' => realpath($info['template'] . DIRECTORY_SEPARATOR . '..'),
57
                        ];
58
                    }
59
                } catch (\Exception $e) {
60
                    $modules[] = $e->getMessage();
61
                }
62
            }
63
        }
64
65
        return $modules;
66
    }
67
68
    /**
69
     * Method that extract all endpoints for each module
70
     *
71
     * @param array $module
72
     *
73
     * @return array
74
     */
75
    public function extractApiEndpoints(array $module)
76
    {
77
        $module_path = $module['path'] . DIRECTORY_SEPARATOR . 'Api';
78
        $module_name = $module['name'];
79
        $endpoints = [];
80
        if (file_exists($module_path)) {
81
            $finder = new Finder();
82
            $finder->files()->in($module_path)->depth(0)->name('*.php');
83
            if (count($finder)) {
84
                /** @var \SplFileInfo $file */
85
                foreach ($finder as $file) {
86
                    $namespace = "\\{$module_name}\\Api\\" . str_replace('.php', '', $file->getFilename());
87
                    $info = $this->extractApiInfo($namespace, $module_name);
88
                    if (!empty($info)) {
89
                        $endpoints[$namespace] = $info;
90
                    }
91
                }
92
            }
93
        }
94
        return $endpoints;
95
    }
96
97
    /**
98
     * Method that extract all the endpoit information by reflection
99
     *
100
     * @param string $namespace
101
     *
102
     * @return array
103
     */
104
    public function extractApiInfo($namespace, $module)
105
    {
106
        $info = [];
107
        if (class_exists($namespace)) {
108
            $reflection = new \ReflectionClass($namespace);
109
            foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
110
                try {
111
                    $mInfo = $this->extractMethodInfo($namespace, $method, $reflection, $module);
112
                    if (NULL !== $mInfo) {
113
                        $info[] = $mInfo;
114
                    }
115
                } catch (\Exception $e) {
116
                    Logger::getInstance()->errorLog($e->getMessage());
117
                }
118
            }
119
        }
120
        return $info;
121
    }
122
123
    /**
124
     * Extract route from doc comments
125
     *
126
     * @param string $comments
127
     *
128
     * @return string
129
     */
130
    protected function extractRoute($comments = '')
131
    {
132
        $route = '';
133
        preg_match('/@route\ (.*)\n/i', $comments, $route);
134
135
        return $route[1];
136
    }
137
138
    /**
139
     * Extract api from doc comments
140
     *
141
     * @param string $comments
142
     *
143
     * @return string
144
     */
145
    protected function extractApi($comments = '')
146
    {
147
        $api = '';
148
        preg_match('/@api\ (.*)\n/i', $comments, $api);
149
150
        return $api[1];
151
    }
152
153
    /**
154
     * Extract api from doc comments
155
     *
156
     * @param string $comments
157
     *
158
     * @return boolean
159
     */
160
    protected function checkDeprecated($comments = '')
161
    {
162
        return false != preg_match('/@deprecated\n/i', $comments);
163
    }
164
165
    /**
166
     * Extract visibility from doc comments
167
     *
168
     * @param string $comments
169
     *
170
     * @return boolean
171
     */
172
    protected function extractVisibility($comments = '')
173
    {
174
        $visible = TRUE;
175
        preg_match('/@visible\ (true|false)\n/i', $comments, $visibility);
176
        if (count($visibility)) {
177
            $visible = !('false' == $visibility[1]);
178
        }
179
180
        return $visible;
181
    }
182
183
    /**
184
     * Method that extract the description for the endpoint
185
     *
186
     * @param string $comments
187
     *
188
     * @return string
189
     */
190
    protected function extractDescription($comments = '')
191
    {
192
        $description = '';
193
        $docs = explode("\n", $comments);
194
        if (count($docs)) {
195
            foreach ($docs as &$doc) {
196 View Code Duplication
                if (!preg_match('/(\*\*|\@)/i', $doc) && preg_match('/\*\ /i', $doc)) {
197
                    $doc = explode('* ', $doc);
198
                    $description = $doc[1];
199
                }
200
            }
201
        }
202
203
        return $description;
204
    }
205
206
    /**
207
     * Method that extract the type of a variable
208
     *
209
     * @param string $comments
210
     *
211
     * @return string
212
     */
213
    public static function extractVarType($comments = '')
214
    {
215
        $type = 'string';
216
        preg_match('/@var\ (.*) (.*)\n/i', $comments, $varType);
217
        if (count($varType)) {
218
            $aux = trim($varType[1]);
219
            $type = str_replace(' ', '', strlen($aux) > 0 ? $varType[1] : $varType[2]);
220
        }
221
222
        return $type;
223
    }
224
225
    /**
226
     * Method that extract the payload for the endpoint
227
     *
228
     * @param string $model
229
     * @param string $comments
230
     *
231
     * @return array
232
     */
233
    protected function extractPayload($model, $comments = '')
234
    {
235
        $payload = [];
236
        preg_match('/@payload\ (.*)\n/i', $comments, $doc);
237
        if (count($doc)) {
238
            $namespace = str_replace('{__API__}', $model, $doc[1]);
239
            $payload = $this->extractModelFields($namespace);
240
        }
241
242
        return $payload;
243
    }
244
245
    /**
246
     * Extract all the properties from Dto class
247
     *
248
     * @param string $class
249
     *
250
     * @return array
251
     */
252
    protected function extractDtoProperties($class)
253
    {
254
        $properties = [];
255
        $reflector = new \ReflectionClass($class);
256
        if ($reflector->isSubclassOf(self::DTO_INTERFACE)) {
257
            $properties = array_merge($properties, InjectorHelper::extractVariables($reflector));
258
        }
259
260
        return $properties;
261
    }
262
263
    /**
264
     * Extract return class for api endpoint
265
     *
266
     * @param string $model
267
     * @param string $comments
268
     *
269
     * @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...
270
     */
271
    protected function extractReturn($model, $comments = '')
272
    {
273
        $modelDto = [];
274
        preg_match('/\@return\ (.*)\((.*)\)\n/i', $comments, $returnTypes);
275
        if (count($returnTypes)) {
276
            // Extract principal DTO information
277
            if (array_key_exists(1, $returnTypes)) {
278
                $modelDto = $this->extractDtoProperties($returnTypes[1]);
279
            }
280
            if (array_key_exists(2, $returnTypes)) {
281
                $subDtos = preg_split('/,?\ /', str_replace('{__API__}', $model, $returnTypes[2]));
282
                if (count($subDtos)) {
283
                    foreach ($subDtos as $subDto) {
284
                        $isArray = false;
285
                        list($field, $dtoName) = explode('=', $subDto);
286
                        if (false !== strpos($dtoName, '[') && false !== strpos($dtoName, ']')) {
287
                            $dtoName = str_replace(']', '', str_replace('[', '', $dtoName));
288
                            $isArray = true;
289
                        }
290
                        $dto = $this->extractModelFields($dtoName);
291
                        $modelDto[$field] = ($isArray) ? [$dto] : $dto;
292
                        $modelDto['objects'][$dtoName] = $dto;
293
                    }
294
                }
295
            }
296
        }
297
298
        return $modelDto;
299
    }
300
301
    /**
302
     * Extract all fields from a ActiveResource model
303
     *
304
     * @param string $namespace
305
     *
306
     * @return mixed
307
     */
308
    protected function extractModelFields($namespace)
309
    {
310
        $payload = [];
311
        try {
312
            $reflector = new \ReflectionClass($namespace);
313
            // Checks if reflector is a subclass of propel ActiveRecords
314
            if (NULL !== $reflector && $reflector->isSubclassOf(self::MODEL_INTERFACE)) {
315
                $tableMap = $namespace::TABLE_MAP;
316
                $tableMap = $tableMap::getTableMap();
317
                /** @var ColumnMap $field */
318
                foreach ($tableMap->getColumns() as $field) {
319
                    $info = [
320
                        "type" => $field->getType(),
321
                        "required" => $field->isNotNull(),
322
                    ];
323
                    $payload[$field->getPhpName()] = $info;
324
                }
325
            } elseif (null !== $reflector && $reflector->isSubclassOf(self::DTO_INTERFACE)) {
326
                $payload = $this->extractDtoProperties($namespace);
327
            }
328
        } catch (\Exception $e) {
329
            Logger::getInstance()->errorLog($e->getMessage());
330
        }
331
332
        return $payload;
333
    }
334
335
    /**
336
     * Method that extract all the needed info for each method in each API
337
     *
338
     * @param string $namespace
339
     * @param \ReflectionMethod $method
340
     * @param \ReflectionClass $reflection
341
     * @param string $module
342
     *
343
     * @return array
344
     */
345
    protected function extractMethodInfo($namespace, \ReflectionMethod $method, \ReflectionClass $reflection, $module)
346
    {
347
        $methodInfo = NULL;
348
        $docComments = $method->getDocComment();
349
        if (FALSE !== $docComments && preg_match('/\@route\ /i', $docComments)) {
350
            $api = self::extractApi($reflection->getDocComment());
351
            list($route, $info) = RouterHelper::extractRouteInfo($method, $api, $module);
352
            $route = explode('#|#', $route);
353
            $modelNamespace = str_replace('Api', 'Models', $namespace);
354
            if ($info['visible'] && !self::checkDeprecated($docComments)) {
355
                try {
356
                    $return = $this->extractReturn($modelNamespace, $docComments);
357
                    $url = array_pop($route);
358
                    $methodInfo = [
359
                        'url' => str_replace("/" . $module . "/api", '', $url),
360
                        'method' => $info['http'],
361
                        'description' => $info['label'],
362
                        'return' => $return,
363
                        'objects' => $return['objects'],
364
                        'class' => $reflection->getShortName(),
365
                    ];
366
                    unset($methodInfo['return']['objects']);
367
                    $this->setRequestParams($method, $methodInfo, $modelNamespace, $docComments);
368
                    $this->setQueryParams($method, $methodInfo);
369
                    $this->setRequestHeaders($reflection, $methodInfo);
370
                } catch (\Exception $e) {
371
                    jpre($e->getMessage());
372
                    Logger::getInstance()->errorLog($e->getMessage());
373
                }
374
            }
375
        }
376
377
        return $methodInfo;
378
    }
379
380
    /**
381
     * Translator from php types to swagger types
382
     * @param string $format
383
     *
384
     * @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...
385
     */
386
    public static function translateSwaggerFormats($format)
387
    {
388
        switch (strtolower($format)) {
389
            case 'bool':
390
            case 'boolean':
391
                $swaggerType = 'boolean';
392
                $swaggerFormat = '';
393
                break;
394
            default:
395
            case 'string':
396
            case 'varchar':
397
                $swaggerType = 'string';
398
                $swaggerFormat = '';
399
                break;
400
            case 'binary':
401
            case 'varbinary':
402
                $swaggerType = 'string';
403
                $swaggerFormat = 'binary';
404
                break;
405
            case 'int':
406
            case 'integer':
407
                $swaggerType = 'integer';
408
                $swaggerFormat = 'int32';
409
                break;
410
            case 'float':
411
            case 'double':
412
                $swaggerType = 'number';
413
                $swaggerFormat = strtolower($format);
414
                break;
415
            case 'date':
416
                $swaggerType = 'string';
417
                $swaggerFormat = 'date';
418
                break;
419
            case 'datetime':
420
                $swaggerType = 'string';
421
                $swaggerFormat = 'date-time';
422
                break;
423
424
        }
425
        return [$swaggerType, $swaggerFormat];
426
    }
427
428
    /**
429
     * Method that parse the definitions for the api's
430
     * @param string $name
431
     * @param array $fields
432
     *
433
     * @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...
434
     */
435
    public static function extractSwaggerDefinition($name, array $fields)
436
    {
437
        $definition = [
438
            $name => [
439
                "type" => "object",
440
                "properties" => [],
441
            ],
442
        ];
443
        foreach ($fields as $field => $info) {
444
            list($type, $format) = self::translateSwaggerFormats($info['type']);
445
            $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...
446
                "type" => $type,
447
                "required" => $info['required'],
448
            ];
449
            $definition[$name]['properties'][$field] = [
450
                "type" => $type,
451
                "required" => $info['required'],
452
            ];
453
            if (strlen($format)) {
454
                $definition[$name]['properties'][$field]['format'] = $format;
455
            }
456
        }
457
        return $definition;
458
    }
459
460
    /**
461
     * @return array
462
     */
463
    private static function swaggerResponses()
464
    {
465
        $codes = [200, 400, 404, 500];
466
        $responses = [];
467
        foreach ($codes as $code) {
468
            switch ($code) {
469
                default:
470
                case 200:
471
                    $message = _('Successful response');
472
                    break;
473
                case 400:
474
                    $message = _('Client error in request');
475
                    break;
476
                case 404:
477
                    $message = _('Service not found');
478
                    break;
479
                case 500:
480
                    $message = _('Server error');
481
                    break;
482
            }
483
            $responses[$code] = [
484
                'description' => $message,
485
                'schema' => [
486
                    'type' => 'object',
487
                    'properties' => [
488
                        'success' => [
489
                            'type' => 'boolean'
490
                        ],
491
                        'data' => [
492
                            'type' => 'boolean',
493
                        ],
494
                        'total' => [
495
                            'type' => 'integer',
496
                            'format' => 'int32',
497
                        ],
498
                        'pages' => [
499
                            'type' => 'integer',
500
                            'format' => 'int32',
501
                        ]
502
                    ]
503
                ]
504
            ];
505
        }
506
        return $responses;
507
    }
508
509
    /**
510
     * Method that export
511
     * @param array $module
512
     *
513
     * @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...
514
     */
515
    public static function swaggerFormatter(array $module)
516
    {
517
        $formatted = [
518
            "swagger" => "2.0",
519
            "host" => preg_replace('/^(http|https)\:\/\/(.*)\/$/i', '$2', Router::getInstance()->getRoute('', true)),
520
            "basePath" => '/' . $module['name'] . '/api',
521
            "schemes" => [Request::getInstance()->getServer('HTTPS') == 'on' ? "https" : "http"],
522
            "info" => [
523
                "title" => _('Documentación API módulo ') . $module['name'],
524
                "version" => Config::getParam('api.version', '1.0'),
525
                "contact" => [
526
                    "name" => Config::getParam("author", "Fran López"),
527
                    "email" => Config::getParam("author_email", "[email protected]"),
528
                ]
529
            ]
530
        ];
531
        $dtos = $paths = [];
532
        $endpoints = DocumentorService::getInstance()->extractApiEndpoints($module);
533
        foreach ($endpoints as $model) {
534
            foreach ($model as $endpoint) {
535
                if (!preg_match('/^\/(admin|api)\//i', $endpoint['url']) && strlen($endpoint['url'])) {
536
                    $url = preg_replace('/\/' . $module['name'] . '\/api/i', '', $endpoint['url']);
537
                    $description = $endpoint['description'];
538
                    $method = strtolower($endpoint['method']);
539
                    $paths[$url][$method] = [
540
                        'summary' => $description,
541
                        'produces' => ['application/json'],
542
                        'consumes' => ['application/json'],
543
                        'responses' => self::swaggerResponses(),
544
                        'parameters' => [],
545
                    ];
546
                    if (array_key_exists('parameters', $endpoint)) {
547
                        foreach ($endpoint['parameters'] as $parameter => $type) {
548
                            list($type, $format) = self::translateSwaggerFormats($type);
549
                            $paths[$url][$method]['parameters'][] = [
550
                                'in' => 'path',
551
                                'required' => true,
552
                                'name' => $parameter,
553
                                'type' => $type,
554
                                'format' => $format,
555
                            ];
556
                        }
557
                    }
558 View Code Duplication
                    if (array_key_exists('query', $endpoint)) {
559
                        foreach ($endpoint['query'] as $query) {
560
                            $paths[$url][$method]['parameters'][] = $query;
561
                        }
562
                    }
563 View Code Duplication
                    if (array_key_exists('headers', $endpoint)) {
564
                        foreach ($endpoint['headers'] as $query) {
565
                            $paths[$url][$method]['parameters'][] = $query;
566
                        }
567
                    }
568
                    foreach ($endpoint['objects'] as $name => $object) {
569
                        if (class_exists($name)) {
570
                            $class = GeneratorHelper::extractClassFromNamespace($name);
571
                            $classDefinition = [
572
                                'type' => 'object',
573
                                '$ref' => '#/definitions/' . $class,
574
                            ];
575
                            $paths[$url][$method]['responses'][200]['schema']['properties']['data'] = $classDefinition;
576
                            $dtos += self::extractSwaggerDefinition($class, $object);
577
                            if (array_key_exists('payload', $endpoint)) {
578
                                $paths[$url][$method]['parameters'][] = [
579
                                    'in' => 'body',
580
                                    'name' => $class,
581
                                    'required' => true,
582
                                    'schema' => $classDefinition,
583
                                ];
584
                            }
585
                        }
586
                        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...
587
                            $paths[$url][$method]['tags'][] = $endpoint['class'];
588
                        }
589
                    }
590
                }
591
            }
592
        }
593
        $formatted['definitions'] = $dtos;
594
        $formatted['paths'] = $paths;
595
        return $formatted;
596
    }
597
598
    /**
599
     * Method that extract the Dto class for the api documentation
600
     * @param string $dto
601
     * @param boolean $isArray
602
     *
603
     * @return string
604
     */
605
    protected function extractDtoName($dto, $isArray = false)
606
    {
607
        $dto = explode('\\', $dto);
608
        $modelDto = array_pop($dto) . "Dto";
609
        if ($isArray) {
610
            $modelDto .= "List";
611
        }
612
613
        return $modelDto;
614
    }
615
616
    /**
617
     * @param \ReflectionMethod $method
618
     * @param $methodInfo
619
     */
620
    protected function setQueryParams(\ReflectionMethod $method, &$methodInfo)
621
    {
622
        if (in_array($methodInfo['method'], ['GET']) && in_array($method->getShortName(), self::$nativeMethods)) {
623
            $methodInfo['query'] = [];
624
            $methodInfo['query'][] = [
625
                "name" => "__limit",
626
                "in" => "query",
627
                "description" => _("Límite de registros a devolver, -1 para devolver todos los registros"),
628
                "required" => false,
629
                "type" => "integer",
630
            ];
631
            $methodInfo['query'][] = [
632
                "name" => "__page",
633
                "in" => "query",
634
                "description" => _("Página a devolver"),
635
                "required" => false,
636
                "type" => "integer",
637
            ];
638
            $methodInfo['query'][] = [
639
                "name" => "__fields",
640
                "in" => "query",
641
                "description" => _("Campos a devolver"),
642
                "required" => false,
643
                "type" => "array",
644
                "items" => [
645
                    "type" => "string",
646
                ]
647
            ];
648
        }
649
    }
650
    /**
651
     * @param \ReflectionClass $reflection
652
     * @param $methodInfo
653
     */
654
    protected function setRequestHeaders(\ReflectionClass $reflection, &$methodInfo)
655
    {
656
657
        $methodInfo['headers'] = [];
658
        foreach($reflection->getProperties() as $property) {
659
            $doc = $property->getDocComment();
660
            preg_match('/@header\ (.*)\n/i', $doc, $headers);
661
            if(count($headers)) {
662
                $header = [
663
                    "name" => $headers[1],
664
                    "in" => "header",
665
                    "required" => true,
666
                ];
667
668
                // Extract var type
669
                preg_match('/@var\ (.*)\n/i', $doc, $type);
670
                if(count($type)) {
671
                    $header['type'] = $type[1];
672
                }
673
674
                // Extract description
675
                preg_match('/@label\ (.*)\n/i', $doc, $label);
676
                if(count($label)) {
677
                    $header['description'] = _($label[1]);
678
                }
679
680
                // Extract default value
681
                preg_match('/@default\ (.*)\n/i', $doc, $default);
682
                if(count($default)) {
683
                    $header['default'] = $default[1];
684
                }
685
                $methodInfo['headers'][] = $header;
686
            }
687
        }
688
    }
689
690
    /**
691
     * @param \ReflectionMethod $method
692
     * @param array $methodInfo
693
     * @param string $modelNamespace
694
     * @param string $docComments
695
     */
696
    protected function setRequestParams(\ReflectionMethod $method, &$methodInfo, $modelNamespace, $docComments)
697
    {
698
        if (in_array($methodInfo['method'], ['POST', 'PUT'])) {
699
            $payload = $this->extractPayload($modelNamespace, $docComments);
700
            if (count($payload)) {
701
                $methodInfo['payload'] = $payload;
702
            }
703
        }
704
        if ($method->getNumberOfParameters() > 0) {
705
            $methodInfo['parameters'] = [];
706
            foreach ($method->getParameters() as $parameter) {
707
                $parameterName = $parameter->getName();
708
                $types = [];
709
                preg_match_all('/\@param\ (.*)\ \$' . $parameterName . '$/im', $docComments, $types);
710
                if (count($types) > 1) {
711
                    $methodInfo['parameters'][$parameterName] = $types[1][0];
712
                }
713
            }
714
        }
715
    }
716
}
717