1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @author Marwan Al-Soltany <[email protected]> |
5
|
|
|
* @copyright Marwan Al-Soltany 2021 |
6
|
|
|
* For the full copyright and license information, please view |
7
|
|
|
* the LICENSE file that was distributed with this source code. |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
declare(strict_types=1); |
11
|
|
|
|
12
|
|
|
namespace MAKS\Velox\Backend; |
13
|
|
|
|
14
|
|
|
use MAKS\Velox\App; |
15
|
|
|
use MAKS\Velox\Backend\Config; |
16
|
|
|
use MAKS\Velox\Backend\Globals; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* A class that serves as a router and an entry point for the application. |
20
|
|
|
* |
21
|
|
|
* Example: |
22
|
|
|
* ``` |
23
|
|
|
* // register a middleware |
24
|
|
|
* Router::middleware('/pages/{pageId}', function ($path, $match, $previous) { |
25
|
|
|
* return 'I am working as expected!'; |
26
|
|
|
* }, 'POST'); |
27
|
|
|
* |
28
|
|
|
* // register a route handler |
29
|
|
|
* Router::route('/pages/{pageId}', function ($path, $match, $previous) { |
30
|
|
|
* return sprintf('Hi from "%s" handler, Page ID is: %s, also the middleware said: %s', $path, $match, $previous ?? 'Nothing!'); |
31
|
|
|
* }, ['GET', 'POST']); |
32
|
|
|
* |
33
|
|
|
* // register a route handler using an HTTP verb |
34
|
|
|
* Router::get('/another-page', function () { |
35
|
|
|
* return View::render('another-page'); |
36
|
|
|
* }); |
37
|
|
|
* |
38
|
|
|
* // register handler for 404 |
39
|
|
|
* Router::handleRouteNotFound(function ($path) { |
40
|
|
|
* // forward the request to some route. |
41
|
|
|
* Router::forward('/'); |
42
|
|
|
* }); |
43
|
|
|
* |
44
|
|
|
* // register handler for 405 |
45
|
|
|
* Router::handleMethodNotAllowed(function ($path, $method) { |
46
|
|
|
* // redirect the request to some URL. |
47
|
|
|
* Router::redirect('/some-page'); |
48
|
|
|
* }); |
49
|
|
|
* |
50
|
|
|
* // start the application |
51
|
|
|
* Router::start(); |
52
|
|
|
* ``` |
53
|
|
|
* |
54
|
|
|
* @method static self get(string $expression, callable $handler) |
55
|
|
|
* @method static self head(string $expression, callable $handler) |
56
|
|
|
* @method static self post(string $expression, callable $handler) |
57
|
|
|
* @method static self put(string $expression, callable $handler) |
58
|
|
|
* @method static self patch(string $expression, callable $handler) |
59
|
|
|
* @method static self delete(string $expression, callable $handler) |
60
|
|
|
* @method static self connect(string $expression, callable $handler) |
61
|
|
|
* @method static self options(string $expression, callable $handler) |
62
|
|
|
* @method static self trace(string $expression, callable $handler) |
63
|
|
|
* @method static self any(string $expression, callable $handler) |
64
|
|
|
* |
65
|
|
|
* @since 1.0.0 |
66
|
|
|
* @api |
67
|
|
|
*/ |
68
|
|
|
class Router |
69
|
|
|
{ |
70
|
|
|
/** |
71
|
|
|
* The default values of class parameters. |
72
|
|
|
* |
73
|
|
|
* @var array |
74
|
|
|
*/ |
75
|
|
|
public const DEFAULTS = [ |
76
|
|
|
'base' => '/', |
77
|
|
|
'allowMultiMatch' => true, |
78
|
|
|
'caseMatters' => false, |
79
|
|
|
'slashMatters' => true, |
80
|
|
|
]; |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Supported HTTP methods. |
84
|
|
|
* |
85
|
|
|
* @var array |
86
|
|
|
*/ |
87
|
|
|
public const SUPPORTED_METHODS = [ |
88
|
|
|
'GET', |
89
|
|
|
'HEAD', |
90
|
|
|
'POST', |
91
|
|
|
'PUT', |
92
|
|
|
'PATCH', |
93
|
|
|
'DELETE', |
94
|
|
|
'CONNECT', |
95
|
|
|
'OPTIONS', |
96
|
|
|
'TRACE' |
97
|
|
|
]; |
98
|
|
|
|
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* The parameters the application started with. |
102
|
|
|
*/ |
103
|
|
|
private static ?array $params = null; |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* The current base URL of the application. |
107
|
|
|
*/ |
108
|
|
|
protected static ?string $base = null; |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* The currently requested path. |
112
|
|
|
*/ |
113
|
|
|
protected static ?string $path = null; |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* The currently registered routes. |
117
|
|
|
*/ |
118
|
|
|
protected static array $routes = []; |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* @var callable|null |
122
|
|
|
*/ |
123
|
|
|
protected static $routeNotFoundCallback = null; |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @var callable|null |
127
|
|
|
*/ |
128
|
|
|
protected static $methodNotAllowedCallback = null; |
129
|
|
|
|
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Registers a route. |
133
|
|
|
* |
134
|
|
|
* @param string $type |
135
|
|
|
* @param string $expression |
136
|
|
|
* @param callable $handler |
137
|
|
|
* @param array $arguments |
138
|
|
|
* @param string|array $method |
139
|
|
|
* |
140
|
|
|
* @return void |
141
|
|
|
*/ |
142
|
6 |
|
private static function registerRoute(string $type, string $expression, callable $handler, array $arguments, $method) |
143
|
|
|
{ |
144
|
|
|
$route = [ |
145
|
6 |
|
'type' => $type, |
146
|
6 |
|
'expression' => $expression, |
147
|
6 |
|
'handler' => $handler, |
148
|
6 |
|
'arguments' => $arguments, |
149
|
6 |
|
'method' => $method |
150
|
|
|
]; |
151
|
|
|
|
152
|
6 |
|
static::$routes[] = &$route; |
153
|
|
|
|
154
|
6 |
|
return new static(); |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Registers a handler for a route. |
159
|
|
|
* |
160
|
|
|
* @param string $expression A route like `/page`, `/page/{id}` (`id` is required), or `/page/{id?}` (`id` is optional). For more flexibility, pass en expression like `/page/([\d]+|[0-9]*)` (regex capture group). |
161
|
|
|
* @param callable $handler A function to call if route has matched. It will be passed the current `$path`, the `$match` or `...$match` from the expression if there was any, and lastly the `$previous` result (the return of the last middleware or route with a matching expression) if `$allowMultiMatch` is set to `true`. |
162
|
|
|
* @param string|string[] $method [optional] Either a string or an array of the allowed method. |
163
|
|
|
* |
164
|
|
|
* @return static |
165
|
|
|
*/ |
166
|
6 |
|
public static function handle(string $expression, callable $handler, $method = 'GET') |
167
|
|
|
{ |
168
|
6 |
|
return static::registerRoute('handler', $expression, $handler, [], $method); |
|
|
|
|
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Registers a middleware for a route. This method has no effect if `$allowMultiMatch` is set to `false`. |
173
|
|
|
* Note that middlewares must be registered before routes in order to work correctly. |
174
|
|
|
* This method is just an alias for `self::handle()`. |
175
|
|
|
* |
176
|
|
|
* @param string $expression A route like `/page`, `/page/{id}` (`id` is required), or `/page/{id?}` (`id` is optional). For more flexibility, pass en expression like `/page/([\d]+|[0-9]*)` (regex capture group). |
177
|
|
|
* @param callable $handler A function to call if route has matched. It will be passed the current `$path`, the `$match` or `...$match` from the expression if there was any, and lastly the `$previous` result (the return of the last middleware or route with a matching expression) if `$allowMultiMatch` is set to `true`. |
178
|
|
|
* @param string|string[] $method [optional] Either a string or an array of the allowed method. |
179
|
|
|
* |
180
|
|
|
* @return static |
181
|
|
|
*/ |
182
|
1 |
|
public static function middleware(string $expression, callable $handler, $method = 'GET') |
183
|
|
|
{ |
184
|
1 |
|
return static::registerRoute('middleware', $expression, $handler, [], $method); |
|
|
|
|
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* Redirects the request to another route. |
189
|
|
|
* Note that this function will exit the script (code that comes after it will not be executed). |
190
|
|
|
* |
191
|
|
|
* @param string $to A route like `/page` or a URL like `http://domain.tld`. |
192
|
|
|
* |
193
|
|
|
* @return void |
194
|
|
|
*/ |
195
|
2 |
|
public static function redirect(string $to): void |
196
|
|
|
{ |
197
|
2 |
|
if (filter_var($to, FILTER_VALIDATE_URL)) { |
198
|
1 |
|
$header = sprintf('Location: %s', $to); |
199
|
|
|
} else { |
200
|
1 |
|
$scheme = Globals::getServer('HTTPS') == 'on' ? 'https' : 'http'; |
201
|
1 |
|
$host = Globals::getServer('HTTP_HOST'); |
202
|
1 |
|
$path = static::$base . '/' . $to; |
203
|
1 |
|
$path = trim(preg_replace('/(\/+)/', '/', $path), '/'); |
204
|
|
|
|
205
|
1 |
|
$header = sprintf('Location: %s://%s/%s', $scheme, $host, $path); |
206
|
|
|
} |
207
|
|
|
|
208
|
2 |
|
header($header, true, 302); |
209
|
|
|
|
210
|
|
|
exit; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Forwards the request to another route. |
215
|
|
|
* Note that this function will exit the script (code that comes after it will not be executed). |
216
|
|
|
* |
217
|
|
|
* @param string $to A route like `/page`. |
218
|
|
|
* |
219
|
|
|
* @return void |
220
|
|
|
*/ |
221
|
1 |
|
public static function forward(string $to): void |
222
|
|
|
{ |
223
|
1 |
|
$base = static::$base ?? ''; |
224
|
1 |
|
$path = trim($base, '/') . '/' . ltrim($to, '/'); |
225
|
|
|
|
226
|
1 |
|
Globals::setServer('REQUEST_URI', $path); |
227
|
|
|
|
228
|
1 |
|
static::start(...self::$params); |
229
|
|
|
|
230
|
|
|
exit; |
|
|
|
|
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* Registers 404 handler. |
235
|
|
|
* |
236
|
|
|
* @param callable $handler The handler to use. It will be passed the current `$path` and the current `$method`. |
237
|
|
|
* |
238
|
|
|
* @return static |
239
|
|
|
*/ |
240
|
1 |
|
public static function handleRouteNotFound(callable $handler) |
241
|
|
|
{ |
242
|
1 |
|
static::$routeNotFoundCallback = $handler; |
243
|
|
|
|
244
|
1 |
|
return new static(); |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Registers 405 handler. |
249
|
|
|
* |
250
|
|
|
* @param callable $handler The handler to use. It will be passed the current `$path`. |
251
|
|
|
* |
252
|
|
|
* @return static |
253
|
|
|
*/ |
254
|
1 |
|
public static function handleMethodNotAllowed(callable $handler) |
255
|
|
|
{ |
256
|
1 |
|
static::$methodNotAllowedCallback = $handler; |
257
|
|
|
|
258
|
1 |
|
return new static(); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Starts the router. |
263
|
|
|
* |
264
|
|
|
* @param string|null [optional] $base App base path, this will prefix all routes. |
265
|
|
|
* @param bool|null [optional] $allowMultiMatch Whether the router should execute handlers of all matches. Useful to make middleware-like functionality, the first match will act as a middleware. |
266
|
|
|
* @param bool|null [optional] $caseMatters Whether the route matching should be case sensitive or not. |
267
|
|
|
* @param bool|null [optional] $slashMatters Whether trailing slash should be taken in consideration with route matching or not. |
268
|
|
|
* |
269
|
|
|
* @return void |
270
|
|
|
* |
271
|
|
|
* @throws \Exception If route handler failed or returned false. |
272
|
|
|
*/ |
273
|
5 |
|
public static function start(?string $base = null, ?bool $allowMultiMatch = null, ?bool $caseMatters = null, ?bool $slashMatters = null): void |
274
|
|
|
{ |
275
|
5 |
|
self::$params = func_get_args(); |
276
|
|
|
|
277
|
5 |
|
[$base, $allowMultiMatch, $caseMatters, $slashMatters] = static::getValidParameters($base, $allowMultiMatch, $caseMatters, $slashMatters); |
278
|
|
|
|
279
|
5 |
|
static::$base = $base = '/' . trim($base, '/'); |
280
|
5 |
|
static::$path = $path = static::getRoutePath($slashMatters); |
281
|
|
|
|
282
|
5 |
|
$routeMatchFound = false; |
283
|
5 |
|
$pathMatchFound = false; |
284
|
5 |
|
$result = null; |
285
|
|
|
|
286
|
5 |
|
foreach (static::$routes as &$route) { |
287
|
5 |
|
$expression = $base === '/' ? $route['expression'] : sprintf('%s/%s', $base, ltrim($route['expression'], '/')); |
288
|
|
|
|
289
|
5 |
|
$regex = static::getRouteRegex($expression, $slashMatters, $caseMatters); |
290
|
5 |
|
if (preg_match($regex, $path, $matches, PREG_UNMATCHED_AS_NULL)) { |
291
|
4 |
|
$pathMatchFound = true; |
292
|
|
|
|
293
|
4 |
|
$currentMethod = static::getRequestMethod(); |
294
|
4 |
|
$allowedMethods = (array)$route['method']; |
295
|
4 |
|
foreach ($allowedMethods as $allowedMethod) { |
296
|
4 |
|
if (strtoupper($currentMethod) !== strtoupper($allowedMethod)) { |
297
|
2 |
|
continue; |
298
|
|
|
} |
299
|
|
|
|
300
|
3 |
|
$routeMatchFound = true; |
301
|
|
|
|
302
|
3 |
|
$route['arguments'] = static::getRouteArguments($route['arguments'], $matches, $result); |
303
|
|
|
|
304
|
3 |
|
$result = call_user_func_array($route['handler'], $route['arguments']); |
305
|
|
|
|
306
|
3 |
|
if ($result === false) { |
307
|
1 |
|
throw new \Exception("Something went wrong when trying to respond to '{$path}'! Check the handler for this route"); |
308
|
|
|
} |
309
|
|
|
} |
310
|
|
|
} |
311
|
|
|
|
312
|
5 |
|
if ($routeMatchFound && !$allowMultiMatch) { |
313
|
1 |
|
break; |
314
|
|
|
} |
315
|
|
|
} |
316
|
|
|
|
317
|
4 |
|
unset($route); |
318
|
|
|
|
319
|
4 |
|
static::echoResponse($routeMatchFound, $pathMatchFound, $result); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* Returns valid parameters for `self::start()` by validating the passed parameters and adding the deficiency from router config. |
324
|
|
|
* |
325
|
|
|
* @param string|null $base |
326
|
|
|
* @param bool|null $allowMultiMatch |
327
|
|
|
* @param bool|null $caseMatters |
328
|
|
|
* @param bool|null $slashMatters |
329
|
|
|
* |
330
|
|
|
* @return array |
331
|
|
|
*/ |
332
|
5 |
|
private static function getValidParameters(?string $base, ?bool $allowMultiMatch, ?bool $caseMatters, ?bool $slashMatters): array |
333
|
|
|
{ |
334
|
5 |
|
$routerConfig = Config::get('router'); |
335
|
|
|
|
336
|
5 |
|
$base ??= $routerConfig['base']; |
337
|
5 |
|
$allowMultiMatch ??= $routerConfig['allowMultiMatch']; |
338
|
5 |
|
$caseMatters ??= $routerConfig['caseMatters']; |
339
|
5 |
|
$slashMatters ??= $routerConfig['slashMatters']; |
340
|
|
|
|
341
|
|
|
return [ |
342
|
5 |
|
$base, |
343
|
5 |
|
$allowMultiMatch, |
344
|
5 |
|
$caseMatters, |
345
|
5 |
|
$slashMatters, |
346
|
|
|
]; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* Returns a valid decoded route path. |
351
|
|
|
* |
352
|
|
|
* @param string $base |
353
|
|
|
* |
354
|
|
|
* @return string |
355
|
|
|
*/ |
356
|
5 |
|
private static function getRoutePath(bool $slashMatters): string |
357
|
|
|
{ |
358
|
5 |
|
$url = static::getParsedUrl(); |
359
|
|
|
|
360
|
5 |
|
$path = '/'; |
361
|
5 |
|
if (isset($url['path'])) { |
362
|
5 |
|
$path = $url['path']; |
363
|
5 |
|
$path = !$slashMatters && $path !== '/' ? rtrim($path, '/') : $path; |
364
|
|
|
} |
365
|
|
|
|
366
|
5 |
|
return urldecode($path); |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* Returns a valid route regex. |
371
|
|
|
* |
372
|
|
|
* @param string $expression |
373
|
|
|
* @param bool $slashMatters |
374
|
|
|
* @param bool $caseMatters |
375
|
|
|
* |
376
|
|
|
* @return string |
377
|
|
|
*/ |
378
|
5 |
|
private static function getRouteRegex(string $expression, bool $slashMatters, bool $caseMatters): string |
379
|
|
|
{ |
380
|
5 |
|
$routePlaceholderRegex = '/{([a-z0-9_\-\.?]+)}/i'; |
381
|
5 |
|
if (preg_match($routePlaceholderRegex, $expression)) { |
382
|
1 |
|
$routeMatchRegex = strpos($expression, '?}') !== false ? '(.*)?' : '(.+)'; |
383
|
1 |
|
$expression = preg_replace( |
384
|
1 |
|
$routePlaceholderRegex, |
385
|
|
|
$routeMatchRegex, |
386
|
|
|
$expression |
387
|
|
|
); |
388
|
|
|
} |
389
|
5 |
|
return sprintf( |
390
|
5 |
|
'<^%s$>%s', |
391
|
5 |
|
(!$slashMatters && $expression !== '/' ? rtrim($expression, '/') : $expression), |
392
|
5 |
|
(!$caseMatters ? 'iu' : 'u') |
393
|
|
|
); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* Returns valid arguments for route handler in the order that the handler expect. |
398
|
|
|
* |
399
|
|
|
* @param array $current |
400
|
|
|
* @param array $matches |
401
|
|
|
* @param mixed $result |
402
|
|
|
* |
403
|
|
|
* @return array |
404
|
|
|
*/ |
405
|
3 |
|
private static function getRouteArguments(array $current, array $matches, $result): array |
406
|
|
|
{ |
407
|
3 |
|
$arguments = array_merge($current, $matches); |
408
|
3 |
|
$arguments = array_filter($arguments); |
409
|
3 |
|
if (count($arguments) > 1) { |
410
|
1 |
|
array_push($arguments, $result); |
411
|
|
|
} else { |
412
|
2 |
|
array_push($arguments, null, $result); |
413
|
|
|
} |
414
|
|
|
|
415
|
3 |
|
return $arguments; |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
/** |
419
|
|
|
* Echos the response according to the passed parameters. |
420
|
|
|
* |
421
|
|
|
* @param bool $routeMatchFound |
422
|
|
|
* @param bool $pathMatchFound |
423
|
|
|
* @param mixed $result |
424
|
|
|
* |
425
|
|
|
* @return void |
426
|
|
|
*/ |
427
|
4 |
|
private static function echoResponse(bool $routeMatchFound, bool $pathMatchFound, $result): void |
428
|
|
|
{ |
429
|
4 |
|
$protocol = Globals::getServer('SERVER_PROTOCOL'); |
430
|
4 |
|
$method = Globals::getServer('REQUEST_METHOD'); |
431
|
|
|
|
432
|
4 |
|
if (!$routeMatchFound) { |
433
|
2 |
|
$result = sprintf('The "%s" route is not found, or the request method is not allowed!', static::$path); |
434
|
|
|
|
435
|
2 |
|
if ($pathMatchFound) { |
436
|
1 |
|
if (static::$methodNotAllowedCallback) { |
437
|
1 |
|
$result = call_user_func(static::$methodNotAllowedCallback, static::$path, $method); |
438
|
|
|
|
439
|
1 |
|
header("{$protocol} 405 Method Not Allowed", true, 405); |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
App::log( |
443
|
|
|
'Responded with 405 to the request for "{path}" with method "{method}"', |
444
|
|
|
['path' => static::$path, 'method' => $method], |
445
|
|
|
'system' |
446
|
|
|
); |
447
|
|
|
} else { |
448
|
1 |
|
if (static::$routeNotFoundCallback) { |
449
|
1 |
|
$result = call_user_func(static::$routeNotFoundCallback, static::$path); |
450
|
|
|
|
451
|
1 |
|
header("{$protocol} 404 Not Found", true, 404); |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
App::log( |
455
|
|
|
'Responded with 404 to the request for "{path}"', |
456
|
|
|
['path' => static::$path], |
457
|
|
|
'system' |
458
|
|
|
); |
459
|
|
|
} |
460
|
|
|
} else { |
461
|
2 |
|
header("{$protocol} 200 OK", false, 200); |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
echo $result; |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
/** |
468
|
|
|
* Returns query parameters. |
469
|
|
|
* |
470
|
|
|
* @return array |
471
|
|
|
*/ |
472
|
1 |
|
public static function getParsedQuery(): array |
473
|
|
|
{ |
474
|
1 |
|
$url = static::getParsedUrl(); |
475
|
|
|
|
476
|
1 |
|
parse_str($url['query'] ?? '', $query); |
477
|
|
|
|
478
|
1 |
|
return $query; |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
/** |
482
|
|
|
* Returns components of the current URL. |
483
|
|
|
* |
484
|
|
|
* @return array |
485
|
|
|
*/ |
486
|
7 |
|
public static function getParsedUrl(): array |
487
|
|
|
{ |
488
|
7 |
|
$uri = Globals::getServer('REQUEST_URI'); |
489
|
|
|
|
490
|
|
|
// remove double slashes as they make parse_url() fail |
491
|
7 |
|
$url = preg_replace('/(\/+)/', '/', $uri); |
492
|
7 |
|
$url = parse_url($url); |
493
|
|
|
|
494
|
7 |
|
return $url; |
495
|
|
|
} |
496
|
|
|
|
497
|
4 |
|
protected static function getRequestMethod(): string |
498
|
|
|
{ |
499
|
4 |
|
$method = Globals::getPost('_method') ?? ''; |
500
|
4 |
|
$methods = static::SUPPORTED_METHODS; |
501
|
4 |
|
$methodAllowed = in_array( |
502
|
4 |
|
strtoupper($method), |
503
|
4 |
|
array_map('strtoupper', $methods) |
504
|
|
|
); |
505
|
|
|
|
506
|
4 |
|
if ($methodAllowed) { |
507
|
2 |
|
Globals::setServer('REQUEST_METHOD', $method); |
508
|
|
|
} |
509
|
|
|
|
510
|
4 |
|
return Globals::getServer('REQUEST_METHOD'); |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
/** |
514
|
|
|
* Returns all registered routes with their `expression`, `handler`, `arguments`, and `method`. |
515
|
|
|
* |
516
|
|
|
* @return array |
517
|
|
|
*/ |
518
|
2 |
|
public static function getRegisteredRoutes(): array |
519
|
|
|
{ |
520
|
2 |
|
return static::$routes; |
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
|
524
|
|
|
/** |
525
|
|
|
* Class constructor. |
526
|
|
|
*/ |
527
|
18 |
|
final public function __construct() |
528
|
|
|
{ |
529
|
|
|
// prevent overwriting constructor in subclasses to allow to use |
530
|
|
|
// "return new static()" without caring about dependencies. |
531
|
18 |
|
} |
532
|
|
|
|
533
|
|
|
/** |
534
|
|
|
* Aliases `self::handle()` method with common HTTP verbs. |
535
|
|
|
*/ |
536
|
1 |
|
public static function __callStatic(string $method, array $arguments) |
537
|
|
|
{ |
538
|
1 |
|
$methods = static::SUPPORTED_METHODS; |
539
|
1 |
|
$methodAllowed = in_array( |
540
|
1 |
|
strtoupper($method), |
541
|
1 |
|
array_map('strtoupper', ['ANY', ...$methods]) |
542
|
|
|
); |
543
|
|
|
|
544
|
1 |
|
if (!$methodAllowed) { |
545
|
1 |
|
$class = static::class; |
546
|
1 |
|
throw new \Exception("Call to undefined method {$class}::{$method}()"); |
547
|
|
|
} |
548
|
|
|
|
549
|
1 |
|
if (count($arguments) > 2) { |
550
|
1 |
|
$arguments = array_slice($arguments, 0, 2); |
551
|
|
|
} |
552
|
|
|
|
553
|
1 |
|
if (strtoupper($method) === 'ANY') { |
554
|
1 |
|
array_push($arguments, $methods); |
555
|
|
|
} else { |
556
|
1 |
|
array_push($arguments, $method); |
557
|
|
|
} |
558
|
|
|
|
559
|
1 |
|
return static::handle(...$arguments); |
|
|
|
|
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
/** |
563
|
|
|
* Allows static methods handled by self::__callStatic() to be accessible via object operator `->`. |
564
|
|
|
*/ |
565
|
1 |
|
public function __call(string $method, array $arguments) |
566
|
|
|
{ |
567
|
1 |
|
return static::__callStatic($method, $arguments); |
|
|
|
|
568
|
|
|
} |
569
|
|
|
} |
570
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.