Passed
Push — master ( 7fd6e2...6fd100 )
by Charis
04:17
created

Router::getCacheItem()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 0
1
<?php
2
3
namespace Resilient;
4
5
use BadMethodCallException;
6
use \Resilient\Route;
7
use \Resilient\Design\RouteableInterface;
8
use \Resilient\Traits\Routeable;
9
use \Resilient\Traits\Bindable;
10
use \FastRoute\Dispatcher;
11
use \FastRoute\RouteCollector;
12
use \FastRoute\RouteParser;
13
use \FastRoute\RouteParser\Std as StdParser;
14
use \Psr\Http\Message\UriInterface;
15
use \Psr\SimpleCache\CacheItemPoolInterface;
16
17
/**
18
 * Router class.
19
 *
20
 * @implements RouteableInterface
21
 * @method callable run(array $args)
22
 */
23
class Router implements RouteableInterface
24
{
25
    use Routeable, Bindable;
26
27
    /**
28
     * notFoundFuncName
29
     *
30
     * (default value: 'notFoundHandler')
31
     *
32
     * @var string
33
     * @access protected
34
     */
35
    protected $notFoundFuncName = 'notFoundHandler';
36
37
    /**
38
     * forbiddenFuncName
39
     *
40
     * (default value: 'forbidenMethodHandler')
41
     *
42
     * @var string
43
     * @access protected
44
     */
45
    protected $forbiddenFuncName = 'forbidenMethodHandler';
46
47
    /**
48
     * dispatch_result
49
     *
50
     * @var mixed
51
     * @access protected
52
     */
53
    protected $dispatch_result;
54
55
    /**
56
     * routes
57
     *
58
     * (default value: [])
59
     *
60
     * @var mixed
61
     * @access protected
62
     */
63
    protected $routes = [];
64
65
    /**
66
     * routeCount
67
     *
68
     * (default value: 0)
69
     *
70
     * @var int
71
     * @access protected
72
     */
73
    protected $routeCount = 0;
74
75
    /**
76
     * routeGroup
77
     *
78
     * @var mixed
79
     * @access protected
80
     */
81
    protected $routeGroup;
82
83
    /**
84
     * parser
85
     *
86
     * @var mixed
87
     * @access protected
88
     */
89
    protected $parser;
90
91
    /**
92
     * dispatcher
93
     *
94
     * @var mixed
95
     * @access protected
96
     */
97
    protected $dispatcher;
98
99
    /**
100
     * cachePool
101
     *
102
     * @var mixed
103
     * @access protected
104
     */
105
    protected $cachePool;
106
107
    /**
108
     * cacheKey
109
     *
110
     * (default value: "{Resilient\Router}/router.cache")
111
     *
112
     * @var string
113
     * @access protected
114
     */
115
    protected $cacheKey = "{Resilient\Router}/router.cache";
116
117
    /**
118
     * cacheTtl
119
     *
120
     * (default value: 86400)
121
     *
122
     * @var int
123
     * @access protected
124
     */
125
    protected $cacheTtl = 86400;
126
127
    /**
128
     * apiHandler
129
     *
130
     * @var mixed
131
     * @access protected
132
     */
133
    protected $apiHandler;
134
135
    /**
136
     * notFoundHandler
137
     *
138
     * @var mixed
139
     * @access protected
140
     */
141
    protected $notFoundHandler;
142
143
    /**
144
     * methodNotAllowedHandler
145
     *
146
     * @var mixed
147
     * @access protected
148
     */
149
    protected $methodNotAllowedHandler;
150
151
    /**
152
     * __construct function.
153
     *
154
     * @access public
155
     * @param mixed $parser
156
     */
157
    public function __construct($parser)
158
    {
159
        $this->parser = $parser ?: new StdParser;
160
    }
161
162
    /**
163
     * setDispatcher function.
164
     *
165
     * @access public
166
     * @param Dispatcher $dispatcher
167
     * @return Router
168
     */
169
    public function setDispatcher(Dispatcher $dispatcher)
170
    {
171
        $this->dispatcher = $dispatcher;
172
173
        return $this;
174
    }
175
176
    /**
177
     * setcachePool function.
178
     *
179
     * @access public
180
     * @param CacheInterface $cachePool
181
     * @return Router
182
     */
183
    public function setCachePool(CacheItemPoolInterface $cachePool)
184
    {
185
        $this->cachePool = $cachePool;
186
187
        return $this;
188
    }
189
190
    /**
191
     * setCacheTtl function.
192
     *
193
     * @access public
194
     * @param int $cacheTtl
195
     * @return Router
196
     */
197
    public function setCacheTtl(int $cacheTtl)
198
    {
199
        $this->cacheTtl = $cacheTtl;
200
201
        return $this;
202
    }
203
204
    /**
205
     * setCacheKey function.
206
     *
207
     * @access public
208
     * @param string $cacheKey
209
     * @return Router
210
     */
211
    public function setCacheKey(string $cacheKey)
212
    {
213
        $this->cacheKey = $cacheKey;
214
        return $this;
215
    }
216
217
    /**
218
     * getRoute function.
219
     *
220
     * @access public
221
     * @param string $identifier
222
     * @return null|Route
223
     */
224
    public function getRoute(string $identifier)
225
    {
226
        return !empty($this->routes[$identifier]) ? $this->routes[$identifier] : null;
227
    }
228
229
    /**
230
     * getRoutes function.
231
     *
232
     * @access public
233
     * @return array
234
     */
235
    public function getRoutes()
236
    {
237
        return $this->routes;
238
    }
239
240
    /**
241
     * setRoutes function.
242
     *
243
     * @access public
244
     * @param array $method
245
     * @param array $routes
246
     * @return Router
247
     */
248
    public function setRoutes(array $method, array $routes)
249
    {
250
        foreach ($routes as $pattern => $handler) {
251
            $this->map($method, $pattern, $handler);
252
        }
253
254
        return $this;
255
    }
256
257
    /**
258
     * getResult function.
259
     *
260
     * @access public
261
     * @return void
262
     */
263
    public function getResult()
264
    {
265
        return $this->dispatch_result;
266
    }
267
268
    /**
269
     * {@inheritdoc}
270
     */
271
    public function map($method, string $pattern, $handler)
272
    {
273
        $method = is_array($method) ? $method : [$method];
274
275
        foreach ($method as $m) {
276
            $route = $this->createRoute($m, $pattern, $handler);
277
278
            $this->routeCount++;
279
280
            $this->routes[$route->getIdentifier()] = $route;
281
282
            if (is_callable($handler)) {
283
                $route->bind('run', $handler);
284
            }
285
        }
286
287
        return $this;
288
    }
289
290
    /**
291
     * createRoute function.
292
     *
293
     * @access protected
294
     * @param string $method
295
     * @param string $pattern
296
     * @param mixed $handler
297
     * @return Route Route
298
     */
299
    protected function createRoute(string $method, string $pattern, $handler)
300
    {
301
        return new Route($method, $pattern, $handler, $this->routeGroup, 'route_' . $this->routeCount);
302
    }
303
304
    /**
305
     * cacheAble function.
306
     *
307
     * @access protected
308
     * @return void
309
     */
310
    protected function cacheAble()
311
    {
312
        return !empty($this->cachePool) && !empty($this->cacheKey);
313
    }
314
315
    /**
316
     * getCacheItem function.
317
     *
318
     * @access protected
319
     * @return CacheItemInterface
320
     */
321
    protected function getCacheItem()
322
    {
323
        return $this->cacheAble() ? $this->cachePool->getItem($this->cacheKey)  : new \Resilient\Dummy\CacheItem();
324
    }
325
326
    /**
327
     * routeDispatcher function.
328
     *
329
     * @access protected
330
     * @param callable $routeDefinitionCallback
331
     * @param array $options (default: [])
332
     * @return Dispatcher
333
     */
334
    protected function routeDispatcher(callable $routeDefinitionCallback, array $options = [])
335
    {
336
        $options += [
337
            'routeParser' => 'FastRoute\\RouteParser\\Std',
338
            'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased',
339
            'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased',
340
            'routeCollector' => 'FastRoute\\RouteCollector'
341
        ];
342
343
        $cacheItem = $this->getCacheItem();
344
345
        if ($cacheItem->isHit()) {
346
            return new $options['dispatcher']($cacheItem->get($this->cacheKey));
347
        }
348
349
        $routeCollector = new $options['routeCollector'](
350
            new $options['routeParser'], new $options['dataGenerator']
351
        );
352
        $routeDefinitionCallback ($routeCollector);
353
354
        $dispatchData = $routeCollector->getData();
355
356
        $cacheItem->set($dispatchData);
357
        $cacheItem->expiresAfter($this->cacheTtl);
358
359
        return new $options['dispatcher']($dispatchData);
360
    }
361
362
    /**
363
     * createDispatcher function.
364
     *
365
     * @access protected
366
     * @return Dispatcher
367
     */
368
    protected function createDispatcher()
369
    {
370
        if ($this->dispatcher) {
371
            return $this->dispatcher;
372
        }
373
374
        $this->dispatcher = $this->routeDispatcher(function (RouteCollector $r) {
375
            foreach ($this->getRoutes() as $route) {
376
                $r->addRoute($route->getMethod(), $route->getPattern(), $route->getIdentifier());
377
            }
378
        }, [
379
            'routeParser' => $this->parser,
380
        ]);
381
382
        return $this->dispatcher;
383
    }
384
385
    /**
386
     * dispatch function.
387
     *
388
     * @access public
389
     * @param UriInterface $uri
390
     * @param string $method (default: 'GET')
391
     * @return Route Handling Method
392
     */
393
    public function dispatch(UriInterface $uri, $method = 'GET')
394
    {
395
        $this->dispatch_result = $this->createDispatcher()->dispatch(
396
            $method,
397
            $uri->getPath()
398
        );
399
400
        $code = array_shift($this->dispatch_result);
401
402
        if ($code == Dispatcher::FOUND) {
403
            return $this->routerRoutine(...$this->dispatch_result);
0 ignored issues
show
Bug introduced by
The call to routerRoutine() misses a required argument $args.

This check looks for function calls that miss required arguments.

Loading history...
404
        }
405
406
        $exceptionMapper = [
407
            Dispatcher::NOT_FOUND => [
408
                $this->notFoundFuncName,
409
                [$uri, $method],
410
                ((string) $uri) . ' Not Available'
411
            ],
412
            Dispatcher::METHOD_NOT_ALLOWED => [
413
                $this->forbiddenFuncName,
414
                [$uri, $method],
415
                'Method : ' . ((string) $method) . ' ON uri : ' . ((string) $uri) . ' Not Allowed'
416
            ]
417
        ];
418
419
        return $this->handleException(...$exceptionMapper[$code]);
0 ignored issues
show
Bug introduced by
The call to handleException() misses some required arguments starting with $args.
Loading history...
Documentation introduced by
$exceptionMapper[$code] is of type array<integer,string|arr...ing\"}>","2":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
420
    }
421
422
    protected function handleException(string $handler, array $args, string $message)
423
    {
424
        if ($this->hasMethod($handler)) {
425
            return $this->$handler(...$args);
426
        }
427
428
        throw new BadMethodCallException($message);
429
    }
430
431
    /**
432
     * whenNotFound function.
433
     *
434
     * @access public
435
     * @param callable $callable
436
     * @return Router
437
     */
438
    public function whenNotFound(callable $callable)
439
    {
440
        $this->bind($this->notFoundFuncName, $callable);
441
442
        return $this;
443
    }
444
445
    /**
446
     * whenForbidden function.
447
     *
448
     * @access public
449
     * @param callable $callable
450
     * @return Router
451
     */
452
    public function whenForbidden(callable $callable)
453
    {
454
        $this->bind($this->forbiddenFuncName, $callable);
455
456
        return $this;
457
    }
458
459
    /**
460
     * routerRoutine function.
461
     *
462
     * @access protected
463
     * @param mixed $identifier
464
     * @param mixed $args
465
     * @return void
466
     */
467
    protected function routerRoutine($identifier, $args)
468
    {
469
        $route = $this->getRoute($identifier);
470
471
        if (!empty($args)) {
472
            foreach ($args as &$v) {
473
                $v = urldecode($v);
474
            }
475
        }
476
477
        if ($route->hasMethod('run')) {
478
            return $route->run($args);
479
        }
480
481
        return $route->setArgs($args);
482
    }
483
}
484