1
|
|
|
<?php |
|
|
|
|
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Codeburner Framework. |
5
|
|
|
* |
6
|
|
|
* @author Alex Rohleder <[email protected]> |
7
|
|
|
* @copyright 2016 Alex Rohleder |
8
|
|
|
* @license http://opensource.org/licenses/MIT |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace Codeburner\Router; |
12
|
|
|
|
13
|
|
|
use Codeburner\Router\Exceptions\BadRouteException; |
14
|
|
|
use Codeburner\Router\Exceptions\MethodNotSupportedException; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Explicit Avoiding autoload for classes and traits |
18
|
|
|
* that are aways needed. Don't need an condition of class exists |
19
|
|
|
* because the routes will not be used until the collector is used. |
20
|
|
|
*/ |
21
|
|
|
|
22
|
1 |
|
if (!class_exists(Parser::class, false)) { |
23
|
|
|
include __DIR__ . "/Parser.php"; |
24
|
|
|
} |
25
|
|
|
|
26
|
1 |
|
include __DIR__ . "/Route.php"; |
27
|
1 |
|
include __DIR__ . "/Group.php"; |
28
|
1 |
|
include __DIR__ . "/Collectors/ControllerCollectorTrait.php"; |
29
|
1 |
|
include __DIR__ . "/Collectors/ResourceCollectorTrait.php"; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* The Collector class hold, parse and build routes. |
33
|
|
|
* |
34
|
|
|
* @author Alex Rohleder <[email protected]> |
35
|
|
|
*/ |
36
|
|
|
|
37
|
|
|
class Collector |
38
|
|
|
{ |
39
|
|
|
|
40
|
|
|
use Collectors\ControllerCollectorTrait; |
41
|
|
|
use Collectors\ResourceCollectorTrait; |
42
|
|
|
|
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* All the supported http methods separated by spaces. |
46
|
|
|
* |
47
|
|
|
* @var string |
48
|
|
|
*/ |
49
|
|
|
|
50
|
|
|
const HTTP_METHODS = "get post put patch delete"; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* The static routes are simple stored in a multidimensional array, the first |
54
|
|
|
* dimension is indexed by an http method and hold an array indexed with the patterns |
55
|
|
|
* and holding the route. ex. [METHOD => [PATTERN => ROUTE]] |
56
|
|
|
* |
57
|
|
|
* @var array |
58
|
|
|
*/ |
59
|
|
|
|
60
|
|
|
protected $statics = []; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* The dynamic routes have parameters and are stored in a hashtable that every cell have |
64
|
|
|
* an array with route patterns as indexes and routes as values. ex. [INDEX => [PATTERN => ROUTE]] |
65
|
|
|
* |
66
|
|
|
* @var array |
67
|
|
|
*/ |
68
|
|
|
|
69
|
|
|
protected $dynamics = []; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* The pattern parser instance. |
73
|
|
|
* |
74
|
|
|
* @var Parser |
75
|
|
|
*/ |
76
|
|
|
|
77
|
|
|
protected $parser; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Collector constructor. |
81
|
|
|
* |
82
|
|
|
* @param Parser|null $parser |
83
|
|
|
*/ |
84
|
|
|
|
85
|
54 |
|
public function __construct(Parser $parser = null) |
86
|
|
|
{ |
87
|
54 |
|
$this->parser = $parser ?: new Parser; |
88
|
54 |
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* @param string $method |
92
|
|
|
* @param string $pattern |
93
|
|
|
* @param callable $action |
94
|
|
|
* |
95
|
|
|
* @throws BadRouteException |
96
|
|
|
* @throws MethodNotSupportedException |
97
|
|
|
* |
98
|
|
|
* @return Group |
99
|
|
|
*/ |
100
|
|
|
|
101
|
54 |
|
public function set($method, $pattern, $action) |
102
|
|
|
{ |
103
|
54 |
|
$method = $this->getValidMethod($method); |
104
|
53 |
|
$patterns = $this->parser->parsePattern($pattern); |
105
|
50 |
|
$group = new Group; |
106
|
|
|
|
107
|
50 |
|
foreach ($patterns as $pattern) |
108
|
|
|
{ |
109
|
50 |
|
$route = new Route($this, $method, $pattern, $action); |
110
|
50 |
|
$group->setRoute($route); |
111
|
|
|
|
112
|
50 |
|
if (strpos($pattern, "{") !== false) { |
113
|
31 |
|
$index = $this->getDynamicIndex($method, $pattern); |
114
|
31 |
|
$this->dynamics[$index][$pattern] = $route; |
115
|
50 |
|
} else $this->statics[$method][$pattern] = $route; |
116
|
50 |
|
} |
117
|
|
|
|
118
|
50 |
|
return $group; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
public function get ($pattern, $action) { return $this->set("get" , $pattern, $action); } |
122
|
|
|
public function post ($pattern, $action) { return $this->set("post" , $pattern, $action); } |
123
|
|
|
public function put ($pattern, $action) { return $this->set("put" , $pattern, $action); } |
124
|
|
|
public function patch ($pattern, $action) { return $this->set("patch" , $pattern, $action); } |
125
|
|
|
public function delete($pattern, $action) { return $this->set("delete", $pattern, $action); } |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Insert a route into several http methods. |
129
|
|
|
* |
130
|
|
|
* @param string[] $methods |
131
|
|
|
* @param string $pattern |
132
|
|
|
* @param callable $action |
133
|
|
|
* |
134
|
|
|
* @return Group |
135
|
|
|
*/ |
136
|
|
|
|
137
|
3 |
|
public function match(array $methods, $pattern, $action) |
138
|
|
|
{ |
139
|
3 |
|
$group = new Group; |
140
|
3 |
|
foreach ($methods as $method) |
141
|
3 |
|
$group->set($this->set($method, $pattern, $action)); |
142
|
3 |
|
return $group; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Insert a route into every http method supported. |
147
|
|
|
* |
148
|
|
|
* @param string $pattern |
149
|
|
|
* @param callable $action |
150
|
|
|
* |
151
|
|
|
* @return Group |
152
|
|
|
*/ |
153
|
|
|
|
154
|
1 |
|
public function any($pattern, $action) |
155
|
|
|
{ |
156
|
1 |
|
return $this->match(explode(" ", self::HTTP_METHODS), $pattern, $action); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Insert a route into every http method supported but the given ones. |
161
|
|
|
* |
162
|
|
|
* @param string $methods |
163
|
|
|
* @param string $pattern |
164
|
|
|
* @param callable $action |
165
|
|
|
* |
166
|
|
|
* @return Group |
167
|
|
|
*/ |
168
|
|
|
|
169
|
1 |
|
public function except($methods, $pattern, $action) |
170
|
|
|
{ |
171
|
1 |
|
return $this->match(array_diff(explode(" ", self::HTTP_METHODS), (array) $methods), $pattern, $action); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Group all given routes. |
176
|
|
|
* |
177
|
|
|
* @param Route[] $routes |
178
|
|
|
* @return Group |
179
|
|
|
*/ |
180
|
|
|
|
181
|
9 |
|
public function group(array $routes) |
182
|
|
|
{ |
183
|
9 |
|
$group = new Group; |
184
|
9 |
|
foreach ($routes as $route) |
185
|
9 |
|
$group->set($route); |
186
|
9 |
|
return $group; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Remove a route from collector. |
191
|
|
|
* |
192
|
|
|
* @param string $method |
193
|
|
|
* @param string $pattern |
194
|
|
|
*/ |
195
|
|
|
|
196
|
6 |
|
public function forget($method, $pattern) |
197
|
|
|
{ |
198
|
6 |
|
if (strpos($pattern, "{") === false) { |
199
|
6 |
|
unset($this->statics[$method][$pattern]); |
200
|
6 |
|
} else unset($this->dynamics[$this->getDynamicIndex($method, $pattern)][$pattern]); |
201
|
6 |
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* @param string $method |
205
|
|
|
* @param string $pattern |
206
|
|
|
* |
207
|
|
|
* @return Route|false |
208
|
|
|
*/ |
209
|
|
|
|
210
|
48 |
|
public function findStaticRoute($method, $pattern) |
211
|
|
|
{ |
212
|
48 |
|
$method = strtolower($method); |
213
|
48 |
|
if (isset($this->statics[$method]) && isset($this->statics[$method][$pattern])) |
214
|
48 |
|
return $this->statics[$method][$pattern]; |
215
|
35 |
|
return false; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* @param string $method |
220
|
|
|
* @param string $pattern |
221
|
|
|
* |
222
|
|
|
* @return array|false |
223
|
|
|
*/ |
224
|
|
|
|
225
|
35 |
|
public function findDynamicRoutes($method, $pattern) |
226
|
|
|
{ |
227
|
35 |
|
$index = $this->getDynamicIndex($method, $pattern); |
228
|
35 |
|
return isset($this->dynamics[$index]) ? $this->dynamics[$index] : false; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* @param string $method |
233
|
|
|
* @param string $pattern |
234
|
|
|
* |
235
|
|
|
* @return int |
236
|
|
|
*/ |
237
|
|
|
|
238
|
35 |
|
protected function getDynamicIndex($method, $pattern) |
239
|
|
|
{ |
240
|
35 |
|
return crc32(strtolower($method)) + substr_count($pattern, "/"); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Determine if the http method is valid. |
245
|
|
|
* |
246
|
|
|
* @param string $method |
247
|
|
|
* |
248
|
|
|
* @throws MethodNotSupportedException |
249
|
|
|
* @return string |
250
|
|
|
*/ |
251
|
|
|
|
252
|
54 |
|
protected function getValidMethod($method) |
253
|
|
|
{ |
254
|
54 |
|
$method = strtolower($method); |
255
|
|
|
|
256
|
54 |
|
if (strpos(self::HTTP_METHODS, $method) === false) { |
257
|
1 |
|
throw new MethodNotSupportedException($method); |
258
|
|
|
} |
259
|
|
|
|
260
|
53 |
|
return $method; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* @return string[] |
265
|
|
|
*/ |
266
|
|
|
|
267
|
|
|
public function getWildcards() |
268
|
|
|
{ |
269
|
|
|
return $this->parser->getWildcards(); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* @return string[] |
274
|
|
|
*/ |
275
|
|
|
|
276
|
|
|
public function getWildcardTokens() |
277
|
|
|
{ |
278
|
|
|
return $this->parser->getWildcardTokens(); |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* @param string $wildcard |
283
|
|
|
* @return string|null |
284
|
|
|
*/ |
285
|
|
|
|
286
|
|
|
public function getWildcard($wildcard) |
287
|
|
|
{ |
288
|
|
|
return $this->parser->getWildcard($wildcard); |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* @param string $wildcard |
293
|
|
|
* @param string $pattern |
294
|
|
|
* |
295
|
|
|
* @return self |
296
|
|
|
*/ |
297
|
|
|
|
298
|
|
|
public function setWildcard($wildcard, $pattern) |
299
|
|
|
{ |
300
|
|
|
$this->parser->setWildcard($wildcard, $pattern); |
301
|
|
|
return $this; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
/** |
305
|
|
|
* @return Parser |
306
|
|
|
*/ |
307
|
|
|
|
308
|
30 |
|
public function getParser() |
309
|
|
|
{ |
310
|
30 |
|
return $this->parser; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* @param Parser $parser |
315
|
|
|
* |
316
|
|
|
* @throws \LogicException |
317
|
|
|
* @return self |
318
|
|
|
*/ |
319
|
|
|
|
320
|
1 |
|
public function setParser(Parser $parser) |
321
|
|
|
{ |
322
|
1 |
|
if (!empty($this->statics) || !empty($this->dynamics)) { |
323
|
1 |
|
throw new \LogicException("You can't define a route parser after registering a route."); |
324
|
|
|
} |
325
|
|
|
|
326
|
1 |
|
$this->parser = $parser; |
327
|
1 |
|
return $this; |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
} |
331
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.