Passed
Branch dev (8b2306)
by Alex
02:48
created

Route::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
ccs 6
cts 6
cp 1
rs 9.4285
cc 1
eloc 5
nc 1
nop 4
crap 1
1
<?php
2
3
/**
4
 * Codeburner Framework.
5
 *
6
 * @author Alex Rohleder <[email protected]>
7
 * @copyright 2016 Alex Rohleder
8
 * @license http://opensource.org/licenses/MIT
9
 */
10
11
namespace Codeburner\Router;
12
13
use Codeburner\Router\Exceptions\BadRouteException;
14
use Codeburner\Router\Strategies\MatcherAwareInterface;
15
use Codeburner\Router\Strategies\StrategyInterface;
16
17
/**
18
 * Route representation, a route must be able to chang and execute itself.
19
 *
20
 * @author Alex Rohleder <[email protected]>
21
 */
22
23
class Route
24
{
25
26
    /**
27
     * @var Collector
28
     */
29
30
    protected $collector;
31
    
32
    /**
33
     * @var string
34
     */
35
36
    protected $method;
37
    
38
    /**
39
     * @var string
40
     */
41
42
    protected $pattern;
43
    
44
    /**
45
     * @var string|array|\Closure
46
     */
47
48
    protected $action;
49
    
50
    /**
51
     * @var string
52
     */
53
54
    protected $namespace = "";
55
56
    /**
57
     * @var string[]
58
     */
59
60
    protected $params = [];
61
62
    /**
63
     * Defaults are parameters set by the user, and don't
64
     * appear on the pattern.
65
     *
66
     * @var array
67
     */
68
69
    protected $defaults = [];
70
71
    /**
72
     * Metadata can be set to be used on filters, dispatch strategies
73
     * or anywhere the route object is used.
74
     *
75
     * @var array
76
     */
77
78
    protected $metadata = [];
79
80
    /**
81
     * @var null|string|StrategyInterface
82
     */
83
84
    protected $strategy;
85
86
    /**
87
     * Blocked routes are dynamic routes selected to pass by the matcher.
88
     *
89
     * @var boolean
90
     */
91
92
    protected $blocked = false;
93
94
    /**
95
     * The matcher that dispatched this route.
96
     *
97
     * @var Matcher $matcher
98
     */
99
100
    protected $matcher;
101
102
    /**
103
     * The function used to create controllers from name.
104
     *
105
     * @var null|string|array|\Closure
106
     */
107
108
    protected $controllerCreationFunction;
109
110
    /**
111
     * @param Collector $collector
112
     * @param string $method
113
     * @param string $pattern
114
     * @param string|array|\Closure $action
115
     */
116
117 28
    public function __construct(Collector $collector, $method, $pattern, $action)
118
    {
119 28
        $this->collector = $collector;
120 28
        $this->method    = $method;
121 28
        $this->pattern   = $pattern;
122 28
        $this->action    = $action;
123 28
    }
124
125
    /**
126
     * Clone this route and set it into the collector.
127
     *
128
     * @return Route
129
     */
130
131 3
    public function reset()
132
    {
133 3
        return $this->collector->set($this->method, $this->pattern, $this->action)->nth(0)
134 3
                               ->setStrategy($this->strategy)->setParams($this->params)
0 ignored issues
show
Bug introduced by
It seems like $this->strategy can also be of type null; however, Codeburner\Router\Route::setStrategy() does only seem to accept string|object<Codeburner...gies\StrategyInterface>, 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...
135 3
                               ->setDefaults($this->defaults)->setMetadataArray($this->metadata);
136
    }
137
138
    /**
139
     * Remove this route from the collector.
140
     *
141
     * @return self
142
     */
143
144 5
    public function forget()
145
    {
146 5
        $this->collector->forget($this->method, $this->pattern);
147 5
        return $this;
148
    }
149
150
    /**
151
     * Blocking a route indicate that that route have been selected and
152
     * parsed, now it will be given to the matcher.
153
     *
154
     * @return self
155
     */
156
157 17
    public function block()
158
    {
159 17
        $this->blocked = true;
160 17
        return $this;
161
    }
162
163
    /**
164
     * Verify if a Route have already been blocked.
165
     *
166
     * @return boolean
167
     */
168
169 17
    public function blocked()
170
    {
171 17
        return $this->blocked;
172
    }
173
174
    /**
175
     * Execute the route action, if no strategy was provided the action
176
     * will be executed by the call_user_func PHP function.
177
     *
178
     * @throws BadRouteException
179
     * @return mixed
180
     */
181
182 5
    public function call()
183
    {
184 5
        $this->action = $this->parseCallable($this->action);
185
186 5
        if ($this->strategy === null) {
187 5
            return call_user_func_array($this->action, array_merge($this->defaults, $this->params));
188
        }
189
190
        if (!is_object($this->strategy)) {
191
            $this->strategy = new $this->strategy;
192
        }
193
194
        return $this->callWithStrategy();
195
    }
196
197
    /**
198
     * Seek for dynamic content on callables. eg. routes action controller#action
199
     * syntax allow to use the variables to build the string like: {controller}@{action}
200
     *
201
     * @param string|array|\Closure $callable
202
     * @return string|array|\Closure
203
     */
204
205 5
    private function parseCallable($callable)
206
    {
207 5
        if (is_string($callable) && strpos($callable, "@")) {
208 2
            $callable = explode("@", $callable);
209 2
        }
210
211 5
        if (is_array($callable)) {
212 2
            if (is_string($callable[0])) {
213 2
                   $callable[0] = $this->parseCallableController($callable[0]);
214 2
                   $callable[1] = $this->parseCallablePlaceholders($callable[1]);
215 2
            } else $callable[1] = $this->parseCallablePlaceholders($callable[1]);
216 2
        }
217
218 5
        return $callable;
219
    }
220
221
    /**
222
     * Get the controller object.
223
     *
224
     * @param string $controller
225
     * @return Object
226
     */
227
228 2
    private function parseCallableController($controller)
229
    {
230 2
        $controller  = rtrim($this->namespace, "\\") . "\\" . $this->parseCallablePlaceholders($controller);
231
232 2
        if ($this->controllerCreationFunction === null) {
233 1
               return new $controller;
234 1
        } else return call_user_func($this->controllerCreationFunction, $controller);
235
    }
236
237
    /**
238
     * Parse and replace dynamic content on route action.
239
     *
240
     * @param  string $fragment Part of callable
241
     * @return string
242
     */
243
244 2
    private function parseCallablePlaceholders($fragment)
245
    {
246 2
        if (strpos($fragment, "{") !== false) {
247
            foreach ($this->params as $placeholder => $value) {
248
                if (strpos($fragment, "{" . $placeholder . "}") !== false) {
249
                    $fragment = str_replace("{" . $placeholder . "}", ucwords(str_replace("-", " ", $value)), $fragment);
250
                }
251
            }
252
        }
253
254 2
        return $fragment;
255
    }
256
257
    /**
258
     * Execute the route action with the given strategy.
259
     *
260
     * @throws BadRouteException
261
     * @return mixed
262
     */
263
264
    private function callWithStrategy()
265
    {
266
        if ($this->strategy instanceof StrategyInterface) {
267
            if ($this->strategy instanceof MatcherAwareInterface) {
268
                $this->strategy->setMatcher($this->matcher);
269
            }
270
271
            return $this->strategy->call($this);
272
        }
273
274
        throw new BadRouteException("`$this->strategy` is not a valid route dispatch strategy, ".
275
            "it must implement the `Codeburner\Router\Strategies\StrategyInterface` interface.");
276
    }
277
278
    /**
279
     * @return Collector
280
     */
281
282
    public function getCollector()
283
    {
284
        return $this->collector;
285
    }
286
287
    /**
288
     * @return string
289
     */
290
291
    public function getMethod()
292
    {
293
        return $this->method;
294
    }
295
296
    /**
297
     * @return string
298
     */
299
300 17
    public function getPattern()
301
    {
302 17
        return $this->pattern;
303
    }
304
305
    /**
306
     * @return string[]
307
     */
308
309
    public function getSegments()
310
    {
311
        return explode("/", $this->pattern);
312
    }
313
314
    /**
315
     * @return string|array|\Closure
316
     */
317
318 5
    public function getAction()
319
    {
320 5
        return $this->action;
321
    }
322
323
    /**
324
     * @return string
325
     */
326
327
    public function getNamespace()
328
    {
329
        return $this->namespace;
330
    }
331
332
    /**
333
     * @return string[]
334
     */
335
336 17
    public function getParams()
337
    {
338 17
        return $this->params;
339
    }
340
341
    /**
342
     * @param string $key
343
     * @return string
344
     */
345
346
    public function getParam($key)
347
    {
348
        return $this->params[$key];
349
    }
350
351
    /**
352
     * @return array
353
     */
354
355
    public function getDefaults()
356
    {
357
        return $this->defaults;
358
    }
359
360
    /**
361
     * @param string $key
362
     * @return mixed
363
     */
364
365
    public function getDefault($key)
366
    {
367
        return $this->defaults[$key];
368
    }
369
370
    /**
371
     * @return array
372
     */
373
374
    public function getMetadataArray()
375
    {
376
        return $this->metadata;
377
    }
378
379
    /**
380
     * @param string $key
381
     * @return mixed
382
     */
383
384
    public function getMetadata($key)
385
    {
386
        return $this->metadata[$key];
387
    }
388
389
    /**
390
     * @return string|null
391
     */
392
393
    public function getStrategy()
394
    {
395
        if ($this->strategy instanceof StrategyInterface) {
396
            return get_class($this->strategy);
397
        }
398
399
        return $this->strategy;
400
    }
401
402
    /**
403
     * @inheritdoc
404
     */
405
406
    public function getMatcher()
407
    {
408
        return $this->matcher;
409
    }
410
411
    /**
412
     * @param string $method
413
     * @return Route
414
     */
415
416
    public function setMethod($method)
417
    {
418
        $this->forget();
419
        $this->method = $method;
420
        return $this->reset();
421
    }
422
423
    /**
424
     * @param string $pattern
425
     * @return Route
426
     */
427
428 3
    public function setPattern($pattern)
429
    {
430 3
        $this->forget();
431 3
        $this->pattern = $pattern;
432 3
        return $this->reset();
433
    }
434
435
    /**
436
     * @param string $pattern
437
     * @return self
438
     */
439
440 17
    public function setPatternWithoutReset($pattern)
441
    {
442 17
        $this->pattern = $pattern;
443 17
        return $this;
444
    }
445
446
    /**
447
     * @param string $action
448
     * @return self
449
     */
450
451
    public function setAction($action)
452
    {
453
        $this->action = $action;
454
        return $this;
455
    }
456
457
    /**
458
     * @param string $namespace
459
     * @return self
460
     */
461
462
    public function setNamespace($namespace)
463
    {
464
        $this->namespace = $namespace;
465
        return $this;
466
    }
467
468
    /**
469
     * @param string[] $params
470
     * @return self
471
     */
472
473 17
    public function setParams(array $params)
474
    {
475 17
        $this->params = $params;
476 17
        return $this;
477
    }
478
479
    /**
480
     * @param string $key
481
     * @param string $value
482
     *
483
     * @return self
484
     */
485
486
    public function setParam($key, $value)
487
    {
488
        $this->params[$key] = $value;
489
        return $this;
490
    }
491
492
    /**
493
     * @param mixed[] $defaults
494
     * @return self
495
     */
496
497 3
    public function setDefaults(array $defaults)
498
    {
499 3
        $this->defaults = $defaults;
500 3
        return $this;
501
    }
502
503
    /**
504
     * @param string $key
505
     * @param mixed $value
506
     *
507
     * @return self
508
     */
509
510
    public function setDefault($key, $value)
511
    {
512
        $this->defaults[$key] = $value;
513
        return $this;
514
    }
515
516
    /**
517
     * @param mixed[] $metadata
518
     * @return self
519
     */
520
521 3
    public function setMetadataArray(array $metadata)
522
    {
523 3
        $this->metadata = $metadata;
524 3
        return $this;
525
    }
526
527
    /**
528
     * @param string $key
529
     * @param mixed $value
530
     *
531
     * @return $this
532
     */
533
534
    public function setMetadata($key, $value)
535
    {
536
        $this->metadata[$key] = $value;
537
        return $this;
538
    }
539
540
    /**
541
     * @param string|StrategyInterface $strategy
542
     * @return self
543
     */
544
545 10
    public function setStrategy($strategy)
546
    {
547 10
        $this->strategy = $strategy;
548 10
        return $this;
549
    }
550
551
    /**
552
     * @inheritdoc
553
     */
554
555 15
    public function setMatcher(Matcher $matcher)
556
    {
557 15
        $this->matcher = $matcher;
558 15
    }
559
560
    /**
561
     * Set a function to create controllers.
562
     *
563
     * @param string|array|\Closure $callable
564
     * @throws BadRouteException
565
     * @return self
566
     */
567
568 1
    public function setControllerCreationFunction($callable)
569
    {
570 1
        if (!is_callable($callable)) {
571
            throw new BadRouteException(BadRouteException::WRONG_CONTROLLER_CREATION_FUNC);
572
        }
573
574 1
        $this->controllerCreationFunction = $this->parseCallable($callable);
575 1
        return $this;
576
    }
577
578
    /**
579
     * @param string $key
580
     * @return bool
581
     */
582
583
    public function hasParam($key)
584
    {
585
        return isset($this->params[$key]);
586
    }
587
588
    /**
589
     * @param string $key
590
     * @return bool
591
     */
592
593
    public function hasDefault($key)
594
    {
595
        return isset($this->defaults[$key]);
596
    }
597
598
    /**
599
     * @param string $key
600
     * @return bool
601
     */
602
603
    public function hasMetadata($key)
604
    {
605
        return isset($this->metadata[$key]);
606
    }
607
608
}
609