Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
60 / 60 |
|
100.00% |
26 / 26 |
CRAP | |
100.00% |
1 / 1 |
Router | |
100.00% |
60 / 60 |
|
100.00% |
26 / 26 |
41 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
5 | |||
addRoute | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
addRoutes | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
name | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
hasName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUrl | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
addControllerParams | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
appendControllerParams | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getControllerParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasControllerParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
removeControllerParams | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getRoutes | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRouteMatch | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasRoute | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRouteParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasRouteParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getController | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasController | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAction | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasAction | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getControllerClass | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isCli | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isHttp | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
prepare | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
route | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
11 | |||
noRouteFound | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (https://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@noladev.com> |
7 | * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. |
8 | * @license https://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Router; |
15 | |
16 | use Closure; |
17 | use ReflectionException; |
18 | |
19 | /** |
20 | * Pop router class |
21 | * |
22 | * @category Pop |
23 | * @package Pop\Router |
24 | * @author Nick Sagona, III <dev@noladev.com> |
25 | * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. |
26 | * @license https://www.popphp.org/license New BSD License |
27 | * @version 4.3.7 |
28 | */ |
29 | class Router |
30 | { |
31 | |
32 | /** |
33 | * Route match object |
34 | * @var ?Match\MatchInterface |
35 | */ |
36 | protected ?Match\MatchInterface $routeMatch = null; |
37 | |
38 | /** |
39 | * Controller object |
40 | * @var mixed |
41 | */ |
42 | protected mixed $controller = null; |
43 | |
44 | /** |
45 | * Action |
46 | * @var mixed |
47 | */ |
48 | protected mixed $action = null; |
49 | |
50 | /** |
51 | * Controller class |
52 | * @var ?string |
53 | */ |
54 | protected ?string $controllerClass = null; |
55 | |
56 | /** |
57 | * Constructor |
58 | * |
59 | * Instantiate the router object |
60 | * |
61 | * @param ?array $routes |
62 | * @param ?Match\AbstractMatch $match |
63 | */ |
64 | public function __construct(?array $routes = null, ?Match\AbstractMatch $match = null) |
65 | { |
66 | if ($match !== null) { |
67 | $this->routeMatch = $match; |
68 | } else { |
69 | $this->routeMatch = ((stripos(php_sapi_name(), 'cli') !== false) && |
70 | (stripos(php_sapi_name(), 'server') === false)) ? |
71 | new Match\Cli() : new Match\Http(); |
72 | } |
73 | |
74 | if ($routes !== null) { |
75 | $this->addRoutes($routes); |
76 | } |
77 | } |
78 | |
79 | /** |
80 | * Add a route |
81 | * |
82 | * @param string $route |
83 | * @param mixed $controller |
84 | * @return static |
85 | */ |
86 | public function addRoute(string $route, mixed $controller): static |
87 | { |
88 | $this->routeMatch->addRoute($route, $controller); |
89 | return $this; |
90 | } |
91 | |
92 | /** |
93 | * Add multiple controller routes |
94 | * |
95 | * @param array $routes |
96 | * @return static |
97 | */ |
98 | public function addRoutes(array $routes): static |
99 | { |
100 | $this->routeMatch->addRoutes($routes); |
101 | return $this; |
102 | } |
103 | |
104 | /** |
105 | * Add a route name |
106 | * |
107 | * @param string $routeName |
108 | * @return Router |
109 | */ |
110 | public function name(string $routeName): static |
111 | { |
112 | $this->routeMatch->name($routeName); |
113 | return $this; |
114 | } |
115 | |
116 | /** |
117 | * Has a route name |
118 | * |
119 | * @param string $routeName |
120 | * @return bool |
121 | */ |
122 | public function hasName(string $routeName): bool |
123 | { |
124 | return $this->routeMatch->hasName($routeName); |
125 | } |
126 | |
127 | /** |
128 | * Get URL for the named route |
129 | * |
130 | * @param string $routeName |
131 | * @param mixed $params |
132 | * @param bool $fqdn |
133 | * @throws Exception |
134 | * @return string |
135 | */ |
136 | public function getUrl(string $routeName, mixed $params = null, bool $fqdn = false): string |
137 | { |
138 | if (!$this->isHttp()) { |
139 | throw new Exception('Error: The route is not HTTP.'); |
140 | } |
141 | return $this->routeMatch->getUrl($routeName, $params, $fqdn); |
142 | } |
143 | |
144 | /** |
145 | * Add controller params to be passed into a new controller instance |
146 | * |
147 | * @param string $controller |
148 | * @param mixed $params |
149 | * @return static |
150 | */ |
151 | public function addControllerParams(string $controller, mixed $params): static |
152 | { |
153 | $this->routeMatch->addControllerParams($controller, $params); |
154 | return $this; |
155 | } |
156 | |
157 | /** |
158 | * Append controller params to be passed into a new controller instance |
159 | * |
160 | * @param string $controller |
161 | * @param mixed $params |
162 | * @return static |
163 | */ |
164 | public function appendControllerParams(string $controller, mixed $params): static |
165 | { |
166 | $this->routeMatch->appendControllerParams($controller, $params); |
167 | return $this; |
168 | } |
169 | |
170 | /** |
171 | * Get the params assigned to the controller |
172 | * |
173 | * @param string $controller |
174 | * @return mixed |
175 | */ |
176 | public function getControllerParams(string $controller): mixed |
177 | { |
178 | return $this->routeMatch->getControllerParams($controller); |
179 | } |
180 | |
181 | /** |
182 | * Determine if the controller has params |
183 | * |
184 | * @param string $controller |
185 | * @return bool |
186 | */ |
187 | public function hasControllerParams(string $controller): bool |
188 | { |
189 | return $this->routeMatch->hasControllerParams($controller); |
190 | } |
191 | |
192 | /** |
193 | * Remove controller params |
194 | * |
195 | * @param string $controller |
196 | * @return static |
197 | */ |
198 | public function removeControllerParams(string $controller): static |
199 | { |
200 | $this->routeMatch->removeControllerParams($controller); |
201 | return $this; |
202 | } |
203 | |
204 | /** |
205 | * Get routes |
206 | * |
207 | * @return array |
208 | */ |
209 | public function getRoutes(): array |
210 | { |
211 | return $this->routeMatch->getRoutes(); |
212 | } |
213 | |
214 | /** |
215 | * Get route match object |
216 | * |
217 | * @return Match\MatchInterface |
218 | */ |
219 | public function getRouteMatch(): Match\MatchInterface |
220 | { |
221 | return $this->routeMatch; |
222 | } |
223 | |
224 | /** |
225 | * Determine if there is a route match |
226 | * |
227 | * @return bool |
228 | */ |
229 | public function hasRoute(): bool |
230 | { |
231 | return $this->routeMatch->hasRoute(); |
232 | } |
233 | |
234 | /** |
235 | * Get the params discovered from the route |
236 | * |
237 | * @return array |
238 | */ |
239 | public function getRouteParams(): array |
240 | { |
241 | return $this->routeMatch->getRouteParams(); |
242 | } |
243 | |
244 | /** |
245 | * Determine if the route has params |
246 | * |
247 | * @return bool |
248 | */ |
249 | public function hasRouteParams(): bool |
250 | { |
251 | return $this->routeMatch->hasRouteParams(); |
252 | } |
253 | |
254 | /** |
255 | * Get the current controller object |
256 | * |
257 | * @return mixed |
258 | */ |
259 | public function getController(): mixed |
260 | { |
261 | return $this->controller; |
262 | } |
263 | |
264 | /** |
265 | * Determine if the router has a controller |
266 | * |
267 | * @return bool |
268 | */ |
269 | public function hasController(): bool |
270 | { |
271 | return ($this->controller !== null); |
272 | } |
273 | |
274 | /** |
275 | * Get the action |
276 | * |
277 | * @return mixed |
278 | */ |
279 | public function getAction(): mixed |
280 | { |
281 | return $this->action; |
282 | } |
283 | |
284 | /** |
285 | * Determine if the router has an action |
286 | * |
287 | * @return bool |
288 | */ |
289 | public function hasAction(): bool |
290 | { |
291 | return ($this->action !== null); |
292 | } |
293 | |
294 | /** |
295 | * Get the current controller class name |
296 | * |
297 | * @return string |
298 | */ |
299 | public function getControllerClass(): string |
300 | { |
301 | return $this->controllerClass; |
302 | } |
303 | |
304 | /** |
305 | * Determine if the route is CLI |
306 | * |
307 | * @return bool |
308 | */ |
309 | public function isCli(): bool |
310 | { |
311 | return ($this->routeMatch instanceof Match\Cli); |
312 | } |
313 | |
314 | /** |
315 | * Determine if the route is HTTP |
316 | * |
317 | * @return bool |
318 | */ |
319 | public function isHttp(): bool |
320 | { |
321 | return ($this->routeMatch instanceof Match\Http); |
322 | } |
323 | |
324 | /** |
325 | * Prepare routes |
326 | * |
327 | * @return static |
328 | */ |
329 | public function prepare(): static |
330 | { |
331 | $this->routeMatch->prepare(); |
332 | return $this; |
333 | } |
334 | |
335 | /** |
336 | * Route to the correct controller |
337 | * |
338 | * @param ?string $forceRoute |
339 | * @throws Exception|ReflectionException |
340 | * @return void |
341 | */ |
342 | public function route(?string $forceRoute = null): void |
343 | { |
344 | if ($this->routeMatch->match($forceRoute)) { |
345 | if ($this->routeMatch->hasController()) { |
346 | $controller = $this->routeMatch->getController(); |
347 | |
348 | if ($controller instanceof Closure) { |
349 | $this->controllerClass = 'Closure'; |
350 | $this->controller = $controller; |
351 | } else if (class_exists($controller)) { |
352 | $this->controllerClass = $controller; |
353 | $controllerParams = null; |
354 | |
355 | if ($this->routeMatch->hasControllerParams($controller)) { |
356 | $controllerParams = $this->routeMatch->getControllerParams($controller); |
357 | } else if ($this->routeMatch->hasControllerParams('*')) { |
358 | $controllerParams = $this->routeMatch->getControllerParams('*'); |
359 | } |
360 | |
361 | if ($controllerParams !== null) { |
362 | $this->controller = (new \ReflectionClass($controller))->newInstanceArgs($controllerParams); |
363 | } else { |
364 | $this->controller = new $controller(); |
365 | } |
366 | |
367 | if (!($this->controller instanceof \Pop\Controller\ControllerInterface)) { |
368 | throw new Exception('Error: The controller must be an instance of Pop\Controller\Interface'); |
369 | } |
370 | |
371 | $action = $this->routeMatch->getAction(); |
372 | $this->action = (($action === null) && ($this->routeMatch->isDynamicRoute())) ? 'index' : $action; |
373 | } |
374 | } |
375 | } |
376 | } |
377 | |
378 | /** |
379 | * Method to process if a route was not found |
380 | * |
381 | * @param bool $exit |
382 | * @return void |
383 | */ |
384 | public function noRouteFound(bool $exit = true): void |
385 | { |
386 | $this->routeMatch->noRouteFound($exit); |
387 | } |
388 | |
389 | } |