Passed
Push — master ( be88e3...87c6b6 )
by Fran
04:17
created

DocumentorService::checkDtoAttributes()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

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

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

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

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

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

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

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

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

393
        if (FALSE !== $docComments && preg_match('/\@route\ /i', /** @scrutinizer ignore-type */ $docComments)) {
Loading history...
394
            $api = self::extractApi($reflection->getDocComment());
0 ignored issues
show
Bug Best Practice introduced by
The method PSFS\services\DocumentorService::extractApi() is not static, but was called statically. ( Ignorable by Annotation )

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

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

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

398
            if ($info['visible'] && !self::checkDeprecated(/** @scrutinizer ignore-type */ $docComments)) {
Loading history...
Bug Best Practice introduced by
The method PSFS\services\DocumentorService::checkDeprecated() is not static, but was called statically. ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

}

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

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

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

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