@@ 11-297 (lines=287) @@ | ||
8 | /** |
|
9 | * A container for routes. |
|
10 | */ |
|
11 | class Router implements InjectionAwareInterface |
|
12 | { |
|
13 | use InjectionAwareTrait; |
|
14 | ||
15 | /** |
|
16 | * @var array $routes all the routes. |
|
17 | * @var array $internalRoutes all internal routes. |
|
18 | * @var null|string $lastRoute last route that was matched and called. |
|
19 | */ |
|
20 | private $routes = []; |
|
21 | private $internalRoutes = []; |
|
22 | private $lastRoute = null; |
|
23 | ||
24 | ||
25 | ||
26 | /** |
|
27 | * Handle the routes and match them towards the request, dispatch them |
|
28 | * when a match is made. Each route handler may throw exceptions that |
|
29 | * may redirect to an internal route for error handling. |
|
30 | * Several routes can match and if the routehandler does not break |
|
31 | * execution flow, the route matching will carry on. |
|
32 | * Only the last routehandler will get its return value returned further. |
|
33 | * |
|
34 | * @param string $path the path to find a matching handler for. |
|
35 | * @param string $method the request method to match. |
|
36 | * |
|
37 | * @return mixed content returned from route. |
|
38 | */ |
|
39 | public function handle($path, $method = null) |
|
40 | { |
|
41 | try { |
|
42 | $match = false; |
|
43 | foreach ($this->routes as $route) { |
|
44 | if ($route->match($path, $method)) { |
|
45 | $this->lastRoute = $route->getRule(); |
|
46 | $match = true; |
|
47 | $results = $route->handle($this->di); |
|
48 | } |
|
49 | } |
|
50 | ||
51 | if ($match) { |
|
52 | return $results; |
|
53 | } |
|
54 | ||
55 | $this->handleInternal("404"); |
|
56 | } catch (ForbiddenException $e) { |
|
57 | $this->handleInternal("403"); |
|
58 | } catch (NotFoundException $e) { |
|
59 | $this->handleInternal("404"); |
|
60 | } catch (InternalErrorException $e) { |
|
61 | $this->handleInternal("500"); |
|
62 | } |
|
63 | } |
|
64 | ||
65 | ||
66 | ||
67 | /** |
|
68 | * Handle an internal route, the internal routes are not exposed to the |
|
69 | * end user. |
|
70 | * |
|
71 | * @param string $rule for this route. |
|
72 | * |
|
73 | * @return void |
|
74 | * |
|
75 | * @throws \Anax\Route\NotFoundException |
|
76 | */ |
|
77 | public function handleInternal($rule) |
|
78 | { |
|
79 | if (!isset($this->internalRoutes[$rule])) { |
|
80 | throw new NotFoundException("No internal route to handle: " . $rule); |
|
81 | } |
|
82 | $route = $this->internalRoutes[$rule]; |
|
83 | $this->lastRoute = $rule; |
|
84 | $route->handle(); |
|
85 | } |
|
86 | ||
87 | ||
88 | ||
89 | /** |
|
90 | * Load routes from a config file, the file should return an array with |
|
91 | * routes. |
|
92 | * |
|
93 | * @param string $file to load routes from. |
|
94 | * |
|
95 | * @return self |
|
96 | */ |
|
97 | public function load($file) |
|
98 | { |
|
99 | $config = require $file; |
|
100 | foreach ($config["routes"] as $route) { |
|
101 | $this->any( |
|
102 | $route["requestMethod"], |
|
103 | $route["path"], |
|
104 | $route["callable"] |
|
105 | ); |
|
106 | } |
|
107 | return $this; |
|
108 | } |
|
109 | ||
110 | ||
111 | ||
112 | /** |
|
113 | * Add a route with a request method, a path rule to match and an action |
|
114 | * as the callback. Adding several path rules (array) results in several |
|
115 | * routes being created. |
|
116 | * |
|
117 | * @param null|string|array $method as a valid request method. |
|
118 | * @param null|string|array $rule path rule for this route. |
|
119 | * @param null|string|callable $action to implement a handler for the route. |
|
120 | * |
|
121 | * @return class|array as new route(s), class if one added, else array. |
|
122 | */ |
|
123 | public function any($method, $rule, $action) |
|
124 | { |
|
125 | $rules = is_array($rule) ? $rule : [$rule]; |
|
126 | ||
127 | $routes = []; |
|
128 | foreach ($rules as $val) { |
|
129 | $route = new Route(); |
|
130 | $route->set($val, $action, $method); |
|
131 | $routes[] = $route; |
|
132 | $this->routes[] = $route; |
|
133 | } |
|
134 | ||
135 | return count($routes) === 1 ? $routes[0] : $routes; |
|
136 | } |
|
137 | ||
138 | ||
139 | ||
140 | /** |
|
141 | * Add a route to the router by rule(s) and a callback. |
|
142 | * |
|
143 | * @param null|string|array $rule for this route. |
|
144 | * @param null|string|callable $action a callback handler for the route. |
|
145 | * |
|
146 | * @return class|array as new route(s), class if one added, else array. |
|
147 | */ |
|
148 | public function add($rule, $action = null) |
|
149 | { |
|
150 | return $this->any(null, $rule, $action); |
|
151 | } |
|
152 | ||
153 | ||
154 | ||
155 | /** |
|
156 | * Add a default route which will be applied for any path. |
|
157 | * |
|
158 | * @param string|callable $action a callback handler for the route. |
|
159 | * |
|
160 | * @return class as new route. |
|
161 | */ |
|
162 | public function always($action) |
|
163 | { |
|
164 | return $this->any(null, null, $action); |
|
165 | } |
|
166 | ||
167 | ||
168 | ||
169 | /** |
|
170 | * Add a default route which will be applied for any path, if the choosen |
|
171 | * request method is matching. |
|
172 | * |
|
173 | * @param null|string|array $method as request methods |
|
174 | * @param null|string|callable $action a callback handler for the route. |
|
175 | * |
|
176 | * @return class|array as new route(s), class if one added, else array. |
|
177 | */ |
|
178 | public function all($method, $action) |
|
179 | { |
|
180 | return $this->any($method, null, $action); |
|
181 | } |
|
182 | ||
183 | ||
184 | ||
185 | /** |
|
186 | * Shortcut to add a GET route. |
|
187 | * |
|
188 | * @param null|string|array $method as request methods |
|
189 | * @param null|string|callable $action a callback handler for the route. |
|
190 | * |
|
191 | * @return class|array as new route(s), class if one added, else array. |
|
192 | */ |
|
193 | public function get($rule, $action) |
|
194 | { |
|
195 | return $this->any(["GET"], $rule, $action); |
|
196 | } |
|
197 | ||
198 | ||
199 | ||
200 | /** |
|
201 | * Shortcut to add a POST route. |
|
202 | * |
|
203 | * @param null|string|array $method as request methods |
|
204 | * @param null|string|callable $action a callback handler for the route. |
|
205 | * |
|
206 | * @return class|array as new route(s), class if one added, else array. |
|
207 | */ |
|
208 | public function post($rule, $action) |
|
209 | { |
|
210 | return $this->any(["POST"], $rule, $action); |
|
211 | } |
|
212 | ||
213 | ||
214 | ||
215 | /** |
|
216 | * Shortcut to add a PUT route. |
|
217 | * |
|
218 | * @param null|string|array $method as request methods |
|
219 | * @param null|string|callable $action a callback handler for the route. |
|
220 | * |
|
221 | * @return class|array as new route(s), class if one added, else array. |
|
222 | */ |
|
223 | public function put($rule, $action) |
|
224 | { |
|
225 | return $this->any(["PUT"], $rule, $action); |
|
226 | } |
|
227 | ||
228 | ||
229 | ||
230 | /** |
|
231 | * Shortcut to add a DELETE route. |
|
232 | * |
|
233 | * @param null|string|array $method as request methods |
|
234 | * @param null|string|callable $action a callback handler for the route. |
|
235 | * |
|
236 | * @return class|array as new route(s), class if one added, else array. |
|
237 | */ |
|
238 | public function delete($rule, $action) |
|
239 | { |
|
240 | return $this->any(["DELETE"], $rule, $action); |
|
241 | } |
|
242 | ||
243 | ||
244 | ||
245 | /** |
|
246 | * Add an internal route to the router, this route is not exposed to the |
|
247 | * browser and the end user. |
|
248 | * |
|
249 | * @param string $rule for this route |
|
250 | * @param null|string|callable $action a callback handler for the route. |
|
251 | * |
|
252 | * @return class|array as new route(s), class if one added, else array. |
|
253 | */ |
|
254 | public function addInternal($rule, $action) |
|
255 | { |
|
256 | $route = new Route(); |
|
257 | $route->set($rule, $action); |
|
258 | $this->internalRoutes[$rule] = $route; |
|
259 | return $route; |
|
260 | } |
|
261 | ||
262 | ||
263 | ||
264 | /** |
|
265 | * Get the route for the last route that was handled. |
|
266 | * |
|
267 | * @return mixed |
|
268 | */ |
|
269 | public function getLastRoute() |
|
270 | { |
|
271 | return $this->lastRoute; |
|
272 | } |
|
273 | ||
274 | ||
275 | ||
276 | /** |
|
277 | * Get all routes. |
|
278 | * |
|
279 | * @return array with all routes. |
|
280 | */ |
|
281 | public function getAll() |
|
282 | { |
|
283 | return $this->routes; |
|
284 | } |
|
285 | ||
286 | ||
287 | ||
288 | /** |
|
289 | * Get all internal routes. |
|
290 | * |
|
291 | * @return array with internal routes. |
|
292 | */ |
|
293 | public function getInternal() |
|
294 | { |
|
295 | return $this->internalRoutes; |
|
296 | } |
|
297 | } |
|
298 |
@@ 11-297 (lines=287) @@ | ||
8 | /** |
|
9 | * A container for routes. |
|
10 | */ |
|
11 | class RouterInjectable implements AppInjectableInterface |
|
12 | { |
|
13 | use AppInjectableTrait; |
|
14 | ||
15 | /** |
|
16 | * @var array $routes all the routes. |
|
17 | * @var array $internalRoutes all internal routes. |
|
18 | * @var null|string $lastRoute last route that was matched and called. |
|
19 | */ |
|
20 | private $routes = []; |
|
21 | private $internalRoutes = []; |
|
22 | private $lastRoute = null; |
|
23 | ||
24 | ||
25 | ||
26 | /** |
|
27 | * Handle the routes and match them towards the request, dispatch them |
|
28 | * when a match is made. Each route handler may throw exceptions that |
|
29 | * may redirect to an internal route for error handling. |
|
30 | * Several routes can match and if the routehandler does not break |
|
31 | * execution flow, the route matching will carry on. |
|
32 | * Only the last routehandler will get its return value returned further. |
|
33 | * |
|
34 | * @param string $path the path to find a matching handler for. |
|
35 | * @param string $method the request method to match. |
|
36 | * |
|
37 | * @return mixed content returned from route. |
|
38 | */ |
|
39 | public function handle($path, $method = null) |
|
40 | { |
|
41 | try { |
|
42 | $match = false; |
|
43 | foreach ($this->routes as $route) { |
|
44 | if ($route->match($path, $method)) { |
|
45 | $this->lastRoute = $route->getRule(); |
|
46 | $match = true; |
|
47 | $results = $route->handle($this->app); |
|
48 | } |
|
49 | } |
|
50 | ||
51 | if ($match) { |
|
52 | return $results; |
|
53 | } |
|
54 | ||
55 | $this->handleInternal("404"); |
|
56 | } catch (ForbiddenException $e) { |
|
57 | $this->handleInternal("403"); |
|
58 | } catch (NotFoundException $e) { |
|
59 | $this->handleInternal("404"); |
|
60 | } catch (InternalErrorException $e) { |
|
61 | $this->handleInternal("500"); |
|
62 | } |
|
63 | } |
|
64 | ||
65 | ||
66 | ||
67 | /** |
|
68 | * Handle an internal route, the internal routes are not exposed to the |
|
69 | * end user. |
|
70 | * |
|
71 | * @param string $rule for this route. |
|
72 | * |
|
73 | * @return void |
|
74 | * |
|
75 | * @throws \Anax\Route\NotFoundException |
|
76 | */ |
|
77 | public function handleInternal($rule) |
|
78 | { |
|
79 | if (!isset($this->internalRoutes[$rule])) { |
|
80 | throw new NotFoundException("No internal route to handle: " . $rule); |
|
81 | } |
|
82 | $route = $this->internalRoutes[$rule]; |
|
83 | $this->lastRoute = $rule; |
|
84 | $route->handle(); |
|
85 | } |
|
86 | ||
87 | ||
88 | ||
89 | /** |
|
90 | * Load routes from a config file, the file should return an array with |
|
91 | * routes. |
|
92 | * |
|
93 | * @param string $file to load routes from. |
|
94 | * |
|
95 | * @return self |
|
96 | */ |
|
97 | public function load($file) |
|
98 | { |
|
99 | $config = require $file; |
|
100 | foreach ($config["routes"] as $route) { |
|
101 | $this->any( |
|
102 | $route["requestMethod"], |
|
103 | $route["path"], |
|
104 | $route["callable"] |
|
105 | ); |
|
106 | } |
|
107 | return $this; |
|
108 | } |
|
109 | ||
110 | ||
111 | ||
112 | /** |
|
113 | * Add a route with a request method, a path rule to match and an action |
|
114 | * as the callback. Adding several path rules (array) results in several |
|
115 | * routes being created. |
|
116 | * |
|
117 | * @param null|string|array $method as a valid request method. |
|
118 | * @param null|string|array $rule path rule for this route. |
|
119 | * @param null|string|callable $action to implement a handler for the route. |
|
120 | * |
|
121 | * @return class|array as new route(s), class if one added, else array. |
|
122 | */ |
|
123 | public function any($method, $rule, $action) |
|
124 | { |
|
125 | $rules = is_array($rule) ? $rule : [$rule]; |
|
126 | ||
127 | $routes = []; |
|
128 | foreach ($rules as $val) { |
|
129 | $route = new Route(); |
|
130 | $route->set($val, $action, $method); |
|
131 | $routes[] = $route; |
|
132 | $this->routes[] = $route; |
|
133 | } |
|
134 | ||
135 | return count($routes) === 1 ? $routes[0] : $routes; |
|
136 | } |
|
137 | ||
138 | ||
139 | ||
140 | /** |
|
141 | * Add a route to the router by rule(s) and a callback. |
|
142 | * |
|
143 | * @param null|string|array $rule for this route. |
|
144 | * @param null|string|callable $action a callback handler for the route. |
|
145 | * |
|
146 | * @return class|array as new route(s), class if one added, else array. |
|
147 | */ |
|
148 | public function add($rule, $action = null) |
|
149 | { |
|
150 | return $this->any(null, $rule, $action); |
|
151 | } |
|
152 | ||
153 | ||
154 | ||
155 | /** |
|
156 | * Add a default route which will be applied for any path. |
|
157 | * |
|
158 | * @param string|callable $action a callback handler for the route. |
|
159 | * |
|
160 | * @return class as new route. |
|
161 | */ |
|
162 | public function always($action) |
|
163 | { |
|
164 | return $this->any(null, null, $action); |
|
165 | } |
|
166 | ||
167 | ||
168 | ||
169 | /** |
|
170 | * Add a default route which will be applied for any path, if the choosen |
|
171 | * request method is matching. |
|
172 | * |
|
173 | * @param null|string|array $method as request methods |
|
174 | * @param null|string|callable $action a callback handler for the route. |
|
175 | * |
|
176 | * @return class|array as new route(s), class if one added, else array. |
|
177 | */ |
|
178 | public function all($method, $action) |
|
179 | { |
|
180 | return $this->any($method, null, $action); |
|
181 | } |
|
182 | ||
183 | ||
184 | ||
185 | /** |
|
186 | * Shortcut to add a GET route. |
|
187 | * |
|
188 | * @param null|string|array $method as request methods |
|
189 | * @param null|string|callable $action a callback handler for the route. |
|
190 | * |
|
191 | * @return class|array as new route(s), class if one added, else array. |
|
192 | */ |
|
193 | public function get($rule, $action) |
|
194 | { |
|
195 | return $this->any(["GET"], $rule, $action); |
|
196 | } |
|
197 | ||
198 | ||
199 | ||
200 | /** |
|
201 | * Shortcut to add a POST route. |
|
202 | * |
|
203 | * @param null|string|array $method as request methods |
|
204 | * @param null|string|callable $action a callback handler for the route. |
|
205 | * |
|
206 | * @return class|array as new route(s), class if one added, else array. |
|
207 | */ |
|
208 | public function post($rule, $action) |
|
209 | { |
|
210 | return $this->any(["POST"], $rule, $action); |
|
211 | } |
|
212 | ||
213 | ||
214 | ||
215 | /** |
|
216 | * Shortcut to add a PUT route. |
|
217 | * |
|
218 | * @param null|string|array $method as request methods |
|
219 | * @param null|string|callable $action a callback handler for the route. |
|
220 | * |
|
221 | * @return class|array as new route(s), class if one added, else array. |
|
222 | */ |
|
223 | public function put($rule, $action) |
|
224 | { |
|
225 | return $this->any(["PUT"], $rule, $action); |
|
226 | } |
|
227 | ||
228 | ||
229 | ||
230 | /** |
|
231 | * Shortcut to add a DELETE route. |
|
232 | * |
|
233 | * @param null|string|array $method as request methods |
|
234 | * @param null|string|callable $action a callback handler for the route. |
|
235 | * |
|
236 | * @return class|array as new route(s), class if one added, else array. |
|
237 | */ |
|
238 | public function delete($rule, $action) |
|
239 | { |
|
240 | return $this->any(["DELETE"], $rule, $action); |
|
241 | } |
|
242 | ||
243 | ||
244 | ||
245 | /** |
|
246 | * Add an internal route to the router, this route is not exposed to the |
|
247 | * browser and the end user. |
|
248 | * |
|
249 | * @param string $rule for this route |
|
250 | * @param null|string|callable $action a callback handler for the route. |
|
251 | * |
|
252 | * @return class|array as new route(s), class if one added, else array. |
|
253 | */ |
|
254 | public function addInternal($rule, $action) |
|
255 | { |
|
256 | $route = new Route(); |
|
257 | $route->set($rule, $action); |
|
258 | $this->internalRoutes[$rule] = $route; |
|
259 | return $route; |
|
260 | } |
|
261 | ||
262 | ||
263 | ||
264 | /** |
|
265 | * Get the route for the last route that was handled. |
|
266 | * |
|
267 | * @return mixed |
|
268 | */ |
|
269 | public function getLastRoute() |
|
270 | { |
|
271 | return $this->lastRoute; |
|
272 | } |
|
273 | ||
274 | ||
275 | ||
276 | /** |
|
277 | * Get all routes. |
|
278 | * |
|
279 | * @return array with all routes. |
|
280 | */ |
|
281 | public function getAll() |
|
282 | { |
|
283 | return $this->routes; |
|
284 | } |
|
285 | ||
286 | ||
287 | ||
288 | /** |
|
289 | * Get all internal routes. |
|
290 | * |
|
291 | * @return array with internal routes. |
|
292 | */ |
|
293 | public function getInternal() |
|
294 | { |
|
295 | return $this->internalRoutes; |
|
296 | } |
|
297 | } |
|
298 |