Passed
Push — master ( 5ae195...c1faee )
by Nícollas
02:35 queued 56s
created

RouteCollection::dispatchRoute()   A

Complexity

Conditions 5
Paths 10

Size

Total Lines 53
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 5
eloc 32
nc 10
nop 0
dl 0
loc 53
rs 9.0968
c 2
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace MinasRouter\Router;
4
5
use MinasRouter\Router\RouteGroups;
6
use MinasRouter\Traits\RouterHelpers;
7
use MinasRouter\Traits\RouteManagement;
8
use MinasRouter\Exceptions\NotFoundException;
9
use MinasRouter\Exceptions\BadMethodCallException;
10
use MinasRouter\Exceptions\MethodNotAllowedException;
11
use MinasRouter\Router\Middlewares\MiddlewareCollection;
12
use MinasRouter\Exceptions\BadMiddlewareExecuteException;
13
14
class RouteCollection
15
{
16
    use RouteManagement, RouterHelpers;
0 ignored issues
show
Bug introduced by
The trait MinasRouter\Traits\RouterHelpers requires the property $prefix which is not provided by MinasRouter\Router\RouteCollection.
Loading history...
17
18
    protected $actionSeparator;
19
20
    protected $currentUri;
21
22
    protected $currentGroup;
23
24
    protected $baseUrl;
25
26
    protected $currentRoute;
27
28
    protected $requestMethod;
29
30
    protected $httpCodes = [
31
        "badRequest" => 400,
32
        "notAllowed" => 403,
33
        "notFound" => 404,
34
        "methodNotAllowed" => 405,
35
        "notImplemented" => 501,
36
        "redirect" => 302
37
    ];
38
39
    protected $routes = [
40
        "GET" => [],
41
        "POST" => [],
42
        "PUT" => [],
43
        "PATCH" => [],
44
        "DELETE" => [],
45
        "REDIRECT" => []
46
    ];
47
48
    public function __construct(String $separator, String $baseUrl)
49
    {
50
        $this->actionSeparator = $separator;
51
        $this->baseUrl = $baseUrl;
52
        $this->currentUri = filter_input(INPUT_GET, "route", FILTER_DEFAULT) ?? "/";
53
    }
54
55
    /**
56
     * Method responsible for defining the 
57
     * group of current routes.
58
     * 
59
     * @param null|\MinasRouter\Router\RouteGroups $group = null
60
     * 
61
     * @return void
62
     */
63
    public function defineGroup(?RouteGroups $group = null): void
64
    {
65
        $this->currentGroup = $group;
66
    }
67
68
    /**
69
     * Method responsible for adding a
70
     * route to an http method.
71
     * 
72
     * @param string $method
73
     * @param string $uri
74
     * @param array|string|\Closure $callback
75
     * 
76
     * @return \MinasRouter\Router\RouteManager
77
     */
78
    public function addRoute(String $method, $uri, $callback)
79
    {
80
        $uri = $this->resolveRouterUri($uri);
81
82
        if (array_key_exists($method, $this->routes)) {
83
            return $this->routes[$method][$uri] = $this->addRouter($uri, $callback);
84
        }
85
    }
86
87
    /**
88
     * Method responsible for adding the same
89
     * route in more than one http method.
90
     * 
91
     * @param string $uri
92
     * @param array|string|\Closure $callback
93
     * @param null|array $methods
94
     * 
95
     * @return \MinasRouter\Router\RouteManager
96
     */
97
    public function addMultipleHttpRoutes(String $uri, $callback, ?array $methods = null)
98
    {
99
        if (!$methods) {
100
            $methods = array_keys($this->routes);
101
        }
102
103
        $methods = array_map("strtoupper", $methods);
104
105
        array_map(function ($method) use ($uri, $callback) {
106
            $this->routes[$method][$uri] = $this->addRouter($uri, $callback);
107
        }, $methods);
108
    }
109
110
    /**
111
     * Method responsible for adding a redirect route.
112
     * 
113
     * @param string $uri
114
     * @param string $redirect
115
     * @param int $httpCode
116
     * 
117
     * @return void
118
     */
119
    public function addRedirectRoute(String $uri, String $redirect, Int $httpCode): void
120
    {
121
        $uri = $this->resolveRouterUri($uri);
122
123
        $this->routes["REDIRECT"][$uri] = $this->redirectRouterData($redirect, $httpCode);
124
    }
125
126
    /**
127
     * Method responsible for handling method
128
     * calls that do not exist in the class.
129
     * 
130
     * @param string $method
131
     * @param array $arguments
132
     * 
133
     * @return void
134
     */
135
    public function __call($method, $arguments)
136
    {
137
        $this->throwException(
138
            "badRequest",
139
            BadMethodCallException::class,
140
            "Method [%s::%s] doesn't exist.",
141
            static::class,
142
            $method
143
        );
144
    }
145
146
    /**
147
     * Method responsible for returning a route
148
     * by the name attribute.
149
     * 
150
     * @param string $routeName
151
     * @param null|string $httpMethod = null
152
     * 
153
     * @return \MinasRouter\Router\RouteManager|null
154
     */
155
    public function getByName(String $routeName, $httpMethod = null): ?RouteManager
156
    {
157
        $routes = $this->routes;
158
        $httpMethod = !$httpMethod ?: strtoupper($httpMethod);
159
160
        unset($routes["REDIRECT"]);
161
162
        if ($httpMethod && isset($this->routes[$httpMethod])) {
163
            $routes = $this->routes[$httpMethod];
164
        }
165
166
        if (!is_array($routes)) return null;
167
168
        $soughtRoute = null;
169
170
        foreach ($routes as $verb) {
171
            if (!$this->instanceOf($verb, RouteManager::class)) {
172
                foreach ($verb as $route) {
173
                    if ($route->getName() === $routeName) {
174
                        $soughtRoute = $route;
175
                        break;
176
                    }
177
                }
178
            } else {
179
                if ($verb->getName() === $routeName) {
180
                    $soughtRoute = $verb;
181
                    break;
182
                }
183
            }
184
        }
185
186
        return $soughtRoute;
187
    }
188
189
    /**
190
     * Method responsible for verifying if the
191
     * object is an instance of class.
192
     * 
193
     * @param mixed $object
194
     * 
195
     * @return bool
196
     */
197
    protected function instanceOf($object, $class)
198
    {
199
        return is_a($object, $class);
200
    }
201
202
    /**
203
     * Method responsible for redirecting to an
204
     * existing route or a uri.
205
     * 
206
     * @param object|array $route
207
     * @param bool $permanent = false
208
     */
209
    protected function redirectRoute(array $routes, $permanent = false)
210
    {
211
        $redirectRoute = $this->baseUrl;
212
213
        [$routeObject, $route] = $routes;
214
215
        if ($this->instanceOf($routeObject, RouteManager::class)) {
216
            $redirectRoute .= rtrim($routeObject->getRoute(), '(\/)?');
217
        } else {
218
            $redirectRoute .= $this->resolveRouterUri($route["redirect"]);
219
        }
220
221
        header("Location: {$redirectRoute}", true, $permanent ? 301 : $route["httpCode"]);
222
        exit();
223
    }
224
225
    /**
226
     * Method responsible for formSpoofing the
227
     * HTTP verbs coming from the form.
228
     * 
229
     * @return null|void
230
     */
231
    protected function resolveRequestMethod()
232
    {
233
        $method = $_SERVER["REQUEST_METHOD"];
234
235
        if ($method != "GET" && isset($_POST["_method"])) {
236
            $this->requestMethod = $_POST["_method"];
237
            return null;
238
        }
239
240
        $this->requestMethod = $method;
241
    }
242
243
    /**
244
     * Method responsible for listening to browser calls
245
     * and returning the corresponding route.
246
     * 
247
     * @return void
248
     */
249
    public function run(): void
250
    {
251
        $this->currentRoute = null;
252
253
        if (array_key_exists($currentRoute = $this->resolveRouterUri($this->currentUri), $this->routes["REDIRECT"])) {
254
            $route = $this->routes["REDIRECT"][$currentRoute];
255
            $redirectRoute = $this->getByName($route["redirect"]);
256
257
            $this->redirectRoute(
258
                [$redirectRoute, $route],
259
                $route["permanent"]
260
            );
261
        }
262
263
        $this->resolveRequestMethod();
264
265
        foreach ($this->routes[$this->requestMethod] as $route) {
266
            if (preg_match("~^" . $route->getRoute() . "$~", $this->currentUri)) {
267
                $this->currentRoute = $route;
268
            }
269
        }
270
271
        $this->dispatchRoute();
272
    }
273
274
    /**
275
     * Method responsible for performing
276
     * route actions.
277
     * 
278
     * @return null|\Closure
279
     */
280
    protected function dispatchRoute(): ?\Closure
281
    {
282
        if (!$route = $this->currentRoute) {
283
            $this->setHttpCode($this->httpCodes["notFound"]);
284
285
            $this->throwException(
286
                "notFound",
287
                NotFoundException::class,
288
                "Route [%s] with method [%s] not found.",
289
                $_SERVER["REQUEST_URI"],
290
                $this->requestMethod
291
            );
292
        }
293
294
        $this->executeMiddlewares($route);
295
296
        [$controller, $method] = $route->getCompleteAction();
297
298
        if ($this->instanceOf($method, \Closure::class)) {
299
            $this->setHttpCode();
300
301
            return call_user_func($route->getAction(), ...$route->closureReturn());
302
        }
303
304
        if (!class_exists($controller)) {
305
            $this->setHttpCode($this->httpCodes["badRequest"]);
306
307
            $this->throwException(
308
                "badRequest",
309
                BadMethodCallException::class,
310
                "Class [%s::%s] doesn't exist.",
311
                $controller,
312
                $method
313
            );
314
        }
315
316
        $obController = new $controller;
317
318
        if (!method_exists($obController, $method)) {
319
            $this->setHttpCode($this->httpCodes["methodNotAllowed"]);
320
321
            $this->throwException(
322
                "methodNotAllowed",
323
                MethodNotAllowedException::class,
324
                "Method [%s::%s] doesn't exist.",
325
                $controller,
326
                $method
327
            );
328
        }
329
330
        $obController->{$method}(...$route->closureReturn());
331
332
        return null;
333
    }
334
335
    /**
336
     * Method responsible for executing
337
     * the middlewares of the current route.
338
     * 
339
     * @return void
340
     */
341
    protected function executeMiddlewares(RouteManager $route)
342
    {
343
        if ($this->instanceOf($route->getMiddleware(), MiddlewareCollection::class)) {
344
345
            $route->getMiddleware()->setRequest($route->request());
346
347
            if (!$route->getMiddleware()->execute()) {
348
                $this->setHttpCode($this->httpCodes["notFound"]);
349
350
                $this->throwException(
351
                    "notFound",
352
                    BadMiddlewareExecuteException::class,
353
                    "Some middleware has not approved your request."
354
                );
355
            }
356
        }
357
    }
358
359
    /**
360
     * Method responsible for returning an
361
     * http method by slug.
362
     * 
363
     * @param string $slug
364
     * 
365
     * @return null|int
366
     */
367
    protected function getHttpCode(String $slug)
368
    {
369
        if (!isset($this->httpCodes[$slug])) return null;
370
371
        return $this->httpCodes[$slug];
372
    }
373
374
    /**
375
     * Method responsible for rendering
376
     * the http method on the page.
377
     * 
378
     * @param int $code = 200
379
     * 
380
     * @return void
381
     */
382
    protected function setHttpCode(Int $code = 200)
383
    {
384
        http_response_code($code);
385
    }
386
387
    /**
388
     * Method responsible for returning all routes
389
     * from the http method passed in the parameter.
390
     * 
391
     * @param string $method
392
     * 
393
     * @return null|array
394
     */
395
    public function getRoutesOf(String $method)
396
    {
397
        $method = strtoupper($method);
398
399
        if (!isset($this->routes[$method])) return null;
400
401
        return $this->routes[$method];
402
    }
403
}
404