Completed
Push — master ( f2e556...41a88d )
by
unknown
11s
created

LaravelGenerator::getRouteRules()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.7857
c 0
b 0
f 0
cc 6
nc 5
nop 2
1
<?php
2
3
namespace Mpociot\ApiDoc\Generators;
4
5
use ReflectionClass;
6
use League\Fractal\Manager;
7
use Illuminate\Routing\Route;
8
use League\Fractal\Resource\Item;
9
use Illuminate\Support\Facades\App;
10
use Mpociot\Reflection\DocBlock\Tag;
11
use Illuminate\Support\Facades\Request;
12
use League\Fractal\Resource\Collection;
13
14
class LaravelGenerator extends AbstractGenerator
15
{
16
    /**
17
     * @param Route $route
18
     *
19
     * @return mixed
20
     */
21
    public function getDomain($route)
22
    {
23
        return $route->domain();
24
    }
25
26
    /**
27
     * @param Route $route
28
     *
29
     * @return mixed
30
     */
31
    public function getUri($route)
32
    {
33
        if (version_compare(app()->version(), '5.4', '<')) {
34
            return $route->getUri();
0 ignored issues
show
Bug introduced by
The method getUri() does not seem to exist on object<Illuminate\Routing\Route>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
35
        }
36
37
        return $route->uri();
38
    }
39
40
    /**
41
     * @param Route $route
42
     *
43
     * @return mixed
44
     */
45
    public function getMethods($route)
46
    {
47
        if (version_compare(app()->version(), '5.4', '<')) {
48
            $methods = $route->getMethods();
0 ignored issues
show
Bug introduced by
The method getMethods() does not exist on Illuminate\Routing\Route. Did you maybe mean methods()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
49
        } else {
50
            $methods = $route->methods();
51
        }
52
53
        return array_diff($methods, ['HEAD']);
54
    }
55
56
    /**
57
     * @param  \Illuminate\Routing\Route $route
58
     * @param array $bindings
59
     * @param array $headers
60
     * @param bool $withResponse
61
     *
62
     * @return array
63
     */
64
    public function processRoute($route, $bindings = [], $headers = [], $withResponse = true)
65
    {
66
        $content = '';
67
68
        $routeDomain = $route->domain();
69
        $routeAction = $route->getAction();
70
        $routeGroup = $this->getRouteGroup($routeAction['uses']);
71
        $routeDescription = $this->getRouteDescription($routeAction['uses']);
72
        $showresponse = null;
73
74
        // set correct route domain
75
        $headers[] = "HTTP_HOST: {$routeDomain}";
76
        $headers[] = "SERVER_NAME: {$routeDomain}";
77
78
        if ($withResponse) {
79
            $response = null;
80
            $docblockResponse = $this->getDocblockResponse($routeDescription['tags']);
81
            if ($docblockResponse) {
82
                // we have a response from the docblock ( @response )
83
                $response = $docblockResponse;
84
                $showresponse = true;
85
            }
86
            if (! $response) {
87
                $transformerResponse = $this->getTransformerResponse($routeDescription['tags']);
88
                if ($transformerResponse) {
89
                    // we have a transformer response from the docblock ( @transformer || @transformercollection )
90
                    $response = $transformerResponse;
91
                    $showresponse = true;
92
                }
93
            }
94
            if (! $response) {
95
                $response = $this->getRouteResponse($route, $bindings, $headers);
96
            }
97
            if ($response->headers->get('Content-Type') === 'application/json') {
98
                $content = json_decode($response->getContent(), JSON_PRETTY_PRINT);
99
            } else {
100
                $content = $response->getContent();
101
            }
102
        }
103
104
        return $this->getParameters([
105
            'id' => md5($this->getUri($route).':'.implode($this->getMethods($route))),
106
            'resource' => $routeGroup,
107
            'title' => $routeDescription['short'],
108
            'description' => $routeDescription['long'],
109
            'methods' => $this->getMethods($route),
110
            'uri' => $this->getUri($route),
111
            'parameters' => [],
112
            'response' => $content,
113
            'showresponse' => $showresponse,
114
        ], $routeAction, $bindings);
115
    }
116
117
    /**
118
     * Prepares / Disables route middlewares.
119
     *
120
     * @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...
121
     *
122
     * @return  void
123
     */
124
    public function prepareMiddleware($enable = true)
125
    {
126
        App::instance('middleware.disable', ! $enable);
0 ignored issues
show
Bug introduced by
The method instance() does not exist on Illuminate\Support\Facades\App. Did you maybe mean clearResolvedInstance()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
127
    }
128
129
    /**
130
     * Call the given URI and return the Response.
131
     *
132
     * @param  string  $method
133
     * @param  string  $uri
134
     * @param  array  $parameters
135
     * @param  array  $cookies
136
     * @param  array  $files
137
     * @param  array  $server
138
     * @param  string  $content
139
     *
140
     * @return \Illuminate\Http\Response
141
     */
142
    public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
143
    {
144
        $server = collect([
145
            'CONTENT_TYPE' => 'application/json',
146
            'Accept' => 'application/json',
147
        ])->merge($server)->toArray();
148
149
        $request = Request::create(
0 ignored issues
show
Bug introduced by
The method create() does not exist on Illuminate\Support\Facades\Request. Did you maybe mean createFreshMockInstance()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
150
            $uri, $method, $parameters,
151
            $cookies, $files, $this->transformHeadersToServerVars($server), $content
152
        );
153
154
        $kernel = App::make('Illuminate\Contracts\Http\Kernel');
155
        $response = $kernel->handle($request);
156
157
        $kernel->terminate($request, $response);
158
159
        return $response;
160
    }
161
162
    /**
163
     * Get a response from the transformer tags.
164
     *
165
     * @param array $tags
166
     *
167
     * @return mixed
168
     */
169
    protected function getTransformerResponse($tags)
170
    {
171
        try {
172
            $transFormerTags = array_filter($tags, function ($tag) {
173
                if (! ($tag instanceof Tag)) {
174
                    return false;
175
                }
176
177
                return \in_array(\strtolower($tag->getName()), ['transformer', 'transformercollection']);
178
            });
179
            if (empty($transFormerTags)) {
180
                // we didn't have any of the tags so goodbye
181
                return false;
182
            }
183
184
            $modelTag = array_first(array_filter($tags, function ($tag) {
185
                if (! ($tag instanceof Tag)) {
186
                    return false;
187
                }
188
189
                return \in_array(\strtolower($tag->getName()), ['transformermodel']);
190
            }));
191
            $tag = \array_first($transFormerTags);
192
            $transformer = $tag->getContent();
193
            if (! \class_exists($transformer)) {
194
                // if we can't find the transformer we can't generate a response
195
                return;
196
            }
197
            $demoData = [];
198
199
            $reflection = new ReflectionClass($transformer);
200
            $method = $reflection->getMethod('transform');
201
            $parameter = \array_first($method->getParameters());
202
            $type = null;
203
            if ($modelTag) {
204
                $type = $modelTag->getContent();
205
            }
206
            if (version_compare(PHP_VERSION, '7.0.0') >= 0 && \is_null($type)) {
207
                // we can only get the type with reflection for PHP 7
208
                if ($parameter->hasType() &&
209
                ! $parameter->getType()->isBuiltin() &&
210
                \class_exists((string) $parameter->getType())) {
211
                    //we have a type
212
                    $type = (string) $parameter->getType();
213
                }
214
            }
215
            if ($type) {
216
                // we have a class so we try to create an instance
217
                $demoData = new $type;
218
                try {
219
                    // try a factory
220
                    $demoData = \factory($type)->make();
221
                } catch (\Exception $e) {
222
                    if ($demoData instanceof \Illuminate\Database\Eloquent\Model) {
223
                        // we can't use a factory but can try to get one from the database
224
                        try {
225
                            // check if we can find one
226
                            $newDemoData = $type::first();
227
                            if ($newDemoData) {
228
                                $demoData = $newDemoData;
229
                            }
230
                        } catch (\Exception $e) {
231
                            // do nothing
232
                        }
233
                    }
234
                }
235
            }
236
237
            $fractal = new Manager();
238
            $resource = [];
239
            if ($tag->getName() == 'transformer') {
240
                // just one
241
                $resource = new Item($demoData, new $transformer);
242
            }
243
            if ($tag->getName() == 'transformercollection') {
244
                // a collection
245
                $resource = new Collection([$demoData, $demoData], new $transformer);
246
            }
247
248
            return \response($fractal->createData($resource)->toJson());
0 ignored issues
show
Bug introduced by
It seems like $resource defined by array() on line 238 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...
249
        } catch (\Exception $e) {
250
            // it isn't possible to parse the transformer
251
            return;
252
        }
253
    }
254
}
255