Completed
Push — master ( 202ec5...1a894f )
by Siro Díaz
02:44
created

Router::patch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 2
Metric Value
c 3
b 0
f 2
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 3
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
        $this->routerParser = new RouterParser($this->path);
83
    }
84
85
    /**
86
     * Set the request method. Used in tests or development.
87
     *
88
     * @param $method
89
     */
90
91
    public function setRequestMethod($method) {
92
        $this->requestMethod = $method;
93
    }
94
95
    /**
96
     * Dispatch to the route set.
97
     *
98
     * @param $method string Method to dispatch
99
     * @param $path string Path to dispatch
100
     */
101
102
    public function dispatch($method, $path) {
103
        $this->setRequestMethod($method);
104
        $this->setPath($path);
105
    }
106
107
    /**
108
     * Searchs in the array of routes the route that matches(same URI
109
     * and request method).
110
     *
111
     * @param $uri string
112
     * @param $method string The request method
113
     * @return bool true if exist a result
114
     */
115
116
    public function find($uri, $method) {
117
        $found = false;
118
        $counter = 0;
119
120
        if(count($this->routes) === 0) {
121
            return $found;
122
        }
123
124
        while($found === false && $counter < count($this->routes)) {
125
            if($this->routes[$counter]->getUri() === $uri && $this->routes[$counter]->getMethod() === $method) {
126
                $found = true;
127
            }
128
129
            $counter++;
130
        }
131
132
        return $found;
133
    }
134
135
    /**
136
     * Adds a new route to the routes array.
137
     *
138
     * @param $uri
139
     * @param $method
140
     * @param $response
141
     * @return bool false if the route has not been added.
142
     */
143
144
    private function addRoute($uri, $method, $response, array $middleware = []) {
145
        if($this->find($uri, $method)) {    // search if exists an apparition
146
            return false;
147
        }
148
149
        $totalRoutes = count($this->routes);
150
        $this->routes[$totalRoutes] = new Order($uri, $method, $response, $middleware);
151
        return true;
152
    }
153
154
    /**
155
     * Clear the array of orders(routes) registered.
156
     */
157
158
    public function clear() {
159
        $this->routes = [];
160
    }
161
162
    /**
163
     * Register the GET request.
164
     *
165
     * @param $uri
166
     * @param $response
167
     */
168
169
    public function get($uri, $response, array $middleware = []) {
170
        $this->addRoute($uri, Method::GET, $response, $middleware);
171
    }
172
173
    /**
174
     * Register the POST request.
175
     *
176
     * @param $uri
177
     * @param $response
178
     */
179
180
    public function post($uri, $response, array $middleware = []) {
181
        $this->addRoute($uri, Method::POST, $response, $middleware);
182
    }
183
184
    /**
185
     * Register the PUT request.
186
     *
187
     * @param $uri
188
     * @param $response
189
     */
190
191
    public function put($uri, $response, array $middleware = []) {
192
        $this->addRoute($uri, Method::PUT, $response, $middleware);
193
    }
194
195
    /**
196
     * Register the DELETE request.
197
     *
198
     * @param $uri string The path requested
199
     * @param $response callable Action to response
200
     */
201
202
    public function delete($uri, $response, array $middleware = []) {
203
        $this->addRoute($uri, Method::DELETE, $response, $middleware);
204
    }
205
206
    /**
207
     * Register the PATCH request.
208
     *
209
     * @param $uri string The path requested
210
     * @param $response callable Action to response
211
     */
212
213
    public function patch($uri, $response, array $middleware = []) {
214
        $this->addRoute($uri, Method::PATCH, $response, $middleware);
215
    }
216
217
    /**
218
     * Register one or more requests for the same uri.
219
     *
220
     * @param $uri string The path requested
221
     * @param $response callable Action to response
222
     * @param $methods mixed Methods to bind. Optional. GET by default
223
     * @param $middleware array Middlewares before and after. Optional
224
     */
225
226
    public function both($uri, $response, $methods = Method::GET, array $middleware = []) {
227
        if(is_array($methods)) {
228
            foreach($methods as $method) {
229
                $this->addRoute($uri, $method, $response, $middleware);
230
            }
231
        } else {
232
            $this->addRoute($uri, $methods, $response, $middleware);
233
        }
234
    }
235
236
    /**
237
     *
238
     */
239
240
    public function any($uri, $response, array $middleware = []) {
241
        $this->addRoute($uri, Method::GET, $response, $middleware);
242
        $this->addRoute($uri, Method::POST, $response, $middleware);
243
        $this->addRoute($uri, Method::PUT, $response, $middleware);
244
        $this->addRoute($uri, Method::DELETE, $response, $middleware);
245
        $this->addRoute($uri, Method::PATCH, $response, $middleware);
246
    }
247
248
    /**
249
     * Sets a callback for the notFound event.
250
     *
251
     * @param $func callable
252
     */
253
254
    public function notFound($func) {
255
        if(is_callable($func)) {
256
            $this->notFound = $func;
257
        }
258
    }
259
260
    /**
261
     * Initialize the router app and start to run
262
     * over the array of routes for any appearance.
263
     * If there is a result then call the callback associate.
264
     * If there is not a result it will execute the notFound
265
     * action.
266
     *
267
     * @return mixed The result of execute the callback.
268
     */
269
270
    public function run() {
271
        $found = false;
272
        $counter = 0;
273
        while($found === false && $counter < count($this->routes)) {
274
            if($this->routerParser->match($this->routes[$counter]->getUri()) && $this->routes[$counter]->getMethod() === $this->requestMethod) {
275
                $found = true;
276
            } else {
277
                $counter++;
278
            }
279
        }
280
281
        if($found) {
282
            // run the before middleware if it exists
283
            if($this->routes[$counter]->hasBefore()) {
284
                call_user_func($this->routes[$counter]->getMiddlewares()['before']);
285
            }
286
287
            $params = $this->routerParser->getParams($this->routes[$counter]->getUri());
288
            $response = call_user_func_array($this->routes[$counter]->getResponse(), $params);
289
290
            // run the after middleware if it exists
291
            if($this->routes[$counter]->hasAfter()) {
292
                call_user_func($this->routes[$counter]->getMiddlewares()['after']);
293
            }
294
295
            return $response;
296
        } else {
297
            return call_user_func($this->notFound);
298
        }
299
    }
300
}