Passed
Push — master ( 5d9bff...5565c3 )
by Alex
02:15 queued 11s
created

Router   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 195
Duplicated Lines 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
eloc 45
c 3
b 2
f 0
dl 0
loc 195
rs 10
wmc 20

11 Methods

Rating   Name   Duplication   Size   Complexity  
A addRoute() 0 16 6
A getParam() 0 3 1
A hasParam() 0 3 1
A fetchActions() 0 9 3
A callRoute() 0 17 3
A clear() 0 3 1
A routeExists() 0 3 1
A getRequestMethod() 0 3 1
A noProcessorFoundErrorHandler() 0 4 1
A setNoProcessorFoundErrorHandler() 0 7 1
A __construct() 0 11 1
1
<?php
2
namespace Mezon\Router;
3
4
// TODO compare speed with Symphony router
5
// TODO [create|edit:action]
6
// TODO /date/[i:year]-[i:month]-[i:day]
7
8
/**
9
 * Class Router
10
 *
11
 * @package Mezon
12
 * @subpackage Router
13
 * @author Dodonov A.A.
14
 * @version v.1.0 (2019/08/15)
15
 * @copyright Copyright (c) 2019, aeon.org
16
 */
17
18
/**
19
 * Router class
20
 */
21
class Router
22
{
23
24
    /**
25
     * Method wich handles invalid route error
26
     *
27
     * @var callable
28
     */
29
    private $invalidRouteErrorHandler;
30
31
    /**
32
     * Set of routes
33
     *
34
     * @var \Mezon\Router\RoutesSet
35
     */
36
    private $routesSet = null;
37
38
    /**
39
     * URL parser
40
     *
41
     * @var \Mezon\Router\UrlParser
42
     */
43
    private $urlParser = null;
44
45
    /**
46
     * Method returns request method
47
     *
48
     * @return string Request method
49
     */
50
    protected function getRequestMethod(): string
51
    {
52
        return $_SERVER['REQUEST_METHOD'] ?? 'GET';
53
    }
54
55
    /**
56
     * Constructor
57
     */
58
    public function __construct()
59
    {
60
        $_SERVER['REQUEST_METHOD'] = $this->getRequestMethod();
61
62
        $this->invalidRouteErrorHandler = [
63
            $this,
64
            'noProcessorFoundErrorHandler'
65
        ];
66
67
        $this->routesSet = new RoutesSet();
68
        $this->urlParser = new UrlParser();
69
    }
70
71
    /**
72
     * Method fetches actions from the objects and creates GetRoutes for them
73
     *
74
     * @param object $object
75
     *            Object to be processed
76
     */
77
    public function fetchActions(object $object): void
78
    {
79
        $methods = get_class_methods($object);
80
81
        foreach ($methods as $method) {
82
            if (strpos($method, 'action') === 0) {
83
                $route = \Mezon\Router\Utils::convertMethodNameToRoute($method);
84
                $this->routesSet->addGetRoute($route, $object, $method);
85
                $this->routesSet->addPostRoute($route, $object, $method);
86
            }
87
        }
88
    }
89
90
    /**
91
     * Method adds route and it's handler
92
     *
93
     * $callback function may have two parameters - $route and $parameters. Where $route is a called route,
94
     * and $parameters is associative array (parameter name => parameter value) with URL parameters
95
     *
96
     * @param string $route
97
     *            Route
98
     * @param mixed $callback
99
     *            Collback wich will be processing route call.
100
     * @param string|array $requestMethod
101
     *            Request type
102
     */
103
    public function addRoute(string $route, $callback, $requestMethod = 'GET'): void
104
    {
105
        $route = '/' . trim($route, '/') . '/';
106
107
        if (is_array($requestMethod)) {
108
            foreach ($requestMethod as $r) {
109
                $this->addRoute($route, $callback, $r);
110
            }
111
        } else {
112
            $routes = &$this->routesSet->getRoutesForMethod($requestMethod);
113
            // this 'if' is for backward compatibility
114
            // remove it on 02-04-2021
115
            if (is_array($callback) && isset($callback[1]) && is_array($callback[1])) {
116
                $callback = $callback[1];
117
            }
118
            $routes[$route] = $callback;
119
        }
120
    }
121
122
    /**
123
     * Method processes no processor found error
124
     *
125
     * @param string $route
126
     *            Route
127
     */
128
    public function noProcessorFoundErrorHandler(string $route)
129
    {
130
        throw (new \Exception(
131
            'The processor was not found for the route ' . $route . ' in ' . $this->routesSet->getAllRoutesTrace()));
132
    }
133
134
    /**
135
     * Method sets InvalidRouteErrorHandler function
136
     *
137
     * @param callable $function
138
     *            Error handler
139
     */
140
    public function setNoProcessorFoundErrorHandler(callable $function)
141
    {
142
        $oldErrorHandler = $this->invalidRouteErrorHandler;
143
144
        $this->invalidRouteErrorHandler = $function;
145
146
        return $oldErrorHandler;
147
    }
148
149
    /**
150
     * Processing specified router
151
     *
152
     * @param mixed $route
153
     *            Route
154
     */
155
    public function callRoute($route)
156
    {
157
        $route = \Mezon\Router\Utils::prepareRoute($route);
158
159
        if (($result = $this->urlParser->findStaticRouteProcessor(
160
            $this->routesSet->getRoutesForMethod($this->getRequestMethod()),
161
            $route)) !== false) {
162
            return $result;
163
        }
164
165
        if (($result = $this->urlParser->findDynamicRouteProcessor(
166
            $this->routesSet->getRoutesForMethod($this->getRequestMethod()),
167
            $route)) !== false) {
168
            return $result;
169
        }
170
171
        call_user_func($this->invalidRouteErrorHandler, $route);
172
    }
173
174
    /**
175
     * Method clears router data.
176
     */
177
    public function clear()
178
    {
179
        $this->routesSet->clear();
180
    }
181
182
    /**
183
     * Method returns route parameter
184
     *
185
     * @param string $name
186
     *            Route parameter
187
     * @return string Route parameter
188
     */
189
    public function getParam(string $name): string
190
    {
191
        return $this->urlParser->getParam($name);
192
    }
193
194
    /**
195
     * Does parameter exists
196
     *
197
     * @param string $name
198
     *            Param name
199
     * @return bool True if the parameter exists
200
     */
201
    public function hasParam(string $name): bool
202
    {
203
        return $this->urlParser->hasParam($name);
204
    }
205
206
    /**
207
     * Method returns true if the router exists
208
     *
209
     * @param string $route
210
     *            checking route
211
     * @return bool true if the router exists, false otherwise
212
     */
213
    public function routeExists(string $route): bool
214
    {
215
        return $this->routesSet->routeExists($route);
216
    }
217
}
218