Passed
Branch dev (140aec)
by Alex
02:37
created

Route::getAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
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 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 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 42
    public function __construct(Collector $collector, $method, $pattern, $action)
118
    {
119 42
        $this->collector = $collector;
120 42
        $this->method    = $method;
121 42
        $this->pattern   = $pattern;
122 42
        $this->action    = $action;
123 42
    }
124
125
    /**
126
     * Clone this route and set it into the collector.
127
     *
128
     * @return Route
129
     */
130
131 4
    public function reset()
132
    {
133 4
        return $this->collector->set($this->method, $this->pattern, $this->action)->nth(0)
134 4
                               ->setStrategy($this->strategy)->setParams($this->params)
135 4
                               ->setDefaults($this->defaults)->setMetadataArray($this->metadata);
136
    }
137
138
    /**
139
     * Remove this route from the collector.
140
     *
141
     * @return self
142
     */
143
144 6
    public function forget()
145
    {
146 6
        $this->collector->forget($this->method, $this->pattern);
147 6
        return $this;
148
    }
149
150
    /**
151
     * Execute the route action, if no strategy was provided the action
152
     * will be executed by the call_user_func PHP function.
153
     *
154
     * @throws BadRouteException
155
     * @return mixed
156
     */
157
158 11
    public function call()
159
    {
160 11
        $this->action = $this->parseCallable($this->action);
161
162 11
        if ($this->strategy === null) {
163 9
            return call_user_func_array($this->action, array_merge($this->defaults, $this->params));
164
        }
165
166 2
        if (!is_object($this->strategy)) {
167 2
            $this->strategy = new $this->strategy;
168 2
        }
169
170 2
        return $this->callWithStrategy();
171
    }
172
173
    /**
174
     * Seek for dynamic content on callables. eg. routes action controller#action
175
     * syntax allow to use the variables to build the string like: {controller}@{action}
176
     *
177
     * @param string|array|\Closure $callable
178
     * @return string|array|\Closure
179
     */
180
181 11
    private function parseCallable($callable)
182
    {
183 11
        if (is_string($callable) && strpos($callable, "@")) {
184 5
            $callable = explode("@", $callable);
185 5
        }
186
187 11
        if (is_array($callable)) {
188 5
            if (is_string($callable[0])) {
189 5
                   $callable[0] = $this->parseCallableController($callable[0]);
190 5
            }
191
192 5
            $callable[1] = $this->parseCallablePlaceholders($callable[1]);
193 5
        }
194
195 11
        return $callable;
196
    }
197
198
    /**
199
     * Get the controller object.
200
     *
201
     * @param string $controller
202
     * @return Object
203
     */
204
205 5
    private function parseCallableController($controller)
206
    {
207 5
        $controller  = rtrim($this->namespace, "\\") . "\\" . $this->parseCallablePlaceholders($controller);
208
209 5
        if ($this->controllerCreationFunction === null) {
210 4
               return new $controller;
211 1
        } else return call_user_func($this->controllerCreationFunction, $controller);
212
    }
213
214
    /**
215
     * Parse and replace dynamic content on route action.
216
     *
217
     * @param  string $fragment Part of callable
218
     * @return string
219
     */
220
221 5
    private function parseCallablePlaceholders($fragment)
222
    {
223 5
        if (strpos($fragment, "{") !== false) {
224
            foreach ($this->params as $placeholder => $value) {
225
                if (strpos($fragment, "{" . $placeholder . "}") !== false) {
226
                    $fragment = str_replace("{" . $placeholder . "}", ucwords(str_replace("-", " ", $value)), $fragment);
227
                }
228
            }
229
        }
230
231 5
        return $fragment;
232
    }
233
234
    /**
235
     * Execute the route action with the given strategy.
236
     *
237
     * @throws BadRouteException
238
     * @return mixed
239
     */
240
241 2
    private function callWithStrategy()
242
    {
243 2
        if ($this->strategy instanceof StrategyInterface) {
244 1
            if ($this->strategy instanceof MatcherAwareInterface) {
245 1
                $this->strategy->setMatcher($this->matcher);
246 1
            }
247
248 1
            return $this->strategy->call($this);
249
        }
250
251 1
        $strategy = get_class($this->strategy);
252 1
        throw new BadRouteException("`$strategy` is not a valid route dispatch strategy, ".
253 1
            "it must implement the `Codeburner\\Router\\Strategies\\StrategyInterface` interface.");
254
    }
255
256
    /**
257
     * @return Collector
258
     */
259
260
    public function getCollector()
261
    {
262
        return $this->collector;
263
    }
264
265
    /**
266
     * @return string
267
     */
268
269
    public function getMethod()
270
    {
271
        return $this->method;
272
    }
273
274
    /**
275
     * @return string
276
     */
277
278 24
    public function getPattern()
279
    {
280 24
        return $this->pattern;
281
    }
282
283
    /**
284
     * @return string[]
285
     */
286
287
    public function getSegments()
288
    {
289
        return explode("/", $this->pattern);
290
    }
291
292
    /**
293
     * @return string|array|\Closure
294
     */
295
296 6
    public function getAction()
297
    {
298 6
        return $this->action;
299
    }
300
301
    /**
302
     * @return string
303
     */
304
305
    public function getNamespace()
306
    {
307
        return $this->namespace;
308
    }
309
310
    /**
311
     * @return string[]
312
     */
313
314 24
    public function getParams()
315
    {
316 24
        return $this->params;
317
    }
318
319
    /**
320
     * @param string $key
321
     * @return string
322
     */
323
324
    public function getParam($key)
325
    {
326
        return $this->params[$key];
327
    }
328
329
    /**
330
     * @return array
331
     */
332
333 1
    public function getDefaults()
334
    {
335 1
        return $this->defaults;
336
    }
337
338
    /**
339
     * @param string $key
340
     * @return mixed
341
     */
342
343 1
    public function getDefault($key)
344
    {
345 1
        return $this->defaults[$key];
346
    }
347
348
    /**
349
     * @return array
350
     */
351
352 1
    public function getMetadataArray()
353
    {
354 1
        return $this->metadata;
355
    }
356
357
    /**
358
     * @param string $key
359
     * @return mixed
360
     */
361
362 1
    public function getMetadata($key)
363
    {
364 1
        return $this->metadata[$key];
365
    }
366
367
    /**
368
     * @return string|null
369
     */
370
371
    public function getStrategy()
372
    {
373
        if ($this->strategy instanceof StrategyInterface) {
374
            return get_class($this->strategy);
375
        }
376
377
        return $this->strategy;
378
    }
379
380
    /**
381
     * @inheritdoc
382
     */
383
384
    public function getMatcher()
385
    {
386
        return $this->matcher;
387
    }
388
389
    /**
390
     * Verify if a Route have already been blocked.
391
     *
392
     * @return boolean
393
     */
394
395 24
    public function getBlock()
396
    {
397 24
        return $this->blocked;
398
    }
399
400
    /**
401
     * Blocking a route indicate that that route have been selected and
402
     * parsed, now it will be given to the matcher.
403
     *
404
     * @param bool $blocked
405
     * @return self
406
     */
407
408 24
    public function setBlock($blocked)
409
    {
410 24
        $this->blocked = $blocked;
411 24
        return $this;
412
    }
413
414
    /**
415
     * @param string $method
416
     * @return Route
417
     */
418
419 1
    public function setMethod($method)
420
    {
421 1
        $this->forget();
422 1
        $this->method = $method;
423 1
        return $this->reset();
424
    }
425
426
    /**
427
     * @param string $pattern
428
     * @return Route
429
     */
430
431 3
    public function setPattern($pattern)
432
    {
433 3
        $this->forget();
434 3
        $this->pattern = $pattern;
435 3
        return $this->reset();
436
    }
437
438
    /**
439
     * @param string $pattern
440
     * @return self
441
     */
442
443 24
    public function setPatternWithoutReset($pattern)
444
    {
445 24
        $this->pattern = $pattern;
446 24
        return $this;
447
    }
448
449
    /**
450
     * @param string $action
451
     * @return self
452
     */
453
454 3
    public function setAction($action)
455
    {
456 3
        $this->action = $action;
457 3
        return $this;
458
    }
459
460
    /**
461
     * @param string $namespace
462
     * @return self
463
     */
464
465 1
    public function setNamespace($namespace)
466
    {
467 1
        $this->namespace = $namespace;
468 1
        return $this;
469
    }
470
471
    /**
472
     * @param string[] $params
473
     * @return self
474
     */
475
476 25
    public function setParams(array $params)
477
    {
478 25
        $this->params = $params;
479 25
        return $this;
480
    }
481
482
    /**
483
     * @param string $key
484
     * @param string $value
485
     *
486
     * @return self
487
     */
488
489
    public function setParam($key, $value)
490
    {
491
        $this->params[$key] = $value;
492
        return $this;
493
    }
494
495
    /**
496
     * @param mixed[] $defaults
497
     * @return self
498
     */
499
500 5
    public function setDefaults(array $defaults)
501
    {
502 5
        $this->defaults = $defaults;
503 5
        return $this;
504
    }
505
506
    /**
507
     * @param string $key
508
     * @param mixed $value
509
     *
510
     * @return self
511
     */
512
513 1
    public function setDefault($key, $value)
514
    {
515 1
        $this->defaults[$key] = $value;
516 1
        return $this;
517
    }
518
519
    /**
520
     * @param mixed[] $metadata
521
     * @return self
522
     */
523
524 5
    public function setMetadataArray(array $metadata)
525
    {
526 5
        $this->metadata = $metadata;
527 5
        return $this;
528
    }
529
530
    /**
531
     * @param string $key
532
     * @param mixed $value
533
     *
534
     * @return $this
535
     */
536
537 1
    public function setMetadata($key, $value)
538
    {
539 1
        $this->metadata[$key] = $value;
540 1
        return $this;
541
    }
542
543
    /**
544
     * @param null|string|StrategyInterface $strategy
545
     * @return self
546
     */
547
548 13
    public function setStrategy($strategy)
549
    {
550 13
        $this->strategy = $strategy;
551 13
        return $this;
552
    }
553
554
    /**
555
     * @inheritdoc
556
     */
557
558 22
    public function setMatcher(Matcher $matcher)
559
    {
560 22
        $this->matcher = $matcher;
561 22
        return $this;
562
    }
563
564
    /**
565
     * Set a constraint to a token in the route pattern.
566
     *
567
     * @param string $token
568
     * @param string $regex
569
     *
570
     * @return self
571
     */
572
573 1
    public function setConstraint($token, $regex)
574
    {
575 1
        $initPos = strpos($this->pattern, "{" . $token);
576
577 1
        if ($initPos !== false) {
578 1
            $endPos = strpos($this->pattern, "}", $initPos);
579 1
            $newPattern = substr_replace($this->pattern, "{" . "$token:$regex" . "}", $initPos, $endPos - $initPos + 1);
580 1
            $wildcards = $this->collector->getWildcardTokens();
581 1
            $newPattern = str_replace(array_keys($wildcards), $wildcards, $newPattern);
582 1
            $this->setPatternWithoutReset($newPattern);
583 1
        }
584
585 1
        return $this;
586
    }
587
588
    /**
589
     * Set a function to create controllers.
590
     *
591
     * @param string|array|\Closure $callable
592
     * @throws BadRouteException
593
     * @return self
594
     */
595
596 1
    public function setControllerCreationFunction($callable)
597
    {
598 1
        if (!is_callable($callable)) {
599
            throw new BadRouteException(BadRouteException::WRONG_CONTROLLER_CREATION_FUNC);
600
        }
601
602 1
        $this->controllerCreationFunction = $this->parseCallable($callable);
603 1
        return $this;
604
    }
605
606
    /**
607
     * @param string $key
608
     * @return bool
609
     */
610
611
    public function hasParam($key)
612
    {
613
        return isset($this->params[$key]);
614
    }
615
616
    /**
617
     * @param string $key
618
     * @return bool
619
     */
620
621
    public function hasDefault($key)
622
    {
623
        return isset($this->defaults[$key]);
624
    }
625
626
    /**
627
     * @param string $key
628
     * @return bool
629
     */
630
631
    public function hasMetadata($key)
632
    {
633
        return isset($this->metadata[$key]);
634
    }
635
636
}
637