1
|
|
|
<?php |
2
|
|
|
namespace wmlib\controller; |
3
|
|
|
|
4
|
|
|
|
5
|
|
|
use wmlib\controller\Exception\RouteNotFoundException; |
6
|
|
|
|
7
|
|
|
class Router extends Route |
8
|
|
|
{ |
9
|
|
|
const URI_DELIMITER = '/'; |
10
|
|
|
const URL_VARIABLE_PATTERN = '\:(\(([^\)]+)\))?(\w+)'; |
11
|
|
|
const DEFAULT_REGEX = '[^\/]+'; |
12
|
|
|
|
13
|
|
|
protected $_names = []; |
14
|
|
|
protected $_routes; |
15
|
|
|
|
16
|
|
|
private $_initialized = false; |
17
|
|
|
private $_callback; |
18
|
|
|
|
19
|
|
|
private $childs = array(); |
20
|
|
|
|
21
|
|
|
|
22
|
9 |
|
protected function __construct(callable $callback = null) |
23
|
|
|
{ |
24
|
9 |
|
parent::__construct(); |
25
|
|
|
|
26
|
9 |
|
$this->_callback = $callback; |
27
|
9 |
|
$this->_routes = new \SplObjectStorage(); |
28
|
|
|
|
29
|
9 |
|
} |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Delayed route initilization |
33
|
|
|
* |
34
|
|
|
*/ |
35
|
8 |
|
protected function init() |
36
|
|
|
{ |
37
|
8 |
|
} |
38
|
|
|
|
39
|
8 |
|
final protected function _initialize() |
40
|
|
|
{ |
41
|
8 |
|
if (!$this->_initialized) { |
42
|
8 |
|
$this->init(); |
43
|
|
|
|
44
|
8 |
|
if (($this->_callback !== null) && is_callable($this->_callback)) { |
45
|
8 |
|
if ($this->_callback instanceof \Closure) { |
46
|
8 |
|
call_user_func_array($this->_callback->bindTo($this), array($this)); |
47
|
8 |
|
} else { |
48
|
|
|
call_user_func_array($this->_callback, array($this)); |
49
|
|
|
} |
50
|
8 |
|
} |
51
|
|
|
|
52
|
|
|
|
53
|
8 |
|
$this->_initialized = true; |
54
|
8 |
|
} |
55
|
|
|
|
56
|
8 |
|
return $this; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @param Route|string $route |
61
|
|
|
* @return bool |
62
|
|
|
*/ |
63
|
2 |
|
public function removeRoute($route) |
64
|
|
|
{ |
65
|
|
|
$this->_initialize(); |
66
|
|
|
|
67
|
|
|
$removed = false; |
68
|
|
|
if ($route instanceof Route) { |
69
|
|
|
if ($this->_routes->contains($route)) { |
70
|
|
|
$this->_routes->detach($route); |
71
|
|
|
$removed = true; |
72
|
2 |
|
} |
73
|
|
|
} elseif (is_string($route) && isset($this->_names[$route])) { |
74
|
|
|
$removed = $this->removeRoute($this->_names[$route]); |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
if ($removed) { |
78
|
|
|
$this->childs = []; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
return $removed; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Add route |
86
|
|
|
* |
87
|
|
|
* @param $pattern |
88
|
|
|
* @param Route $route |
89
|
|
|
* @param null $name |
90
|
|
|
* @param array $attributes |
91
|
|
|
* @return Route Added route fluent API support |
92
|
|
|
*/ |
93
|
8 |
|
public function addRoute($pattern, Route $route, $name = null, array $attributes = []) |
94
|
|
|
{ |
95
|
8 |
|
if ($name && isset($this->_names[$name])) { |
96
|
|
|
throw new \OutOfBoundsException('Route with "' . $name . '" already exists'); |
97
|
8 |
|
} elseif ($route instanceof Router) { |
98
|
8 |
|
$this->_routes->addAll($route->_routes); |
99
|
8 |
|
$route->_routes = $this->_routes; |
100
|
8 |
|
} |
101
|
8 |
|
$this->_routes->attach($route, [ |
102
|
8 |
|
'pattern' => $pattern, |
103
|
8 |
|
'name' => $name, |
104
|
8 |
|
'attributes' => $attributes, |
105
|
|
|
'router' => $this |
106
|
8 |
|
]); |
107
|
|
|
|
108
|
8 |
|
if ($name) { |
109
|
8 |
|
$this->_names[$name] = $route; |
110
|
8 |
|
} |
111
|
|
|
|
112
|
8 |
|
if (!$route->logger && $this->logger) { |
113
|
|
|
$route->logger = $this->logger; |
114
|
|
|
} |
115
|
|
|
|
116
|
8 |
|
$this->childs = []; |
117
|
|
|
|
118
|
8 |
|
return $route; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Get routing object by name. Look in overall tree |
123
|
|
|
* |
124
|
|
|
* @throws \OutOfBoundsException |
125
|
|
|
* @param string|array $nameOrNames |
126
|
|
|
* @return Route |
127
|
|
|
*/ |
128
|
3 |
|
public function getRoute($nameOrNames) |
129
|
|
|
{ |
130
|
3 |
|
$this->_initialize(); |
131
|
|
|
|
132
|
3 |
|
if (func_num_args() > 1) { |
133
|
1 |
|
$nameOrNames = func_get_args(); |
134
|
1 |
|
} |
135
|
|
|
|
136
|
3 |
|
if (is_array($nameOrNames)) { |
137
|
3 |
|
$name = array_shift($nameOrNames); |
138
|
3 |
|
} else { |
139
|
2 |
|
$name = $nameOrNames; |
140
|
2 |
|
$nameOrNames = []; |
141
|
|
|
} |
142
|
|
|
|
143
|
3 |
|
if (isset($this->_names[$name])) { |
144
|
3 |
|
$route = $this->_names[$name]; |
145
|
3 |
|
if (empty($nameOrNames)) { |
146
|
3 |
|
return $route; |
147
|
1 |
|
} elseif ($route instanceof Router) { |
148
|
1 |
|
return $route->getRoute($nameOrNames); |
149
|
|
|
} else { |
150
|
|
|
throw new \OutOfBoundsException(sprintf("Route %s should be Router instance", $name)); |
151
|
|
|
} |
152
|
|
|
} else { |
153
|
2 |
|
foreach ($this->_routes as $route) { |
154
|
2 |
|
if ($route instanceof Router) { |
155
|
|
|
try { |
156
|
2 |
|
return $route->getRoute([$name] + $nameOrNames); |
157
|
|
|
} catch (\OutOfBoundsException $e) { |
158
|
|
|
continue; |
159
|
|
|
} |
160
|
|
|
} |
161
|
2 |
|
} |
162
|
|
|
|
163
|
|
|
throw new \OutOfBoundsException(sprintf("Route %s not found in tree", $name)); |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* @param Route $child |
169
|
|
|
* @return null|string |
170
|
|
|
*/ |
171
|
|
|
public function getPattern(Route $child) |
172
|
|
|
{ |
173
|
|
|
$this->_initialize(); |
174
|
|
|
if ($this->_routes->contains($child)) { |
175
|
|
|
$info = $this->_routes->offsetGet($child); |
176
|
|
|
|
177
|
|
|
|
178
|
|
|
return $info['pattern']; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
|
182
|
|
|
return null; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* @param string $pattern_candidate |
187
|
|
|
* @return bool |
188
|
|
|
*/ |
189
|
|
|
public function hasPattern($pattern_candidate) |
190
|
|
|
{ |
191
|
|
|
foreach ($this->_initialize()->_routes as $route) { |
192
|
|
|
$info = $this->_routes->getInfo(); |
193
|
|
|
if (($info['router'] === $this) && $info['pattern'] === $pattern_candidate) { |
194
|
|
|
return true; |
195
|
|
|
} |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
|
199
|
|
|
return false; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @param Route $child |
204
|
|
|
* @return null|string |
205
|
|
|
*/ |
206
|
|
|
public function getName(Route $child) |
207
|
|
|
{ |
208
|
|
|
if ($this->_initialize()->_routes->contains($child)) { |
209
|
|
|
|
210
|
|
|
$data = $this->_initialize()->_routes->offsetGet($child); |
211
|
|
|
$router = $data['router']; |
212
|
|
|
|
213
|
|
|
return array_search($child, $router->_names); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
return null; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* @param Route $child |
221
|
|
|
* @return mixed[] |
222
|
|
|
*/ |
223
|
|
|
public function getAttributes(Route $child) |
224
|
|
|
{ |
225
|
|
|
if ($this->_initialize()->_routes->contains($child)) { |
226
|
|
|
|
227
|
|
|
$data = $this->_initialize()->_routes->offsetGet($child); |
228
|
|
|
$arguments = $data['attributes']; |
229
|
|
|
|
230
|
|
|
return $arguments; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
return []; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Check if route is matched to pattern |
238
|
|
|
* |
239
|
|
|
* @param $pattern |
240
|
|
|
* @param $uri |
241
|
|
|
* @param array $params |
242
|
|
|
* @return bool|string |
243
|
|
|
*/ |
244
|
4 |
|
protected static function _IsMatch($pattern, $uri, &$params = array()) |
245
|
|
|
{ |
246
|
4 |
|
$url = trim((string)$uri, self::URI_DELIMITER); |
247
|
4 |
|
$pattern = trim($pattern, self::URI_DELIMITER); |
248
|
|
|
|
249
|
4 |
|
while ($pattern) { |
250
|
4 |
|
if (preg_match('/^' . self::URL_VARIABLE_PATTERN . '/', $pattern, $matches)) { |
251
|
1 |
|
list($part, , $reg, $key) = $matches; |
252
|
|
|
|
253
|
1 |
|
$uri_pattern = $reg ? $reg : self::DEFAULT_REGEX; |
254
|
|
|
|
255
|
1 |
|
if (preg_match('/^' . $uri_pattern . '/i', $url, $url_matches)) { |
256
|
|
|
$params[$key] = ($variable_value = $url_matches[0]); |
257
|
|
|
$url = (string)substr($url, strlen($variable_value)); |
258
|
1 |
|
} elseif (!$url && isset($params[$key])) { |
259
|
1 |
|
$url = (string)substr($url, strlen($params[$key])); |
260
|
1 |
|
} else { |
261
|
|
|
return false; |
262
|
|
|
} |
263
|
|
|
|
264
|
1 |
|
$pattern = (string)substr($pattern, strlen($part)); |
265
|
4 |
|
} elseif (substr($pattern, 0, 1) === self::URI_DELIMITER) { |
266
|
3 |
|
$pattern = substr($pattern, 1); |
267
|
|
|
|
268
|
3 |
|
if (substr($url, 0, 1) === self::URI_DELIMITER) { |
269
|
|
|
$url = (string)substr($url, 1); |
270
|
3 |
|
} elseif ($url) { |
271
|
3 |
|
return false; |
272
|
|
|
} |
273
|
1 |
|
} else { |
274
|
4 |
|
$static = (($pos = strpos($pattern, self::URI_DELIMITER)) !== false) ? substr($pattern, 0, |
275
|
4 |
|
$pos) : $pattern; |
276
|
|
|
|
277
|
4 |
|
if ($static === substr($url, 0, $len = strlen($static))) { |
278
|
4 |
|
$pattern = (string)substr($pattern, $len); |
279
|
4 |
|
$url = (string)substr($url, $len); |
280
|
4 |
|
} else { |
281
|
4 |
|
return false; |
282
|
|
|
} |
283
|
|
|
} |
284
|
4 |
|
} |
285
|
|
|
|
286
|
4 |
|
return (!$pattern) ? $url : false; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
/** |
290
|
|
|
* Get matched route or null |
291
|
|
|
* |
292
|
|
|
* @param Url|string $uri |
293
|
|
|
* @param array $params |
294
|
|
|
* @param Url|null $matched |
295
|
|
|
* @return Route|null |
296
|
|
|
* @throws \Exception |
297
|
|
|
*/ |
298
|
4 |
|
public function getChild($uri, &$params = array(), &$matched = null) |
299
|
|
|
{ |
300
|
4 |
|
$key = md5((string)$uri . var_export($params, true)); |
301
|
|
|
|
302
|
4 |
|
if (isset($this->_initialize()->childs[$key])) { |
303
|
|
|
list($return, $found_params, $matched) = $this->childs[$key]; |
304
|
|
|
|
305
|
|
|
foreach ($found_params as $n => $v) { |
306
|
|
|
$params[$n] = $v; |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
return $return; |
310
|
|
|
} |
311
|
|
|
|
312
|
4 |
|
foreach ($this->_routes as $route) { |
313
|
4 |
|
$data = $this->_routes->getInfo(); |
314
|
|
|
|
315
|
4 |
|
if ($data['router'] === $this) { |
316
|
|
|
/** @var $route Route */ |
317
|
4 |
|
$match_params = array_merge($route->getDefault(), $data['attributes']); |
318
|
|
|
|
319
|
4 |
|
if (($suburl = self::_IsMatch($data['pattern'], $uri, $match_params)) !== false) { |
320
|
4 |
|
$params = array_merge($params, $match_params); |
321
|
|
|
|
322
|
4 |
|
if (($suburl === '') || ($route instanceof self)) { |
323
|
3 |
|
$matched = self::BuildUri($data['pattern'], $match_params, $data['attributes']); |
324
|
|
|
|
325
|
3 |
|
$this->childs[$key] = array($route, &$params, $matched); |
326
|
|
|
|
327
|
3 |
|
return $route; |
328
|
|
|
} |
329
|
1 |
|
} |
330
|
4 |
|
} |
331
|
4 |
|
} |
332
|
|
|
|
333
|
|
|
|
334
|
1 |
|
$this->childs[$key] = [null, &$params, $matched]; |
335
|
|
|
|
336
|
1 |
|
return null; |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* Build route uri |
341
|
|
|
* |
342
|
|
|
* @param $rule |
343
|
|
|
* @param array $params |
344
|
|
|
* @param bool $addMissedToQuery |
345
|
|
|
* @return Url |
346
|
|
|
* @throws \Exception Something goes wrong |
347
|
|
|
*/ |
348
|
4 |
|
public static function BuildUri($rule, $params = [], $arguments = [], $addMissedToQuery = true) |
349
|
|
|
{ |
350
|
4 |
|
static $Parsed = []; |
351
|
|
|
|
352
|
|
|
|
353
|
4 |
|
if (!isset($Parsed[$rule])) { |
354
|
3 |
|
$Parsed[$index = $rule] = []; |
355
|
3 |
|
while ($index) { |
356
|
3 |
|
if (preg_match('/^' . self::URL_VARIABLE_PATTERN . '/', $index, $matches)) { |
357
|
1 |
|
list($part, , , $key) = $matches; |
358
|
|
|
|
359
|
1 |
|
$Parsed[$rule][] = [1, $key]; |
360
|
1 |
|
$index = substr($index, strlen($part)); |
361
|
3 |
|
} elseif (substr($index, 0, 1) === self::URI_DELIMITER) { |
362
|
2 |
|
$index = substr($index, 1); |
363
|
|
|
|
364
|
|
|
//if (!$rule) |
|
|
|
|
365
|
2 |
|
$Parsed[$rule][] = [2, self::URI_DELIMITER]; |
366
|
2 |
|
} else { |
367
|
3 |
|
$static = (($pos = strpos($index, self::URI_DELIMITER)) !== false) ? substr($index, 0, |
368
|
3 |
|
$pos) : $index; |
369
|
|
|
|
370
|
3 |
|
$Parsed[$rule][] = [2, $static]; |
371
|
|
|
|
372
|
3 |
|
$index = substr($index, strlen($static)); |
373
|
|
|
} |
374
|
3 |
|
} |
375
|
3 |
|
} |
376
|
|
|
|
377
|
|
|
|
378
|
4 |
|
$parts = []; |
379
|
4 |
|
$missed = $params; |
380
|
|
|
|
381
|
4 |
|
foreach ($Parsed[$rule] as list($type, $part)) { |
382
|
|
|
|
383
|
4 |
|
if ($type === 1) { |
384
|
2 |
|
if (isset($params[$part])) { |
385
|
2 |
|
$parts[] = urlencode($params[$part]); |
386
|
2 |
|
if (isset($missed[$part])) { |
387
|
2 |
|
unset($missed[$part]); |
388
|
2 |
|
} |
389
|
2 |
|
} elseif (isset($arguments[$part])) { |
390
|
|
|
$parts[] = urlencode($arguments[$part]); |
391
|
|
|
} else { |
392
|
|
|
throw new \Exception(sprintf('Varible "%s" not specified for url pattern "%s"', $part, $rule)); |
393
|
|
|
} |
394
|
4 |
|
} elseif ($type === 2) { |
395
|
4 |
|
$parts[] = $part; |
396
|
4 |
|
} |
397
|
4 |
|
} |
398
|
|
|
|
399
|
4 |
|
$uri = new Url(implode('', $parts)); |
400
|
|
|
|
401
|
4 |
|
if ((sizeof($missed) > 0) && $addMissedToQuery) { |
402
|
2 |
|
$uri = $uri->withQueryValues($missed); |
403
|
2 |
|
} |
404
|
|
|
|
405
|
4 |
|
return $uri; |
406
|
|
|
} |
407
|
|
|
|
408
|
1 |
|
public function getMatched($uri, &$params = array()) |
409
|
|
|
{ |
410
|
1 |
|
$route = $this->getChild($uri, $params, $matched); |
411
|
|
|
|
412
|
1 |
|
if ($route instanceof Router) { |
413
|
1 |
|
$uri_obj = ($uri instanceof Url) ? $uri : new Url($uri); |
414
|
1 |
|
return $route->getMatched($uri_obj->getRelated($matched), $params); |
|
|
|
|
415
|
|
|
} |
416
|
1 |
|
return $route; |
417
|
|
|
|
418
|
|
|
} |
419
|
|
|
|
420
|
|
|
/** |
421
|
|
|
* @return \SplObjectStorage |
422
|
|
|
*/ |
423
|
|
|
public function getRoutes() |
424
|
|
|
{ |
425
|
|
|
return $this->_initialize()->_routes; |
426
|
|
|
} |
427
|
|
|
|
428
|
1 |
|
public function hasRoute($name) |
429
|
|
|
{ |
430
|
1 |
|
$this->_initialize(); |
431
|
|
|
|
432
|
1 |
|
$route = isset($this->_names[$name]) ? $this->_names[$name] : null; |
433
|
|
|
|
434
|
1 |
|
return ($route && $this->_routes->contains($route)); |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
protected function dispatchRoute(Request $request, Response $response, array $arguments = []) |
438
|
|
|
{ |
439
|
|
|
$params = array(); |
440
|
|
|
|
441
|
|
|
$matched = null; |
442
|
|
|
if (($route = $this->getChild($request->getUrlPath(), $params, $matched)) && $matched) { |
443
|
|
|
$subrequest = $request->withBaseUrl($matched); |
444
|
|
|
|
445
|
|
|
foreach ($this->properties as $name => $value) { |
446
|
|
|
$subrequest = $subrequest->withAttribute($name, $value); |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
foreach ($params as $name => $value) { |
450
|
|
|
$subrequest = $subrequest->withAttribute($name, $value); |
451
|
|
|
} |
452
|
|
|
foreach ($arguments as $name => $value) { |
453
|
|
|
$subrequest = $subrequest->withAttribute($name, $value); |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
if ($route instanceof Router) { |
457
|
|
|
$route->_initialize(); |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
return $route->dispatch($subrequest, $response); |
461
|
|
|
} else { |
462
|
|
|
return $this->dispatchHome($request, $response); |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
} |
466
|
|
|
|
467
|
1 |
|
public function url($uri) |
468
|
|
|
{ |
469
|
1 |
|
$params = array(); |
470
|
1 |
|
$route = $this->getMatched($uri, $params); |
471
|
|
|
|
472
|
1 |
|
if ($route instanceof Route) { |
473
|
1 |
|
$url = $this->uri($route, $params)->__toString(); |
474
|
1 |
|
return $url; |
475
|
|
|
} else { |
476
|
|
|
return $uri; |
477
|
|
|
} |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
/** |
481
|
|
|
* Dispatch home(root) if no matched child found |
482
|
|
|
* |
483
|
|
|
* @param Request $request |
484
|
|
|
* @param Response $response |
485
|
|
|
* @return Response |
486
|
|
|
* @throws RouteNotFoundException |
487
|
|
|
*/ |
488
|
|
|
protected function dispatchHome(Request $request, Response $response) |
|
|
|
|
489
|
|
|
{ |
490
|
|
|
$look_for = $request->getUrl(false); |
491
|
|
|
|
492
|
|
|
$patterns = array(); |
493
|
|
|
foreach ($this->_initialize()->_routes as $route) { |
494
|
|
|
$info = $this->_routes->getInfo(); |
495
|
|
|
|
496
|
|
|
if ($info['router'] === $this) { |
497
|
|
|
$patterns[] = $info['pattern']; |
498
|
|
|
} |
499
|
|
|
} |
500
|
|
|
|
501
|
|
|
throw new RouteNotFoundException(sprintf('No matched route found for %s app[%s], "%s" existed', $look_for, get_class($this), |
502
|
|
|
implode(', ', $patterns))); |
503
|
|
|
|
504
|
|
|
return $response; |
|
|
|
|
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
protected function find(callable $callback) |
508
|
|
|
{ |
509
|
|
|
foreach ($this->_names as $route_name => $route) { |
510
|
|
|
if ($callback($route)) { |
511
|
|
|
yield $route; |
512
|
|
|
} |
513
|
|
|
} |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
|
517
|
|
|
/** |
518
|
|
|
* Build route uri |
519
|
|
|
* |
520
|
|
|
* @param Route $route |
521
|
|
|
* @param array $params |
522
|
|
|
* @param bool $addMissedToQuery |
523
|
|
|
* @return Url |
524
|
|
|
* @throws \Exception |
525
|
|
|
*/ |
526
|
2 |
|
public function uri(Route $route, $params = array(), $addMissedToQuery = true) |
527
|
|
|
{ |
528
|
2 |
|
while ($this->_routes->contains($route)) { |
529
|
2 |
|
$info = $this->_routes->offsetGet($route); |
530
|
2 |
|
$pattern = $info['pattern']; |
531
|
|
|
|
532
|
2 |
|
$params = array_merge($route->default, (array)$params); |
533
|
|
|
|
534
|
2 |
|
$route_uri = self::BuildUri($pattern, $params, $info['attributes'], $addMissedToQuery); |
535
|
|
|
|
536
|
|
|
|
537
|
2 |
|
if (isset($uri)) { |
538
|
2 |
|
$uri = $route_uri->resolve($uri); |
539
|
2 |
|
} else { |
540
|
2 |
|
$uri = $route_uri; |
541
|
|
|
} |
542
|
|
|
|
543
|
|
|
|
544
|
2 |
|
$route = $info['router']; |
545
|
2 |
|
} |
546
|
|
|
|
547
|
2 |
|
if (isset($uri)) { |
548
|
2 |
|
return $uri; |
549
|
|
|
} else { |
550
|
|
|
return new Url('/'); |
551
|
|
|
} |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
/** |
555
|
|
|
* @return Router |
556
|
|
|
*/ |
557
|
|
|
public function getRoot() |
558
|
|
|
{ |
559
|
|
|
$route = $this; |
560
|
|
|
while ($this->_routes->contains($route)) { |
561
|
|
|
$info = $this->_routes->offsetGet($route); |
562
|
|
|
$route = $info['router']; |
563
|
|
|
} |
564
|
|
|
|
565
|
|
|
return $route; |
566
|
|
|
} |
567
|
|
|
} |
568
|
|
|
|
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.