Passed
Push — master ( b8f6df...218d14 )
by Alexander
01:42
created

Router::getPageRoute()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 1
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace alkemann\h2l;
4
5
use alkemann\h2l\util\Http;
6
use Closure;
7
8
/**
9
 * Class Router
10
 *
11
 * @package alkemann\h2l
12
 */
13
class Router implements interfaces\Router
14
{
15
    /**
16
     * Defines which character is used by regex dynamic routes
17
     */
18
    public static $DELIMITER = '|';
19
20
    private static $aliases = [];
21
    private static $routes = [];
22
    private static $fallback = null;
23
24
    /**
25
     * Add an alias route, i.e. `/` as alias for `home.html`
26
     *
27
     * @param string $alias
28
     * @param string $real
29
     */
30
    public static function alias(string $alias, string $real): void
31
    {
32
        self::$aliases[$alias] = $real;
33
    }
34
35
    /**
36
     * Add new dynamic route to application
37
     *
38
     * @param string $url Regex that is valid for preg_match, including named groups
39
     * @param callable $callable
40
     * @param mixed $methods a single Http::<GET/POST/PUT/PATCH/DELETE> or an array of multiple
41
     * @internal param Closure $closure Code to run on this match
42
     */
43
    public static function add(string $url, callable $callable, $methods = [Http::GET]): void
44
    {
45
        if ($callable instanceof Closure) {
46
            $closure = $callable;
47
        } else {
48
            $closure = Closure::fromCallable($callable);
49
        }
50
        foreach ((array) $methods as $method) {
51
            self::$routes[$method][$url] = $closure;
52
        }
53
    }
54
55
    /**
56
     * Sets fallback route to be used if no other route is matched and Page is not used.
57
     *
58
     * @param callable $callable
59
     */
60
    public static function fallback(callable $callable): void
61
    {
62
        self::$fallback = $callable;
63
    }
64
65
    public static function getFallback(): ?interfaces\Route
66
    {
67
        if (isset(self::$fallback)) {
68
            return new Route('FALLBACK', self::$fallback);
69
        }
70
        return null;
71
    }
72
73
    /**
74
     * Given a request url and request method, identify route (dynamic or fixed)
75
     *
76
     * @param string $url Request url, i.e. '/api/user/32'
77
     * @param string $method Http::<GET/POST/PATCH/PUT/DELETE>
78
     * @return ?interfaces\Route
79
     */
80
    public static function match(string $url, string $method = Http::GET): ?interfaces\Route
81
    {
82
        $url = self::$aliases[$url] ?? $url;
83
84
        if (isset(self::$routes[$method])) {
85
            if (isset(self::$routes[$method][$url])) {
86
                return new Route($url, self::$routes[$method][$url]);
87
            }
88
89
            // TODO cache of previous matched dynamic routes
90
            $route = self::matchDynamicRoute($url, $method);
91
            if ($route) {
0 ignored issues
show
introduced by
$route is of type alkemann\h2l\Route, thus it always evaluated to true.
Loading history...
92
                return $route;
93
            }
94
        }
95
        return null;
96
    }
97
98
    private static function matchDynamicRoute(string $url, string $method = Http::GET): ?interfaces\Route
99
    {
100
        foreach (self::$routes[$method] as $route => $cb) {
101
            if ($route[0] !== substr($route, -1) || $route[0] !== static::$DELIMITER) {
102
                continue;
103
            }
104
            $result = preg_match($route, $url, $matches);
105
            if (!$result) {
106
                continue;
107
            }
108
109
            $parameters = array_filter(
110
                $matches,
111
                function ($v) {
112
                    return !is_int($v);
113
                },
114
                \ARRAY_FILTER_USE_KEY
115
            );
116
117
            return new Route($url, $cb, $parameters);
118
        }
119
120
        return null;
121
    }
122
123
    /**
124
     * Set up a route that uses the Page response for url to view files automation
125
     *
126
     * @param string $url
127
     * @return interfaces\Route
128
     */
129
    public static function getPageRoute(string $url): interfaces\Route
130
    {
131
        $url = self::$aliases[$url] ?? $url;
132
        return new Route(
133
            $url,
134
            function (Request $request) {
135
                $page = response\Page::fromRequest($request);
136
                if ($page->isValid()) {
137
                    return $page;
138
                }
139
            }
140
        );
141
    }
142
}
143