Passed
Push — master ( 926ba1...14f6bb )
by Fran
06:34 queued 03:13
created

DocumentorService   D

Complexity

Total Complexity 112

Size/Duplication

Total Lines 717
Duplicated Lines 1.95 %

Coupling/Cohesion

Components 2
Dependencies 9

Test Coverage

Coverage 5.18%

Importance

Changes 0
Metric Value
wmc 112
lcom 2
cbo 9
dl 14
loc 717
ccs 17
cts 328
cp 0.0518
rs 4.4444
c 0
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
B getModules() 0 22 6
B extractApiEndpoints() 0 21 5
B extractApiInfo() 0 18 5
A extractRoute() 0 7 1
A extractApi() 0 7 1
A checkDeprecated() 0 4 1
A extractVisibility() 0 10 2
B extractDescription() 4 15 5
A extractVarType() 0 11 3
A extractPayload() 0 15 2
A extractDtoProperties() 0 10 2
D extractReturn() 0 29 9
C extractModelFields() 0 26 7
B extractMethodInfo() 0 34 6
C translateSwaggerFormats() 0 41 13
B extractSwaggerDefinition() 0 24 3
B swaggerResponses() 0 45 6
D swaggerFormatter() 10 99 19
A extractDtoName() 0 10 2
B setQueryParams() 0 30 3
B setRequestHeaders() 0 32 5
B setRequestParams() 0 23 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like DocumentorService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DocumentorService, and based on these observations, apply Extract Interface, too.

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
                    $info = [
323
                        "type" => $field->getType(),
324
                        "required" => $field->isNotNull(),
325
                    ];
326
                    $payload[$field->getPhpName()] = $info;
327
                }
328
            } elseif (null !== $reflector && $reflector->isSubclassOf(self::DTO_INTERFACE)) {
329
                $payload = $this->extractDtoProperties($namespace);
330
            }
331
        } catch (\Exception $e) {
332
            Logger::getInstance()->errorLog($e->getMessage());
333
        }
334
335
        return $payload;
336
    }
337
338
    /**
339
     * Method that extract all the needed info for each method in each API
340
     *
341
     * @param string $namespace
342
     * @param \ReflectionMethod $method
343
     * @param \ReflectionClass $reflection
344
     * @param string $module
345
     *
346
     * @return array
347
     */
348
    protected function extractMethodInfo($namespace, \ReflectionMethod $method, \ReflectionClass $reflection, $module)
349
    {
350
        $methodInfo = NULL;
351
        $docComments = $method->getDocComment();
352
        if (FALSE !== $docComments && preg_match('/\@route\ /i', $docComments)) {
353
            $api = self::extractApi($reflection->getDocComment());
354
            list($route, $info) = RouterHelper::extractRouteInfo($method, $api, $module);
355
            $route = explode('#|#', $route);
356
            $modelNamespace = str_replace('Api', 'Models', $namespace);
357
            if ($info['visible'] && !self::checkDeprecated($docComments)) {
358
                try {
359
                    $return = $this->extractReturn($modelNamespace, $docComments);
360
                    $url = array_pop($route);
361
                    $methodInfo = [
362
                        'url' => str_replace("/" . $module . "/api", '', $url),
363
                        'method' => $info['http'],
364
                        'description' => $info['label'],
365
                        'return' => $return,
366
                        'objects' => $return['objects'],
367
                        'class' => $reflection->getShortName(),
368
                    ];
369
                    unset($methodInfo['return']['objects']);
370
                    $this->setRequestParams($method, $methodInfo, $modelNamespace, $docComments);
371
                    $this->setQueryParams($method, $methodInfo);
372
                    $this->setRequestHeaders($reflection, $methodInfo);
373
                } catch (\Exception $e) {
374
                    jpre($e->getMessage());
375
                    Logger::getInstance()->errorLog($e->getMessage());
376
                }
377
            }
378
        }
379
380
        return $methodInfo;
381
    }
382
383
    /**
384
     * Translator from php types to swagger types
385
     * @param string $format
386
     *
387
     * @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...
388
     */
389 1
    public static function translateSwaggerFormats($format)
390
    {
391 1
        switch (strtolower($format)) {
392 1
            case 'bool':
393 1
            case 'boolean':
394
                $swaggerType = 'boolean';
395
                $swaggerFormat = '';
396
                break;
397
            default:
398 1
            case 'string':
399 1
            case 'varchar':
400 1
                $swaggerType = 'string';
401 1
                $swaggerFormat = '';
402 1
                break;
403 1
            case 'binary':
404 1
            case 'varbinary':
405
                $swaggerType = 'string';
406
                $swaggerFormat = 'binary';
407
                break;
408 1
            case 'int':
409 1
            case 'integer':
410 1
                $swaggerType = 'integer';
411 1
                $swaggerFormat = 'int32';
412 1
                break;
413
            case 'float':
414
            case 'double':
415
                $swaggerType = 'number';
416
                $swaggerFormat = strtolower($format);
417
                break;
418
            case 'date':
419
                $swaggerType = 'string';
420
                $swaggerFormat = 'date';
421
                break;
422
            case 'datetime':
423
                $swaggerType = 'string';
424
                $swaggerFormat = 'date-time';
425
                break;
426
427
        }
428 1
        return [$swaggerType, $swaggerFormat];
429
    }
430
431
    /**
432
     * Method that parse the definitions for the api's
433
     * @param string $name
434
     * @param array $fields
435
     *
436
     * @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...
437
     */
438
    public static function extractSwaggerDefinition($name, array $fields)
439
    {
440
        $definition = [
441
            $name => [
442
                "type" => "object",
443
                "properties" => [],
444
            ],
445
        ];
446
        foreach ($fields as $field => $info) {
447
            list($type, $format) = self::translateSwaggerFormats($info['type']);
448
            $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...
449
                "type" => $type,
450
                "required" => $info['required'],
451
            ];
452
            $definition[$name]['properties'][$field] = [
453
                "type" => $type,
454
                "required" => $info['required'],
455
            ];
456
            if (strlen($format)) {
457
                $definition[$name]['properties'][$field]['format'] = $format;
458
            }
459
        }
460
        return $definition;
461
    }
462
463
    /**
464
     * @return array
465
     */
466
    private static function swaggerResponses()
467
    {
468
        $codes = [200, 400, 404, 500];
469
        $responses = [];
470
        foreach ($codes as $code) {
471
            switch ($code) {
472
                default:
473
                case 200:
474
                    $message = _('Successful response');
475
                    break;
476
                case 400:
477
                    $message = _('Client error in request');
478
                    break;
479
                case 404:
480
                    $message = _('Service not found');
481
                    break;
482
                case 500:
483
                    $message = _('Server error');
484
                    break;
485
            }
486
            $responses[$code] = [
487
                'description' => $message,
488
                'schema' => [
489
                    'type' => 'object',
490
                    'properties' => [
491
                        'success' => [
492
                            'type' => 'boolean'
493
                        ],
494
                        'data' => [
495
                            'type' => 'boolean',
496
                        ],
497
                        'total' => [
498
                            'type' => 'integer',
499
                            'format' => 'int32',
500
                        ],
501
                        'pages' => [
502
                            'type' => 'integer',
503
                            'format' => 'int32',
504
                        ]
505
                    ]
506
                ]
507
            ];
508
        }
509
        return $responses;
510
    }
511
512
    /**
513
     * Method that export
514
     * @param array $module
515
     *
516
     * @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...
517
     */
518
    public static function swaggerFormatter(array $module)
519
    {
520
        $formatted = [
521
            "swagger" => "2.0",
522
            "host" => preg_replace('/^(http|https)\:\/\/(.*)\/$/i', '$2', Router::getInstance()->getRoute('', true)),
523
            "basePath" => '/' . $module['name'] . '/api',
524
            "schemes" => [Request::getInstance()->getServer('HTTPS') == 'on' ? "https" : "http"],
525
            "info" => [
526
                "title" => _('Documentación API módulo ') . $module['name'],
527
                "version" => Config::getParam('api.version', '1.0'),
528
                "contact" => [
529
                    "name" => Config::getParam("author", "Fran López"),
530
                    "email" => Config::getParam("author_email", "[email protected]"),
531
                ]
532
            ]
533
        ];
534
        $dtos = $paths = [];
535
        $endpoints = DocumentorService::getInstance()->extractApiEndpoints($module);
536
        foreach ($endpoints as $model) {
537
            foreach ($model as $endpoint) {
538
                if (!preg_match('/^\/(admin|api)\//i', $endpoint['url']) && strlen($endpoint['url'])) {
539
                    $url = preg_replace('/\/' . $module['name'] . '\/api/i', '', $endpoint['url']);
540
                    $description = $endpoint['description'];
541
                    $method = strtolower($endpoint['method']);
542
                    $paths[$url][$method] = [
543
                        'summary' => $description,
544
                        'produces' => ['application/json'],
545
                        'consumes' => ['application/json'],
546
                        'responses' => self::swaggerResponses(),
547
                        'parameters' => [],
548
                    ];
549
                    if (array_key_exists('parameters', $endpoint)) {
550
                        foreach ($endpoint['parameters'] as $parameter => $type) {
551
                            list($type, $format) = self::translateSwaggerFormats($type);
552
                            $paths[$url][$method]['parameters'][] = [
553
                                'in' => 'path',
554
                                'required' => true,
555
                                'name' => $parameter,
556
                                'type' => $type,
557
                                'format' => $format,
558
                            ];
559
                        }
560
                    }
561 View Code Duplication
                    if (array_key_exists('query', $endpoint)) {
562
                        foreach ($endpoint['query'] as $query) {
563
                            $paths[$url][$method]['parameters'][] = $query;
564
                        }
565
                    }
566 View Code Duplication
                    if (array_key_exists('headers', $endpoint)) {
567
                        foreach ($endpoint['headers'] as $query) {
568
                            $paths[$url][$method]['parameters'][] = $query;
569
                        }
570
                    }
571
                    foreach ($endpoint['objects'] as $name => $object) {
572
                        if (class_exists($name)) {
573
                            $class = GeneratorHelper::extractClassFromNamespace($name);
574
                            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...
575
                                $classDefinition = [
576
                                    'type' => 'object',
577
                                    '$ref' => '#/definitions/' . $class,
578
                                ];
579
                            } else {
580
                                $classDefinition = [
581
                                    'type' => 'array',
582
                                    'items' => [
583
                                        '$ref' => '#/definitions/' . $class,
584
                                    ],
585
                                ];
586
                            }
587
588
                            $paths[$url][$method]['responses'][200]['schema']['properties']['data'] = $classDefinition;
589
                            $dtos += self::extractSwaggerDefinition($class, $object);
590
                            if (array_key_exists('payload', $endpoint)) {
591
                                $dtos[$endpoint['payload']['type']] = [
592
                                    'type' => 'object',
593
                                    'properties' => $endpoint['payload']['properties'],
594
                                ];
595
                                $paths[$url][$method]['parameters'][] = [
596
                                    'in' => 'body',
597
                                    'name' => $endpoint['payload']['type'],
598
                                    'required' => true,
599
                                    'schema' => [
600
                                        'type' => 'object',
601
                                        '$ref' => '#/definitions/' . $endpoint['payload']['type'],
602
                                    ],
603
                                ];
604
                            }
605
                        }
606
                        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...
607
                            $paths[$url][$method]['tags'][] = $endpoint['class'];
608
                        }
609
                    }
610
                }
611
            }
612
        }
613
        $formatted['definitions'] = $dtos;
614
        $formatted['paths'] = $paths;
615
        return $formatted;
616
    }
617
618
    /**
619
     * Method that extract the Dto class for the api documentation
620
     * @param string $dto
621
     * @param boolean $isArray
622
     *
623
     * @return string
624
     */
625
    protected function extractDtoName($dto, $isArray = false)
626
    {
627
        $dto = explode('\\', $dto);
628
        $modelDto = array_pop($dto) . "Dto";
629
        if ($isArray) {
630
            $modelDto .= "List";
631
        }
632
633
        return $modelDto;
634
    }
635
636
    /**
637
     * @param \ReflectionMethod $method
638
     * @param $methodInfo
639
     */
640
    protected function setQueryParams(\ReflectionMethod $method, &$methodInfo)
641
    {
642
        if (in_array($methodInfo['method'], ['GET']) && in_array($method->getShortName(), self::$nativeMethods)) {
643
            $methodInfo['query'] = [];
644
            $methodInfo['query'][] = [
645
                "name" => "__limit",
646
                "in" => "query",
647
                "description" => _("Límite de registros a devolver, -1 para devolver todos los registros"),
648
                "required" => false,
649
                "type" => "integer",
650
            ];
651
            $methodInfo['query'][] = [
652
                "name" => "__page",
653
                "in" => "query",
654
                "description" => _("Página a devolver"),
655
                "required" => false,
656
                "type" => "integer",
657
            ];
658
            $methodInfo['query'][] = [
659
                "name" => "__fields",
660
                "in" => "query",
661
                "description" => _("Campos a devolver"),
662
                "required" => false,
663
                "type" => "array",
664
                "items" => [
665
                    "type" => "string",
666
                ]
667
            ];
668
        }
669
    }
670
    /**
671
     * @param \ReflectionClass $reflection
672
     * @param $methodInfo
673
     */
674
    protected function setRequestHeaders(\ReflectionClass $reflection, &$methodInfo)
675
    {
676
677
        $methodInfo['headers'] = [];
678
        foreach($reflection->getProperties() as $property) {
679
            $doc = $property->getDocComment();
680
            preg_match('/@header\ (.*)\n/i', $doc, $headers);
681
            if(count($headers)) {
682
                $header = [
683
                    "name" => $headers[1],
684
                    "in" => "header",
685
                    "required" => true,
686
                ];
687
688
                // Extract var type
689
                $header['type'] = $this->extractVarType($doc);
690
691
                // Extract description
692
                preg_match('/@label\ (.*)\n/i', $doc, $label);
693
                if(count($label)) {
694
                    $header['description'] = _($label[1]);
695
                }
696
697
                // Extract default value
698
                preg_match('/@default\ (.*)\n/i', $doc, $default);
699
                if(count($default)) {
700
                    $header['default'] = $default[1];
701
                }
702
                $methodInfo['headers'][] = $header;
703
            }
704
        }
705
    }
706
707
    /**
708
     * @param \ReflectionMethod $method
709
     * @param array $methodInfo
710
     * @param string $modelNamespace
711
     * @param string $docComments
712
     */
713
    protected function setRequestParams(\ReflectionMethod $method, &$methodInfo, $modelNamespace, $docComments)
714
    {
715
        if (in_array($methodInfo['method'], ['POST', 'PUT'])) {
716
            list($payloadNamespace, $payloadDto) = $this->extractPayload($modelNamespace, $docComments);
717
            if (count($payloadDto)) {
718
                $methodInfo['payload'] = [
719
                    'type' => $payloadNamespace,
720
                    'properties' => $payloadDto,
721
                ];
722
            }
723
        }
724
        if ($method->getNumberOfParameters() > 0) {
725
            $methodInfo['parameters'] = [];
726
            foreach ($method->getParameters() as $parameter) {
727
                $parameterName = $parameter->getName();
728
                $types = [];
729
                preg_match_all('/\@param\ (.*)\ \$' . $parameterName . '$/im', $docComments, $types);
730
                if (count($types) > 1) {
731
                    $methodInfo['parameters'][$parameterName] = $types[1][0];
732
                }
733
            }
734
        }
735
    }
736
}
737