1
|
|
|
<?php namespace Comodojo\Dispatcher\Router; |
2
|
|
|
|
3
|
|
|
use \Comodojo\Foundation\Base\Configuration; |
4
|
|
|
use \Comodojo\Dispatcher\Components\AbstractModel; |
5
|
|
|
use \Comodojo\Dispatcher\Cache\RouterCache; |
6
|
|
|
use \Comodojo\Dispatcher\Request\Model as Request; |
7
|
|
|
use \Comodojo\Dispatcher\Router\Parser; |
8
|
|
|
use \Comodojo\Dispatcher\Router\Route; |
9
|
|
|
use \Comodojo\Dispatcher\Router\Model as Router; |
10
|
|
|
use \Comodojo\SimpleCache\Manager as SimpleCacheManager; |
11
|
|
|
use \Comodojo\Exception\DispatcherException; |
12
|
|
|
use \Psr\Log\LoggerInterface; |
13
|
|
|
use \Countable; |
14
|
|
|
use \Exception; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* @package Comodojo Dispatcher |
18
|
|
|
* @author Marco Giovinazzi <[email protected]> |
19
|
|
|
* @author Marco Castiello <[email protected]> |
20
|
|
|
* @license GPL-3.0+ |
21
|
|
|
* |
22
|
|
|
* LICENSE: |
23
|
|
|
* |
24
|
|
|
* This program is free software: you can redistribute it and/or modify |
25
|
|
|
* it under the terms of the GNU Affero General Public License as |
26
|
|
|
* published by the Free Software Foundation, either version 3 of the |
27
|
|
|
* License, or (at your option) any later version. |
28
|
|
|
* |
29
|
|
|
* This program is distributed in the hope that it will be useful, |
30
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
31
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
32
|
|
|
* GNU Affero General Public License for more details. |
33
|
|
|
* |
34
|
|
|
* You should have received a copy of the GNU Affero General Public License |
35
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
36
|
|
|
*/ |
37
|
|
|
|
38
|
|
|
class Table extends AbstractModel implements Countable { |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Types of route that this table will accept |
42
|
|
|
* @var array |
43
|
|
|
*/ |
44
|
|
|
const ALLOWED_ROUTES = [ "ROUTE", "REDIRECT", "ERROR" ]; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Current repository of routes |
48
|
|
|
* @var array |
49
|
|
|
*/ |
50
|
|
|
protected $routes = []; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var Parser |
54
|
|
|
*/ |
55
|
|
|
protected $parser; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @var RouterCache |
59
|
|
|
*/ |
60
|
|
|
protected $cache; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Table constructor |
64
|
|
|
* |
65
|
|
|
* @param Configuration $configuration |
66
|
|
|
* @param LoggerInterface $logger |
67
|
|
|
* @param SimpleCacheManager $cache |
68
|
|
|
*/ |
69
|
1 |
|
public function __construct( |
70
|
|
|
Configuration $configuration, |
71
|
|
|
LoggerInterface $logger, |
72
|
|
|
SimpleCacheManager $cache |
73
|
|
|
) { |
74
|
|
|
|
75
|
1 |
|
parent::__construct($configuration, $logger); |
76
|
|
|
|
77
|
1 |
|
$this->parser = new Parser($logger); |
78
|
1 |
|
$this->cache = new RouterCache($cache); |
79
|
|
|
|
80
|
1 |
|
$this->readCache(); |
81
|
|
|
|
82
|
1 |
|
} |
83
|
|
|
|
84
|
1 |
|
public function add( |
85
|
|
|
$route, |
86
|
|
|
$type, |
87
|
|
|
$class = null, |
88
|
|
|
array $parameters = [] |
89
|
|
|
) { |
90
|
|
|
|
91
|
1 |
|
$type = strtoupper($type); |
92
|
|
|
|
93
|
1 |
|
if ( !in_array($type, self::ALLOWED_ROUTES) ) { |
94
|
|
|
|
95
|
|
|
$this->getLogger()->error("Invalid route definition - unknown type $type for route $route)"); |
96
|
|
|
|
97
|
1 |
|
} else if ( $type == 'ROUTE' && empty($class) ) { |
98
|
|
|
|
99
|
|
|
$this->getLogger()->error("Invalid route definition - missing class for route $route)"); |
100
|
|
|
|
101
|
|
|
} else { |
102
|
|
|
|
103
|
1 |
|
$routeData = $this->get($route); |
104
|
|
|
|
105
|
1 |
|
if ( !is_null($routeData) ) { |
106
|
|
|
|
107
|
|
|
$this->updateRoute($routeData, $type, $class, $parameters); |
108
|
|
|
|
109
|
|
|
} else { |
110
|
|
|
|
111
|
1 |
|
$folders = explode("/", $route); |
112
|
|
|
|
113
|
1 |
|
$this->registerRoute($folders, $type, $class, $parameters); |
114
|
|
|
|
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
} |
118
|
|
|
|
119
|
1 |
|
return $this; |
120
|
|
|
|
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Get registered routes count |
125
|
|
|
* |
126
|
|
|
* @return int |
127
|
|
|
*/ |
128
|
|
|
public function count() { |
129
|
|
|
|
130
|
|
|
return count($this->routes); |
131
|
|
|
|
132
|
|
|
} |
133
|
|
|
|
134
|
1 |
|
public function getRoutes() { |
135
|
|
|
|
136
|
1 |
|
return $this->routes; |
137
|
|
|
|
138
|
|
|
} |
139
|
|
|
|
140
|
1 |
|
public function get($route) { |
141
|
|
|
|
142
|
1 |
|
$regex = $this->regex($route); |
143
|
|
|
|
144
|
1 |
|
if (isset($this->routes[$regex])) { |
145
|
1 |
|
return $this->routes[$regex]; |
146
|
|
|
} |
147
|
|
|
|
148
|
1 |
|
return null; |
149
|
|
|
|
150
|
|
|
} |
151
|
|
|
|
152
|
1 |
|
public function regex($route) { |
153
|
|
|
|
154
|
1 |
|
$folders = explode("/", $route); |
155
|
|
|
|
156
|
1 |
|
return $this->parser->read($folders); |
157
|
|
|
|
158
|
|
|
} |
159
|
|
|
|
160
|
1 |
|
public function remove($route) { |
161
|
|
|
|
162
|
1 |
|
$regex = $this->regex($route); |
163
|
|
|
|
164
|
1 |
|
$routes = $this->routes; |
165
|
|
|
|
166
|
1 |
|
if (isset($routes[$regex])) { |
167
|
|
|
|
168
|
1 |
|
unset($routes[$regex]); |
169
|
|
|
|
170
|
1 |
|
$this->routes = $routes; |
171
|
|
|
|
172
|
1 |
|
return true; |
173
|
|
|
|
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
return false; |
177
|
|
|
|
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
public function defaultRoute() { |
181
|
|
|
|
182
|
|
|
return $this->get('/'); |
183
|
|
|
|
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
public function load(array $routes) { |
187
|
|
|
|
188
|
|
|
if (!empty($routes)) { |
189
|
|
|
|
190
|
|
|
foreach ($routes as $name => $route) { |
191
|
|
|
|
192
|
|
|
$this->add( |
193
|
|
|
$route['route'], |
194
|
|
|
$route['type'], |
195
|
|
|
empty($route['class']) ? null : $route['class'], |
196
|
|
|
empty($route['parameters']) ? [] : $route['parameters'] |
197
|
|
|
); |
198
|
|
|
|
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
$count = $this->count(); |
204
|
|
|
$this->logger->debug("$count routes loaded in routing table"); |
205
|
|
|
|
206
|
|
|
$this->dumpCache(); |
207
|
|
|
|
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
// This method add a route to the supported list |
211
|
1 |
|
private function registerRoute($folders, $type, $class = null, array $parameters = []) { |
212
|
|
|
|
213
|
|
|
// The values associated with a route are as follows: |
214
|
|
|
// $route = new Route($this->router); |
|
|
|
|
215
|
1 |
|
$route = new Route(); |
216
|
|
|
|
217
|
1 |
|
$this->updateRoute($route, $type, $class, $parameters); |
218
|
|
|
|
219
|
|
|
// $route->setType($type) // Type of route |
|
|
|
|
220
|
|
|
// ->setClassName($class) // Class to be invoked |
|
|
|
|
221
|
|
|
// ->setParameters($parameters); // Parameters passed via the composer.json configuration (cache, ttl, etc...) |
|
|
|
|
222
|
|
|
|
223
|
1 |
|
$this->logger->debug("Route table - route: ".implode("/", $folders)); |
224
|
|
|
|
225
|
|
|
// This method generate a global regular expression which will be able to match all the URI supported by the route |
226
|
1 |
|
$regex = $this->parser->read($folders, $route); |
227
|
|
|
|
228
|
1 |
|
$this->logger->debug("Route table - route regex: $regex"); |
229
|
|
|
|
230
|
1 |
|
$this->routes = array_merge($this->routes, [$regex => $route]); |
231
|
|
|
|
232
|
1 |
|
} |
233
|
|
|
|
234
|
1 |
|
private function updateRoute(Route $route, $type, $class = null, array $parameters = []) { |
235
|
|
|
|
236
|
1 |
|
$route->setType($type) |
237
|
1 |
|
->setClassName($class) |
238
|
1 |
|
->setParameters($parameters); |
239
|
|
|
|
240
|
1 |
|
if ( !empty($parameters['redirect-code']) ) $route->setRedirectCode($parameters['redirect-code']); |
241
|
1 |
|
if ( !empty($parameters['redirect-location']) ) $route->setRedirectLocation($parameters['redirect-location']); |
242
|
1 |
|
if ( !empty($parameters['redirect-message']) ) $route->setRedirectLocation($parameters['redirect-message']); |
243
|
1 |
|
if ( !empty($parameters['redirect-type']) ) $route->setRedirectType($parameters['redirect-type']); |
244
|
|
|
|
245
|
1 |
|
if ( !empty($parameters['error-code']) ) $route->setErrorCode($parameters['error-code']); |
246
|
1 |
|
if ( !empty($parameters['error-message']) ) $route->setErrorMessage($parameters['error-message']); |
247
|
|
|
|
248
|
1 |
|
} |
249
|
|
|
|
250
|
1 |
|
private function readCache() { |
251
|
|
|
|
252
|
1 |
|
if ($this->configuration->get('routing-table-cache') !== true) return; |
253
|
|
|
|
254
|
|
|
$data = $this->cache->read(); |
255
|
|
|
|
256
|
|
|
if (is_null($data)) { |
257
|
|
|
|
258
|
|
|
$this->routes = []; |
259
|
|
|
|
260
|
|
|
return; |
261
|
|
|
|
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
$this->routes = $data; |
265
|
|
|
|
266
|
|
|
$this->logger->debug("Routing table loaded from cache"); |
267
|
|
|
|
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
private function dumpCache() { |
271
|
|
|
|
272
|
|
|
if ($this->configuration->get('routing-table-cache') !== true) return; |
273
|
|
|
|
274
|
|
|
$ttl = $this->configuration->get('routing-table-ttl'); |
275
|
|
|
|
276
|
|
|
if ($this->cache->dump($this->routes, $ttl)) { |
277
|
|
|
$this->logger->debug("Routing table saved to cache"); |
278
|
|
|
} else { |
279
|
|
|
$this->logger->warning("Cannot save routing table to cache"); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
} |
285
|
|
|
|
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.