Completed
Push — master ( 11f9b9...975112 )
by Siro Díaz
01:45
created

Router::run()   C

Complexity

Conditions 8
Paths 15

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 30
rs 5.3846
cc 8
eloc 18
nc 15
nop 0
1
<?php
2
3
namespace Routify;
4
5
6
class Router {
7
8
    /**
9
     * @var array The list of routes that contains the callback function and the request method.
10
     */
11
    private $routes;
12
13
    /**
14
     * @var string The path requested for the client(browser, cli, app...).
15
     */
16
    private $path;
17
18
    /**
19
     * @var string The requested method(GET, POST, PUT, DELETE) used by the client.
20
     */
21
    private $requestMethod;
22
23
    /**
24
     * @var object The instance of the router parser.
25
     */
26
    private $routerParser;
27
28
    /**
29
     * @var callable The action to be executed if the any route doesn't match
30
     */
31
    private $notFound;
32
33
    public function __construct() {
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
34
        $this->routes = [];
35
        // only path, without ?, #, etc.
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
36
        $this->path = (isset($_SERVER['REQUEST_URI'])) ?
37
            rawurldecode(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)) : '/';
38
        $this->routerParser = new RouterParser($this->path);
39
        $this->requestMethod = $_SERVER['REQUEST_METHOD'];
40
41
        $this->notFound = function() {};
42
    }
43
44
    /**
45
     * Returns the path.
46
     *
47
     * @return string
48
     */
49
50
    public function getPath() {
51
        return $this->path;
52
    }
53
54
    /**
55
     * Returns the array of routes.
56
     *
57
     * @return array
58
     */
59
60
    public function getRoutes() {
61
        return $this->routes;
62
    }
63
64
    /**
65
     * Return the request method used by the client.
66
     *
67
     * @return string
68
     */
69
70
    public function getRequestMethod() {
71
        return $this->requestMethod;
72
    }
73
74
    /**
75
     * Sets the path. Don't use it in production. Only for tests.
76
     *
77
     * @param $path
78
     */
79
80
    public function setPath($path) {
81
        $this->path = $path;
82
    }
83
84
    /**
85
     * Set the request method. Used in tests or development.
86
     *
87
     * @param $method
88
     */
89
90
    public function setRequestMethod($method) {
91
        $this->requestMethod = $method;
92
    }
93
94
    /**
95
     * Searchs in the array of routes the route that matches(same URI
96
     * and request method).
97
     *
98
     * @param $uri string
99
     * @param $method string The request method
100
     * @return bool true if exist a result
101
     */
102
103
    public function find($uri, $method) {
104
        $found = false;
105
        $counter = 0;
106
107
        if(count($this->routes) === 0) {
108
            return $found;
109
        }
110
111
        while($found === false && $counter < count($this->routes)) {
112
            if ($this->routes[$counter]->getUri() === $uri && $this->routes[$counter]->getMethod() === $method) {
113
                $found = true;
114
            }
115
116
            $counter++;
117
        }
118
119
        return $found;
120
    }
121
122
    /**
123
     * Adds a new route to the routes array.
124
     *
125
     * @param $uri
126
     * @param $method
127
     * @param $response
128
     * @return bool false if the route has not been added.
129
     */
130
131
    private function addOrder($uri, $method, $response, array $middleware = []) {
132
        if($this->find($uri, $method)) {    // search if exists an apparition
133
            return false;
134
        }
135
136
        $totalRoutes = count($this->routes);
137
        $this->routes[$totalRoutes] = new Order($uri, $method, $response, $middleware);
138
    }
139
140
    /**
141
     * Clear the array of orders(routes) registered.
142
     */
143
144
    public function clear() {
145
        $this->routes = [];
146
    }
147
148
    /**
149
     * Register the GET request.
150
     *
151
     * @param $uri
152
     * @param $response
153
     */
154
155
    public function get($uri, $response, array $middleware = []) {
156
        $this->addOrder($uri, Method::GET, $response, $middleware);
157
    }
158
159
    /**
160
     * Register the POST request.
161
     *
162
     * @param $uri
163
     * @param $response
164
     */
165
166
    public function post($uri, $response, array $middleware = []) {
167
        $this->addOrder($uri, Method::POST, $response, $middleware);
168
    }
169
170
    /**
171
     * Register the PUT request.
172
     *
173
     * @param $uri
174
     * @param $response
175
     */
176
177
    public function put($uri, $response, array $middleware = []) {
178
        $this->addOrder($uri, Method::POST, $response, $middleware);
179
    }
180
181
    /**
182
     * Register the DELETE request.
183
     *
184
     * @param $uri
185
     * @param $response
186
     */
187
188
    public function delete($uri, $response, array $middleware = []) {
189
        $this->addOrder($uri, Method::POST, $response, $middleware);
190
    }
191
192
    /**
193
     * Register the PATCH request.
194
     *
195
     * @param $uri
196
     * @param $response
197
     */
198
199
    public function patch($uri, $response, array $middleware = []) {
200
        $this->addOrder($uri, Method::PATCH, $response, $middleware);
201
    }
202
203
    /**
204
     * Sets a callback for the notFound event.
205
     *
206
     * @param $func callable
207
     */
208
209
    public function notFound($func) {
210
        if(is_callable($func)) {
211
            $this->notFound = $func;
212
        }
213
    }
214
215
    /**
216
     * Initialize the router app and start to run
217
     * over the array of routes for any appearance.
218
     * If there is a result then call the callback associate.
219
     * If there is not a result it will execute the notFound
220
     * action.
221
     *
222
     * @return mixed The result of execute the callback.
223
     */
224
225
    public function run() {
226
        $found = false;
227
        $counter = 0;
228
        while($found === false && $counter < count($this->routes)) {
229
            if($this->routerParser->match($this->routes[$counter]->getUri()) && $this->routes[$counter]->getMethod() === $this->requestMethod) {
230
                $found = true;
231
            } else {
232
                $counter++;
233
            }
234
        }
235
236
        if($found) {
237
            // run the before middleware if it exists
238
            if($this->routes[$counter]->hasBefore()) {
239
                call_user_func($this->routes[$counter]->getMiddlewares()['before']);
240
            }
241
242
            $params = $this->routerParser->getParams($this->routes[$counter]->getUri());
243
            $response = call_user_func_array($this->routes[$counter]->getResponse(), $params);
244
245
            // run the after middleware if it exists
246
            if($this->routes[$counter]->hasAfter()) {
247
                call_user_func($this->routes[$counter]->getMiddlewares()['after']);
248
            }
249
250
            return $response;
251
        } else {
252
            return call_user_func($this->notFound);
253
        }
254
    }
255
}