Completed
Push — master ( 7a53af...810e56 )
by
unknown
01:51
created

AbstractGenerator::getMethods()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Mpociot\ApiDoc\Generators;
4
5
use Faker\Factory;
6
use ReflectionClass;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Str;
9
use League\Fractal\Manager;
10
use Illuminate\Routing\Route;
11
use Mpociot\Reflection\DocBlock;
12
use League\Fractal\Resource\Item;
13
use Mpociot\Reflection\DocBlock\Tag;
14
use League\Fractal\Resource\Collection;
15
use Illuminate\Support\Facades\Validator;
16
use Illuminate\Foundation\Http\FormRequest;
17
use Mpociot\ApiDoc\Parsers\RuleDescriptionParser as Description;
18
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
19
20
abstract class AbstractGenerator
21
{
22
    /**
23
     * @param Route $route
24
     *
25
     * @return mixed
26
     */
27
    public function getDomain(Route $route)
28
    {
29
        return $route->domain();
30
    }
31
32
    /**
33
     * @param Route $route
34
     *
35
     * @return mixed
36
     */
37
    public function getUri(Route $route)
38
    {
39
        return $route->uri();
40
    }
41
42
    /**
43
     * @param Route $route
44
     *
45
     * @return mixed
46
     */
47
    public function getMethods(Route $route)
48
    {
49
        return array_diff($route->methods(), ['HEAD']);
50
    }
51
52
    /**
53
     * @param  \Illuminate\Routing\Route $route
54
     * @param array $bindings
55
     * @param bool $withResponse
56
     *
57
     * @return array
58
     */
59
    public function processRoute($route, $bindings = [], $headers = [], $withResponse = true)
60
    {
61
        $routeDomain = $route->domain();
62
        $routeAction = $route->getAction();
63
        $routeGroup = $this->getRouteGroup($routeAction['uses']);
64
        $routeDescription = $this->getRouteDescription($routeAction['uses']);
65
        $showresponse = null;
66
67
        // set correct route domain
68
        $headers[] = "HTTP_HOST: {$routeDomain}";
69
        $headers[] = "SERVER_NAME: {$routeDomain}";
70
71
        $response = null;
72
        $docblockResponse = $this->getDocblockResponse($routeDescription['tags']);
73
        if ($docblockResponse) {
74
            // we have a response from the docblock ( @response )
75
            $response = $docblockResponse;
76
            $showresponse = true;
77
        }
78
        if (! $response) {
79
            $transformerResponse = $this->getTransformerResponse($routeDescription['tags']);
80
            if ($transformerResponse) {
81
                // we have a transformer response from the docblock ( @transformer || @transformercollection )
82
                $response = $transformerResponse;
83
                $showresponse = true;
84
            }
85
        }
86
        if (! $response && $withResponse) {
87
            try {
88
                $response = $this->getRouteResponse($route, $bindings, $headers);
89
            } catch (\Exception $e) {
90
                echo "Couldn't get response for route: ".implode(',', $this->getMethods($route)).$route->uri().']: '.$e->getMessage()."\n";
91
            }
92
        }
93
94
        $content = $this->getResponseContent($response);
95
96
        return $this->getParameters([
97
            'id' => md5($this->getUri($route).':'.implode($this->getMethods($route))),
98
            'resource' => $routeGroup,
99
            'title' => $routeDescription['short'],
100
            'description' => $routeDescription['long'],
101
            'methods' => $this->getMethods($route),
102
            'uri' => $this->getUri($route),
103
            'parameters' => [],
104
            'response' => $content,
105
            'showresponse' => $showresponse,
106
        ], $routeAction, $bindings);
107
    }
108
109
    /**
110
     * Prepares / Disables route middlewares.
111
     *
112
     * @param  bool $disable
0 ignored issues
show
Bug introduced by
There is no parameter named $disable. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
113
     *
114
     * @return  void
115
     */
116
    abstract public function prepareMiddleware($enable = false);
117
118
    /**
119
     * Get the response from the docblock if available.
120
     *
121
     * @param array $tags
122
     *
123
     * @return mixed
124
     */
125
    protected function getDocblockResponse($tags)
126
    {
127
        $responseTags = array_filter($tags, function ($tag) {
128
            if (! ($tag instanceof Tag)) {
129
                return false;
130
            }
131
132
            return \strtolower($tag->getName()) == 'response';
133
        });
134
        if (empty($responseTags)) {
135
            return;
136
        }
137
        $responseTag = \array_first($responseTags);
138
139
        return \response(json_encode($responseTag->getContent()), 200, ['Content-Type' => 'application/json']);
140
    }
141
142
    /**
143
     * @param array $routeData
144
     * @param array $routeAction
145
     * @param array $bindings
146
     *
147
     * @return mixed
148
     */
149
    protected function getParameters($routeData, $routeAction, $bindings)
150
    {
151
        $validationRules = $this->getRouteValidationRules($routeData['methods'], $routeAction['uses'], $bindings);
152
        $rules = $this->simplifyRules($validationRules);
153
154
        foreach ($rules as $attribute => $ruleset) {
155
            $attributeData = [
156
                'required' => false,
157
                'type' => null,
158
                'default' => '',
159
                'value' => '',
160
                'description' => [],
161
            ];
162
            foreach ($ruleset as $rule) {
163
                $this->parseRule($rule, $attribute, $attributeData, $routeData['id'], $routeData);
164
            }
165
            $routeData['parameters'][$attribute] = $attributeData;
166
        }
167
168
        return $routeData;
169
    }
170
171
    /**
172
     * Format the validation rules as a plain array.
173
     *
174
     * @param array $rules
175
     *
176
     * @return array
177
     */
178
    protected function simplifyRules($rules)
179
    {
180
        // this will split all string rules into arrays of strings
181
        $rules = Validator::make([], $rules)->getRules();
182
183
        if (count($rules) === 0) {
184
            return $rules;
185
        }
186
187
        // Laravel will ignore the nested array rules unless the key referenced exists and is an array
188
        // So we'll to create an empty array for each array attribute
189
        $values = collect($rules)
190
            ->filter(function ($values) {
191
                return in_array('array', $values);
192
            })->map(function ($val, $key) {
0 ignored issues
show
Unused Code introduced by
The parameter $val is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
193
                return [''];
194
            })->all();
195
196
        // Now this will return the complete ruleset.
197
        // Nested array parameters will be present, with '*' replaced by '0'
198
        return Validator::make($values, $rules)->getRules();
199
    }
200
201
    /**
202
     * @param  $route
203
     * @param  $bindings
204
     * @param  $headers
205
     *
206
     * @return \Illuminate\Http\Response
207
     */
208
    protected function getRouteResponse($route, $bindings, $headers = [])
209
    {
210
        $uri = $this->addRouteModelBindings($route, $bindings);
211
212
        $methods = $this->getMethods($route);
213
214
        // Split headers into key - value pairs
215
        $headers = collect($headers)->map(function ($value) {
216
            $split = explode(':', $value); // explode to get key + values
217
            $key = array_shift($split); // extract the key and keep the values in the array
218
            $value = implode(':', $split); // implode values into string again
219
220
            return [trim($key) => trim($value)];
221
        })->collapse()->toArray();
222
223
        //Changes url with parameters like /users/{user} to /users/1
224
        $uri = preg_replace('/{(.*?)}/', 1, $uri); // 1 is the default value for route parameters
225
226
        return $this->callRoute(array_shift($methods), $uri, [], [], [], $headers);
227
    }
228
229
    /**
230
     * @param $route
231
     * @param array $bindings
232
     *
233
     * @return mixed
234
     */
235
    protected function addRouteModelBindings($route, $bindings)
236
    {
237
        $uri = $this->getUri($route);
238
        foreach ($bindings as $model => $id) {
239
            $uri = str_replace('{'.$model.'}', $id, $uri);
240
            $uri = str_replace('{'.$model.'?}', $id, $uri);
241
        }
242
243
        return $uri;
244
    }
245
246
    /**
247
     * @param  \Illuminate\Routing\Route  $route
248
     *
249
     * @return array
250
     */
251
    protected function getRouteDescription($route)
252
    {
253
        list($class, $method) = explode('@', $route);
254
        $reflection = new ReflectionClass($class);
255
        $reflectionMethod = $reflection->getMethod($method);
256
257
        $comment = $reflectionMethod->getDocComment();
258
        $phpdoc = new DocBlock($comment);
259
260
        return [
261
            'short' => $phpdoc->getShortDescription(),
262
            'long' => $phpdoc->getLongDescription()->getContents(),
263
            'tags' => $phpdoc->getTags(),
264
        ];
265
    }
266
267
    /**
268
     * @param  string  $route
269
     *
270
     * @return string
271
     */
272
    protected function getRouteGroup($route)
273
    {
274
        list($class, $method) = explode('@', $route);
0 ignored issues
show
Unused Code introduced by
The assignment to $method is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
275
        $reflection = new ReflectionClass($class);
276
        $comment = $reflection->getDocComment();
277
        if ($comment) {
278
            $phpdoc = new DocBlock($comment);
279
            foreach ($phpdoc->getTags() as $tag) {
280
                if ($tag->getName() === 'resource') {
281
                    return $tag->getContent();
282
                }
283
            }
284
        }
285
286
        return 'general';
287
    }
288
289
    /**
290
     * @param  array $routeMethods
291
     * @param  string $routeAction
292
     * @param  array $bindings
293
     *
294
     * @return array
295
     */
296
    protected function getRouteValidationRules(array $routeMethods, $routeAction, $bindings)
297
    {
298
        list($controller, $method) = explode('@', $routeAction);
299
        $reflection = new ReflectionClass($controller);
300
        $reflectionMethod = $reflection->getMethod($method);
301
302
        foreach ($reflectionMethod->getParameters() as $parameter) {
303
            $parameterType = $parameter->getClass();
304
            if (! is_null($parameterType) && class_exists($parameterType->name)) {
305
                $className = $parameterType->name;
306
307
                if (is_subclass_of($className, FormRequest::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \Illuminate\Foundation\Http\FormRequest::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
308
                    /** @var FormRequest $formRequest */
309
                    $formRequest = new $className;
310
                    // Add route parameter bindings
311
                    $formRequest->setContainer(app());
312
                    $formRequest->request->add($bindings);
313
                    $formRequest->query->add($bindings);
314
                    $formRequest->setMethod($routeMethods[0]);
315
316
                    if (method_exists($formRequest, 'validator')) {
317
                        $factory = app(ValidationFactory::class);
318
319
                        return call_user_func_array([$formRequest, 'validator'], [$factory])
320
                            ->getRules();
321
                    } else {
322
                        return call_user_func_array([$formRequest, 'rules'], []);
323
                    }
324
                }
325
            }
326
        }
327
328
        return [];
329
    }
330
331
    /**
332
     * @param  array  $arr
333
     * @param  string  $first
334
     * @param  string  $last
335
     *
336
     * @return string
337
     */
338
    protected function fancyImplode($arr, $first, $last)
339
    {
340
        $arr = array_map(function ($value) {
341
            return '`'.$value.'`';
342
        }, $arr);
343
        array_push($arr, implode($last, array_splice($arr, -2)));
344
345
        return implode($first, $arr);
346
    }
347
348
    protected function splitValuePairs($parameters, $first = 'is ', $last = 'or ')
349
    {
350
        $attribute = '';
351
        collect($parameters)->map(function ($item, $key) use (&$attribute, $first, $last) {
352
            $attribute .= '`'.$item.'` ';
353
            if (($key + 1) % 2 === 0) {
354
                $attribute .= $last;
355
            } else {
356
                $attribute .= $first;
357
            }
358
        });
359
        $attribute = rtrim($attribute, $last);
360
361
        return $attribute;
362
    }
363
364
    /**
365
     * @param  string  $rule
366
     * @param  string  $attribute
367
     * @param  array  $attributeData
368
     * @param  int  $seed
369
     *
370
     * @return void
371
     */
372
    protected function parseRule($rule, $attribute, &$attributeData, $seed, $routeData)
373
    {
374
        $faker = Factory::create();
375
        $faker->seed(crc32($seed));
376
377
        $parsedRule = $this->parseStringRule($rule);
378
        $parsedRule[0] = $this->normalizeRule($parsedRule[0]);
379
        list($rule, $parameters) = $parsedRule;
380
381
        switch ($rule) {
382
            case 'required':
383
                $attributeData['required'] = true;
384
                break;
385
            case 'accepted':
386
                $attributeData['required'] = true;
387
                $attributeData['type'] = 'boolean';
388
                $attributeData['value'] = true;
389
                break;
390 View Code Duplication
            case 'after':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
391
                $attributeData['type'] = 'date';
392
                $format = isset($attributeData['format']) ? $attributeData['format'] : DATE_RFC850;
393
394
                if (strtotime($parameters[0]) === false) {
395
                    // the `after` date refers to another parameter in the request
396
                    $paramName = $parameters[0];
397
                    $attributeData['description'][] = Description::parse($rule)->with($paramName)->getDescription();
398
                    $attributeData['value'] = date($format, strtotime('+1 day', strtotime($routeData['parameters'][$paramName]['value'])));
399
                } else {
400
                    $attributeData['description'][] = Description::parse($rule)->with(date($format, strtotime($parameters[0])))->getDescription();
401
                    $attributeData['value'] = date($format, strtotime('+1 day', strtotime($parameters[0])));
402
                }
403
                break;
404 View Code Duplication
            case 'alpha':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
405
                $attributeData['description'][] = Description::parse($rule)->getDescription();
406
                $attributeData['value'] = $faker->word;
407
                break;
408
            case 'alpha_dash':
409
                $attributeData['description'][] = Description::parse($rule)->getDescription();
410
                break;
411
            case 'alpha_num':
412
                $attributeData['description'][] = Description::parse($rule)->getDescription();
413
                break;
414 View Code Duplication
            case 'in':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
415
                $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
416
                $attributeData['value'] = $faker->randomElement($parameters);
417
                break;
418 View Code Duplication
            case 'not_in':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
419
                $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
420
                $attributeData['value'] = $faker->word;
421
                break;
422 View Code Duplication
            case 'min':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
423
                $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
424
                if (Arr::get($attributeData, 'type') === 'numeric' || Arr::get($attributeData, 'type') === 'integer') {
425
                    $attributeData['value'] = $faker->numberBetween($parameters[0]);
426
                }
427
                break;
428 View Code Duplication
            case 'max':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
429
                $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
430
                if (Arr::get($attributeData, 'type') === 'numeric' || Arr::get($attributeData, 'type') === 'integer') {
431
                    $attributeData['value'] = $faker->numberBetween(0, $parameters[0]);
432
                }
433
                break;
434 View Code Duplication
            case 'between':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
435
                if (! isset($attributeData['type'])) {
436
                    $attributeData['type'] = 'numeric';
437
                }
438
                $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
439
                $attributeData['value'] = $faker->numberBetween($parameters[0], $parameters[1]);
440
                break;
441 View Code Duplication
            case 'before':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
442
                $attributeData['type'] = 'date';
443
                $format = isset($attributeData['format']) ? $attributeData['format'] : DATE_RFC850;
444
445
                if (strtotime($parameters[0]) === false) {
446
                    // the `before` date refers to another parameter in the request
447
                    $paramName = $parameters[0];
448
                    $attributeData['description'][] = Description::parse($rule)->with($paramName)->getDescription();
449
                    $attributeData['value'] = date($format, strtotime('-1 day', strtotime($routeData['parameters'][$paramName]['value'])));
450
                } else {
451
                    $attributeData['description'][] = Description::parse($rule)->with(date($format, strtotime($parameters[0])))->getDescription();
452
                    $attributeData['value'] = date($format, strtotime('-1 day', strtotime($parameters[0])));
453
                }
454
                break;
455 View Code Duplication
            case 'date_format':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
456
                $attributeData['type'] = 'date';
457
                $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
458
                $attributeData['format'] = $parameters[0];
459
                $attributeData['value'] = date($attributeData['format']);
460
                break;
461
            case 'different':
462
                $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
463
                break;
464
            case 'digits':
465
                $attributeData['type'] = 'numeric';
466
                $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
467
                $attributeData['value'] = ($parameters[0] < 9) ? $faker->randomNumber($parameters[0], true) : substr(mt_rand(100000000, mt_getrandmax()), 0, $parameters[0]);
468
                break;
469
            case 'digits_between':
470
                $attributeData['type'] = 'numeric';
471
                $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
472
                break;
473 View Code Duplication
            case 'file':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
474
                $attributeData['type'] = 'file';
475
                $attributeData['description'][] = Description::parse($rule)->getDescription();
476
                break;
477 View Code Duplication
            case 'image':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
478
                $attributeData['type'] = 'image';
479
                $attributeData['description'][] = Description::parse($rule)->getDescription();
480
                break;
481
            case 'json':
482
                $attributeData['type'] = 'string';
483
                $attributeData['description'][] = Description::parse($rule)->getDescription();
484
                $attributeData['value'] = json_encode(['foo', 'bar', 'baz']);
485
                break;
486
            case 'mimetypes':
487 View Code Duplication
            case 'mimes':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
488
                $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
489
                break;
490 View Code Duplication
            case 'required_if':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
491
                $attributeData['description'][] = Description::parse($rule)->with($this->splitValuePairs($parameters))->getDescription();
492
                break;
493 View Code Duplication
            case 'required_unless':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
494
                $attributeData['description'][] = Description::parse($rule)->with($this->splitValuePairs($parameters))->getDescription();
495
                break;
496 View Code Duplication
            case 'required_with':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
497
                $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
498
                break;
499 View Code Duplication
            case 'required_with_all':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
500
                $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' and '))->getDescription();
501
                break;
502 View Code Duplication
            case 'required_without':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
503
                $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' or '))->getDescription();
504
                break;
505 View Code Duplication
            case 'required_without_all':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
506
                $attributeData['description'][] = Description::parse($rule)->with($this->fancyImplode($parameters, ', ', ' and '))->getDescription();
507
                break;
508
            case 'same':
509
                $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
510
                break;
511
            case 'size':
512
                $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
513
                break;
514 View Code Duplication
            case 'timezone':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
515
                $attributeData['description'][] = Description::parse($rule)->getDescription();
516
                $attributeData['value'] = $faker->timezone;
517
                break;
518
            case 'exists':
519
                $fieldName = isset($parameters[1]) ? $parameters[1] : $attribute;
520
                $attributeData['description'][] = Description::parse($rule)->with([Str::singular($parameters[0]), $fieldName])->getDescription();
521
                break;
522
            case 'active_url':
523
                $attributeData['type'] = 'url';
524
                $attributeData['value'] = $faker->url;
525
                break;
526
            case 'regex':
527
                $attributeData['type'] = 'string';
528
                $attributeData['description'][] = Description::parse($rule)->with($parameters)->getDescription();
529
                break;
530
            case 'boolean':
531
                $attributeData['value'] = true;
532
                $attributeData['type'] = $rule;
533
                break;
534
            case 'array':
535
                $attributeData['value'] = $faker->word;
536
                $attributeData['type'] = $rule;
537
                break;
538
            case 'date':
539
                $attributeData['value'] = $faker->date();
540
                $attributeData['type'] = $rule;
541
                break;
542
            case 'email':
543
                $attributeData['value'] = $faker->safeEmail;
544
                $attributeData['type'] = $rule;
545
                break;
546
            case 'string':
547
                $attributeData['value'] = $faker->word;
548
                $attributeData['type'] = $rule;
549
                break;
550
            case 'integer':
551
                $attributeData['value'] = $faker->randomNumber();
552
                $attributeData['type'] = $rule;
553
                break;
554
            case 'numeric':
555
                $attributeData['value'] = $faker->randomNumber();
556
                $attributeData['type'] = $rule;
557
                break;
558
            case 'url':
559
                $attributeData['value'] = $faker->url;
560
                $attributeData['type'] = $rule;
561
                break;
562
            case 'ip':
563
                $attributeData['value'] = $faker->ipv4;
564
                $attributeData['type'] = $rule;
565
                break;
566
            default:
567
                $unknownRuleDescription = Description::parse($rule)->getDescription();
568
                if ($unknownRuleDescription) {
569
                    $attributeData['description'][] = $unknownRuleDescription;
570
                }
571
                break;
572
        }
573
574
        if ($attributeData['value'] === '') {
575
            $attributeData['value'] = $faker->word;
576
        }
577
578
        if (is_null($attributeData['type'])) {
579
            $attributeData['type'] = 'string';
580
        }
581
    }
582
583
    /**
584
     * Call the given URI and return the Response.
585
     *
586
     * @param  string  $method
587
     * @param  string  $uri
588
     * @param  array  $parameters
589
     * @param  array  $cookies
590
     * @param  array  $files
591
     * @param  array  $server
592
     * @param  string  $content
593
     *
594
     * @return \Illuminate\Http\Response
595
     */
596
    abstract public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null);
597
598
    /**
599
     * Transform headers array to array of $_SERVER vars with HTTP_* format.
600
     *
601
     * @param  array  $headers
602
     *
603
     * @return array
604
     */
605
    protected function transformHeadersToServerVars(array $headers)
606
    {
607
        $server = [];
608
        $prefix = 'HTTP_';
609
610
        foreach ($headers as $name => $value) {
611
            $name = strtr(strtoupper($name), '-', '_');
612
613
            if (! Str::startsWith($name, $prefix) && $name !== 'CONTENT_TYPE') {
614
                $name = $prefix.$name;
615
            }
616
617
            $server[$name] = $value;
618
        }
619
620
        return $server;
621
    }
622
623
    /**
624
     * Parse a string based rule.
625
     *
626
     * @param  string  $rules
627
     *
628
     * @return array
629
     */
630
    protected function parseStringRule($rules)
631
    {
632
        $parameters = [];
633
634
        // The format for specifying validation rules and parameters follows an
635
        // easy {rule}:{parameters} formatting convention. For instance the
636
        // rule "max:3" states that the value may only be three letters.
637
        if (strpos($rules, ':') !== false) {
638
            list($rules, $parameter) = explode(':', $rules, 2);
639
640
            $parameters = $this->parseParameters($rules, $parameter);
641
        }
642
643
        return [strtolower(trim($rules)), $parameters];
644
    }
645
646
    /**
647
     * Parse a parameter list.
648
     *
649
     * @param  string  $rule
650
     * @param  string  $parameter
651
     *
652
     * @return array
653
     */
654
    protected function parseParameters($rule, $parameter)
655
    {
656
        if (strtolower($rule) === 'regex') {
657
            return [$parameter];
658
        }
659
660
        return str_getcsv($parameter);
661
    }
662
663
    /**
664
     * Normalizes a rule so that we can accept short types.
665
     *
666
     * @param  string $rule
667
     *
668
     * @return string
669
     */
670
    protected function normalizeRule($rule)
671
    {
672
        switch ($rule) {
673
            case 'int':
674
                return 'integer';
675
            case 'bool':
676
                return 'boolean';
677
            default:
678
                return $rule;
679
        }
680
    }
681
682
    /**
683
     * @param $response
684
     *
685
     * @return mixed
686
     */
687
    private function getResponseContent($response)
688
    {
689
        if (empty($response)) {
690
            return '';
691
        }
692
        if ($response->headers->get('Content-Type') === 'application/json') {
693
            $content = json_decode($response->getContent(), JSON_PRETTY_PRINT);
694
        } else {
695
            $content = $response->getContent();
696
        }
697
698
        return $content;
699
    }
700
701
    /**
702
     * Get a response from the transformer tags.
703
     *
704
     * @param array $tags
705
     *
706
     * @return mixed
707
     */
708
    protected function getTransformerResponse($tags)
709
    {
710
        try {
711
            $transFormerTags = array_filter($tags, function ($tag) {
712
                if (! ($tag instanceof Tag)) {
713
                    return false;
714
                }
715
716
                return \in_array(\strtolower($tag->getName()), ['transformer', 'transformercollection']);
717
            });
718
            if (empty($transFormerTags)) {
719
                // we didn't have any of the tags so goodbye
720
                return false;
721
            }
722
723
            $modelTag = array_first(array_filter($tags, function ($tag) {
724
                if (! ($tag instanceof Tag)) {
725
                    return false;
726
                }
727
728
                return \in_array(\strtolower($tag->getName()), ['transformermodel']);
729
            }));
730
            $tag = \array_first($transFormerTags);
731
            $transformer = $tag->getContent();
732
            if (! \class_exists($transformer)) {
733
                // if we can't find the transformer we can't generate a response
734
                return;
735
            }
736
            $demoData = [];
737
738
            $reflection = new ReflectionClass($transformer);
739
            $method = $reflection->getMethod('transform');
740
            $parameter = \array_first($method->getParameters());
741
            $type = null;
742
            if ($modelTag) {
743
                $type = $modelTag->getContent();
744
            }
745
            if (version_compare(PHP_VERSION, '7.0.0') >= 0 && \is_null($type)) {
746
                // we can only get the type with reflection for PHP 7
747
                if ($parameter->hasType() &&
748
                    ! $parameter->getType()->isBuiltin() &&
749
                    \class_exists((string) $parameter->getType())) {
750
                    //we have a type
751
                    $type = (string) $parameter->getType();
752
                }
753
            }
754
            if ($type) {
755
                // we have a class so we try to create an instance
756
                $demoData = new $type;
757
                try {
758
                    // try a factory
759
                    $demoData = \factory($type)->make();
760
                } catch (\Exception $e) {
761
                    if ($demoData instanceof \Illuminate\Database\Eloquent\Model) {
762
                        // we can't use a factory but can try to get one from the database
763
                        try {
764
                            // check if we can find one
765
                            $newDemoData = $type::first();
766
                            if ($newDemoData) {
767
                                $demoData = $newDemoData;
768
                            }
769
                        } catch (\Exception $e) {
770
                            // do nothing
771
                        }
772
                    }
773
                }
774
            }
775
776
            $fractal = new Manager();
777
            $resource = [];
778
            if ($tag->getName() == 'transformer') {
779
                // just one
780
                $resource = new Item($demoData, new $transformer);
781
            }
782
            if ($tag->getName() == 'transformercollection') {
783
                // a collection
784
                $resource = new Collection([$demoData, $demoData], new $transformer);
785
            }
786
787
            return \response($fractal->createData($resource)->toJson());
0 ignored issues
show
Bug introduced by
It seems like $resource defined by array() on line 777 can also be of type array; however, League\Fractal\Manager::createData() does only seem to accept object<League\Fractal\Resource\ResourceInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
788
        } catch (\Exception $e) {
789
            // it isn't possible to parse the transformer
790
            return;
791
        }
792
    }
793
}
794