Completed
Push — master ( db46be...6b1c50 )
by Marcel
02:26
created

LaravelGenerator::getMethods()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 8
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
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
use Illuminate\Foundation\Http\FormRequest;
14
15
class LaravelGenerator extends AbstractGenerator
16
{
17
    /**
18
     * @param Route $route
19
     *
20
     * @return mixed
21
     */
22
    public function getUri($route)
23
    {
24
        if (version_compare(app()->version(), '5.4', '<')) {
25
            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...
26
        }
27
28
        return $route->uri();
29
    }
30
31
    /**
32
     * @param Route $route
33
     *
34
     * @return mixed
35
     */
36
    public function getMethods($route)
37
    {
38
        if (version_compare(app()->version(), '5.4', '<')) {
39
            return $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...
40
        }
41
42
        return $route->methods();
43
    }
44
45
    /**
46
     * @param  \Illuminate\Routing\Route $route
47
     * @param array $bindings
48
     * @param array $headers
49
     * @param bool $withResponse
50
     *
51
     * @return array
52
     */
53
    public function processRoute($route, $bindings = [], $headers = [], $withResponse = true)
54
    {
55
        $content = '';
56
57
        $routeAction = $route->getAction();
58
        $routeGroup = $this->getRouteGroup($routeAction['uses']);
59
        $routeDescription = $this->getRouteDescription($routeAction['uses']);
60
        $showresponse = null;
61
62
        if ($withResponse) {
63
            $response = null;
64
            $docblockResponse = $this->getDocblockResponse($routeDescription['tags']);
65
            if ($docblockResponse) {
66
                // we have a response from the docblock ( @response )
67
                $response = $docblockResponse;
68
                $showresponse = true;
69
            }
70
            if (! $response) {
71
                $transformerResponse = $this->getTransformerResponse($routeDescription['tags']);
72
                if ($transformerResponse) {
73
                    // we have a transformer response from the docblock ( @transformer || @transformercollection )
74
                    $response = $transformerResponse;
75
                    $showresponse = true;
76
                }
77
            }
78
            if (! $response) {
79
                $response = $this->getRouteResponse($route, $bindings, $headers);
80
            }
81
            if ($response->headers->get('Content-Type') === 'application/json') {
82
                $content = json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT);
83
            } else {
84
                $content = $response->getContent();
85
            }
86
        }
87
88
        return $this->getParameters([
89
            'id' => md5($this->getUri($route).':'.implode($this->getMethods($route))),
90
            'resource' => $routeGroup,
91
            'title' => $routeDescription['short'],
92
            'description' => $routeDescription['long'],
93
            'methods' => $this->getMethods($route),
94
            'uri' => $this->getUri($route),
95
            'parameters' => [],
96
            'response' => $content,
97
            'showresponse' => $showresponse,
98
        ], $routeAction, $bindings);
99
    }
100
101
    /**
102
     * Prepares / Disables route middlewares.
103
     *
104
     * @param  bool $disable
105
     *
106
     * @return  void
107
     */
108
    public function prepareMiddleware($disable = true)
109
    {
110
        App::instance('middleware.disable', true);
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...
111
    }
112
113
    /**
114
     * Call the given URI and return the Response.
115
     *
116
     * @param  string  $method
117
     * @param  string  $uri
118
     * @param  array  $parameters
119
     * @param  array  $cookies
120
     * @param  array  $files
121
     * @param  array  $server
122
     * @param  string  $content
123
     *
124
     * @return \Illuminate\Http\Response
125
     */
126
    public function callRoute($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
127
    {
128
        $server = collect([
129
            'CONTENT_TYPE' => 'application/json',
130
            'Accept' => 'application/json',
131
        ])->merge($server)->toArray();
132
133
        $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...
134
            $uri, $method, $parameters,
135
            $cookies, $files, $this->transformHeadersToServerVars($server), $content
136
        );
137
138
        $kernel = App::make('Illuminate\Contracts\Http\Kernel');
139
        $response = $kernel->handle($request);
140
141
        $kernel->terminate($request, $response);
142
143
        if (file_exists($file = App::bootstrapPath().'/app.php')) {
144
            $app = require $file;
145
            $app->make('Illuminate\Contracts\Console\Kernel')->bootstrap();
146
        }
147
148
        return $response;
149
    }
150
151
    /**
152
     * Get a response from the transformer tags.
153
     *
154
     * @param array $tags
155
     *
156
     * @return mixed
157
     */
158
    protected function getTransformerResponse($tags)
159
    {
160
        try {
161
            $transFormerTags = array_filter($tags, function ($tag) {
162
                if (! ($tag instanceof Tag)) {
163
                    return false;
164
                }
165
166
                return \in_array(\strtolower($tag->getName()), ['transformer', 'transformercollection']);
167
            });
168
            if (empty($transFormerTags)) {
169
                // we didn't have any of the tags so goodbye
170
                return false;
171
            }
172
173
            $modelTag = array_first(array_filter($tags, function ($tag) {
174
                if (! ($tag instanceof Tag)) {
175
                    return false;
176
                }
177
178
                return \in_array(\strtolower($tag->getName()), ['transformermodel']);
179
            }));
180
            $tag = \array_first($transFormerTags);
181
            $transformer = $tag->getContent();
182
            if (! \class_exists($transformer)) {
183
                // if we can't find the transformer we can't generate a response
184
                return;
185
            }
186
            $demoData = [];
187
188
            $reflection = new ReflectionClass($transformer);
189
            $method = $reflection->getMethod('transform');
190
            $parameter = \array_first($method->getParameters());
191
            $type = null;
192
            if ($modelTag) {
193
                $type = $modelTag->getContent();
194
            }
195
            if (version_compare(PHP_VERSION, '7.0.0') >= 0 && \is_null($type)) {
196
                // we can only get the type with reflection for PHP 7
197
                if ($parameter->hasType() &&
198
                ! $parameter->getType()->isBuiltin() &&
199
                \class_exists((string) $parameter->getType())) {
200
                    //we have a type
201
                    $type = (string) $parameter->getType();
202
                }
203
            }
204
            if ($type) {
205
                // we have a class so we try to create an instance
206
                $demoData = new $type;
207
                try {
208
                    // try a factory
209
                    $demoData = \factory($type)->make();
210
                } catch (\Exception $e) {
211
                    if ($demoData instanceof \Illuminate\Database\Eloquent\Model) {
212
                        // we can't use a factory but can try to get one from the database
213
                        try {
214
                            // check if we can find one
215
                            $newDemoData = $type::first();
216
                            if ($newDemoData) {
217
                                $demoData = $newDemoData;
218
                            }
219
                        } catch (\Exception $e) {
220
                            // do nothing
221
                        }
222
                    }
223
                }
224
            }
225
226
            $fractal = new Manager();
227
            $resource = [];
228
            if ($tag->getName() == 'transformer') {
229
                // just one
230
                $resource = new Item($demoData, new $transformer);
231
            }
232
            if ($tag->getName() == 'transformercollection') {
233
                // a collection
234
                $resource = new Collection([$demoData, $demoData], new $transformer);
235
            }
236
237
            return \response($fractal->createData($resource)->toJson());
0 ignored issues
show
Bug introduced by
It seems like $resource defined by array() on line 227 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...
238
        } catch (\Exception $e) {
239
            // it isn't possible to parse the transformer
240
            return;
241
        }
242
    }
243
244
    /**
245
     * @param  string $route
246
     * @param  array $bindings
247
     *
248
     * @return array
249
     */
250
    protected function getRouteRules($route, $bindings)
251
    {
252
        list($class, $method) = explode('@', $route);
253
        $reflection = new ReflectionClass($class);
254
        $reflectionMethod = $reflection->getMethod($method);
255
256
        foreach ($reflectionMethod->getParameters() as $parameter) {
257
            $parameterType = $parameter->getClass();
258
            if (! is_null($parameterType) && class_exists($parameterType->name)) {
259
                $className = $parameterType->name;
260
261
                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...
262
                    $parameterReflection = new $className;
263
                    $parameterReflection->setContainer(app());
264
                    // Add route parameter bindings
265
                    $parameterReflection->query->add($bindings);
266
                    $parameterReflection->request->add($bindings);
267
268
                    if (method_exists($parameterReflection, 'validator')) {
269
                        return app()->call([$parameterReflection, 'validator'])
270
                            ->getRules();
271
                    } else {
272
                        return app()->call([$parameterReflection, 'rules']);
273
                    }
274
                }
275
            }
276
        }
277
278
        return [];
279
    }
280
}
281