Completed
Push — master ( c8fcb0...10d838 )
by Alex
01:42
created

Router.php (2 issues)

1
<?php
2
namespace Mezon\Router;
3
4
// TODO add custom types
5
// TODO PSR-7 compliant
6
// TODO add non-static routes optimizations like here https://medium.com/@nicolas.grekas/making-symfonys-router-77-7x-faster-1-2-958e3754f0e1
7
// TODO add 404 test benchmark
8
9
/**
10
 * Class Router
11
 *
12
 * @package Mezon
13
 * @subpackage Router
14
 * @author Dodonov A.A.
15
 * @version v.1.0 (2019/08/15)
16
 * @copyright Copyright (c) 2019, aeon.org
17
 */
18
19
/**
20
 * Router class
21
 */
22
class Router
23
{
24
25
    use \Mezon\Router\RoutesSet;
26
27
    use \Mezon\Router\UrlParser;
28
29
    /**
30
     * Method wich handles invalid route error
31
     *
32
     * @var callable
33
     */
34
    private $invalidRouteErrorHandler;
35
36
    /**
37
     * Method returns request method
38
     *
39
     * @return string Request method
40
     */
41
    protected function getRequestMethod(): string
42
    {
43
        return $_SERVER['REQUEST_METHOD'] ?? 'GET';
44
    }
45
46
    /**
47
     * Constructor
48
     */
49
    public function __construct()
50
    {
51
        $_SERVER['REQUEST_METHOD'] = $this->getRequestMethod();
52
53
        $this->invalidRouteErrorHandler = [
54
            $this,
55
            'noProcessorFoundErrorHandler'
56
        ];
57
58
        $this->initDefaultTypes();
59
    }
60
61
    /**
62
     * Method fetches actions from the objects and creates GetRoutes for them
63
     *
64
     * @param object $object
65
     *            Object to be processed
66
     */
67
    public function fetchActions(object $object): void
68
    {
69
        $methods = get_class_methods($object);
70
71
        foreach ($methods as $method) {
72
            if (strpos($method, 'action') === 0) {
73
                $route = \Mezon\Router\Utils::convertMethodNameToRoute($method);
74
                $this->addGetRoute($route, $object, $method);
75
                $this->addPostRoute($route, $object, $method);
76
            }
77
        }
78
    }
79
80
    /**
81
     * Method adds route and it's handler
82
     *
83
     * $callback function may have two parameters - $route and $parameters. Where $route is a called route,
84
     * and $parameters is associative array (parameter name => parameter value) with URL parameters
85
     *
86
     * @param string $route
87
     *            Route
88
     * @param mixed $callback
89
     *            Collback wich will be processing route call.
90
     * @param string|array $requestMethod
91
     *            Request type
92
     */
93
    public function addRoute(string $route, $callback, $requestMethod = 'GET'): void
94
    {
95
        $route = trim($route, '/');
96
97
        if ($route == '*') {
98
            $this->universalRouteWasAdded = true;
99
        }
100
101
        if (is_array($requestMethod)) {
102
            foreach ($requestMethod as $r) {
103
                $this->addRoute($route, $callback, $r);
104
            }
105
        } else {
106
            $routes = &$this->getRoutesForMethod($requestMethod);
107
            // this 'if' is for backward compatibility
108
            // remove it on 02-04-2021
109
            if (is_array($callback) && isset($callback[1]) && is_array($callback[1])) {
110
                $callback = $callback[1];
111
            }
112
            $routes[$route] = $callback;
113
        }
114
    }
115
116
    /**
117
     * Method processes no processor found error
118
     *
119
     * @param string $route
120
     *            Route
121
     */
122
    public function noProcessorFoundErrorHandler(string $route)
123
    {
124
        throw (new \Exception(
125
            'The processor was not found for the route ' . $route . ' in ' . $this->getAllRoutesTrace()));
126
    }
127
128
    /**
129
     * Method sets InvalidRouteErrorHandler function
130
     *
131
     * @param callable $function
132
     *            Error handler
133
     */
134
    public function setNoProcessorFoundErrorHandler(callable $function)
135
    {
136
        $oldErrorHandler = $this->invalidRouteErrorHandler;
137
138
        $this->invalidRouteErrorHandler = $function;
139
140
        return $oldErrorHandler;
141
    }
142
143
    /**
144
     * Processing specified router
145
     *
146
     * @param mixed $route
147
     *            Route
148
     */
149
    public function callRoute($route)
150
    {
151
        $route = \Mezon\Router\Utils::prepareRoute($route);
152
        $requestMethod = $this->getRequestMethod();
153
        $routesForMethod = $this->getRoutesForMethod($requestMethod);
154
155
        if (($result = $this->findStaticRouteProcessor($routesForMethod, $route)) !== false) {
156
            return $result;
157
        }
158
159
        if (($result = $this->findDynamicRouteProcessor($routesForMethod, $route)) !== false) {
160
            return $result;
161
        }
162
163
        call_user_func($this->invalidRouteErrorHandler, $route);
164
    }
165
166
    /**
167
     * Method returns call back by it's router
168
     * 
169
     * @param array|string $route route
170
     * @return array|callable route callback
171
     */
172
    public function getCallback($route){
173
        $route = \Mezon\Router\Utils::prepareRoute($route);
174
        $requestMethod = $this->getRequestMethod();
175
        $routesForMethod = $this->getRoutesForMethod($requestMethod);
176
177
        if (($result = $this->getStaticRouteProcessor($routesForMethod, $route)) !== false) {
178
            return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type true which is incompatible with the documented return type array|callable.
Loading history...
179
        }
180
181
        if (($result = $this->getDynamicRouteProcessor($routesForMethod, $route)) !== false) {
182
            return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type true which is incompatible with the documented return type array|callable.
Loading history...
183
        }
184
185
        call_user_func($this->invalidRouteErrorHandler, $route);//@codeCoverageIgnoreStart
186
    }//@codeCoverageIgnoreEnd
187
}
188