Completed
Push — master ( 14a546...2fbb7d )
by Siro Díaz
02:38
created

Router::addRoute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 9
rs 9.6666
cc 2
eloc 6
nc 2
nop 4
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.
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 addRoute($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
        return true;
139
    }
140
141
    /**
142
     * Clear the array of orders(routes) registered.
143
     */
144
145
    public function clear() {
146
        // unset($this->routes);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
147
        $this->routes = [];
148
    }
149
150
    /**
151
     * Register the GET request.
152
     *
153
     * @param $uri
154
     * @param $response
155
     */
156
157
    public function get($uri, $response, array $middleware = []) {
158
        $this->addRoute($uri, Method::GET, $response, $middleware);
159
    }
160
161
    /**
162
     * Register the POST request.
163
     *
164
     * @param $uri
165
     * @param $response
166
     */
167
168
    public function post($uri, $response, array $middleware = []) {
169
        $this->addRoute($uri, Method::POST, $response, $middleware);
170
    }
171
172
    /**
173
     * Register the PUT request.
174
     *
175
     * @param $uri
176
     * @param $response
177
     */
178
179
    public function put($uri, $response, array $middleware = []) {
180
        $this->addRoute($uri, Method::POST, $response, $middleware);
181
    }
182
183
    /**
184
     * Register the DELETE request.
185
     *
186
     * @param $uri string The path requested
187
     * @param $response callable Action to response
188
     */
189
190
    public function delete($uri, $response, array $middleware = []) {
191
        $this->addRoute($uri, Method::POST, $response, $middleware);
192
    }
193
194
    /**
195
     * Register the PATCH request.
196
     *
197
     * @param $uri string The path requested
198
     * @param $response callable Action to response
199
     */
200
201
    public function patch($uri, $response, array $middleware = []) {
202
        $this->addRoute($uri, Method::PATCH, $response, $middleware);
203
    }
204
205
    /**
206
     * Register one or more requests for the same uri.
207
     *
208
     * @param $uri string The path requested
209
     * @param $response callable Action to response
210
     * @param $methods mixed Methods to bind. Optional. GET by default
211
     * @param $middleware array Middlewares before and after. Optional
212
     */
213
214
    public function both($uri, $response, $methods = Method::GET, array $middleware = []) {
215
        if(is_array($methods)) {
216
            foreach($methods as $method) {
217
                $this->addRoute($uri, $method, $response, $middleware);
218
            }
219
        } else {
220
            $this->addRoute($uri, $methods, $response, $middleware);
221
        }
222
    }
223
224
    /**
225
     * Sets a callback for the notFound event.
226
     *
227
     * @param $func callable
228
     */
229
230
    public function notFound($func) {
231
        if(is_callable($func)) {
232
            $this->notFound = $func;
233
        }
234
    }
235
236
    /**
237
     * Initialize the router app and start to run
238
     * over the array of routes for any appearance.
239
     * If there is a result then call the callback associate.
240
     * If there is not a result it will execute the notFound
241
     * action.
242
     *
243
     * @return mixed The result of execute the callback.
244
     */
245
246
    public function run() {
247
        $found = false;
248
        $counter = 0;
249
        while($found === false && $counter < count($this->routes)) {
250
            if($this->routerParser->match($this->routes[$counter]->getUri()) && $this->routes[$counter]->getMethod() === $this->requestMethod) {
251
                $found = true;
252
            } else {
253
                $counter++;
254
            }
255
        }
256
257
        if($found) {
258
            // run the before middleware if it exists
259
            if($this->routes[$counter]->hasBefore()) {
260
                call_user_func($this->routes[$counter]->getMiddlewares()['before']);
261
            }
262
263
            $params = $this->routerParser->getParams($this->routes[$counter]->getUri());
264
            $response = call_user_func_array($this->routes[$counter]->getResponse(), $params);
265
266
            // run the after middleware if it exists
267
            if($this->routes[$counter]->hasAfter()) {
268
                call_user_func($this->routes[$counter]->getMiddlewares()['after']);
269
            }
270
271
            return $response;
272
        } else {
273
            return call_user_func($this->notFound);
274
        }
275
    }
276
}