1 | <?php |
||||||
2 | /** |
||||||
3 | * This file is part of the O2System Framework package. |
||||||
4 | * |
||||||
5 | * For the full copyright and license information, please view the LICENSE |
||||||
6 | * file that was distributed with this source code. |
||||||
7 | * |
||||||
8 | * @author Steeve Andrian Salim |
||||||
9 | * @copyright Copyright (c) Steeve Andrian Salim |
||||||
10 | */ |
||||||
11 | |||||||
12 | // ------------------------------------------------------------------------ |
||||||
13 | |||||||
14 | namespace O2System\Kernel\Http; |
||||||
15 | |||||||
16 | // ------------------------------------------------------------------------ |
||||||
17 | |||||||
18 | use O2System\Kernel\Http\Message\Uri as KernelMessageUri; |
||||||
19 | use O2System\Kernel\Http\Message\Uri\Segments as KernelMessageUriSegments; |
||||||
20 | |||||||
21 | /** |
||||||
22 | * Class Router |
||||||
23 | * @package O2System\Kernel\Http |
||||||
24 | */ |
||||||
25 | class Router |
||||||
26 | { |
||||||
27 | /** |
||||||
28 | * Router::$uri |
||||||
29 | * |
||||||
30 | * @var Message\Uri |
||||||
31 | */ |
||||||
32 | protected $uri; |
||||||
33 | |||||||
34 | /** |
||||||
35 | * Router::$addresses |
||||||
36 | * |
||||||
37 | * @var Router\Addresses |
||||||
38 | */ |
||||||
39 | protected $addresses; |
||||||
40 | |||||||
41 | // ------------------------------------------------------------------------ |
||||||
42 | |||||||
43 | /** |
||||||
44 | * Router::getUri |
||||||
45 | * |
||||||
46 | * Gets routed Uri. |
||||||
47 | * |
||||||
48 | * @return Message\Uri |
||||||
49 | */ |
||||||
50 | public function getUri() |
||||||
51 | { |
||||||
52 | return $this->uri; |
||||||
53 | } |
||||||
54 | |||||||
55 | // ------------------------------------------------------------------------ |
||||||
56 | |||||||
57 | /** |
||||||
58 | * Router::getAddresses |
||||||
59 | * |
||||||
60 | * @return \O2System\Kernel\Http\Router\Addresses |
||||||
61 | */ |
||||||
62 | public function getAddresses() |
||||||
63 | { |
||||||
64 | return $this->addresses; |
||||||
65 | } |
||||||
66 | |||||||
67 | // ------------------------------------------------------------------------ |
||||||
68 | |||||||
69 | /** |
||||||
70 | * Router::setAddresses |
||||||
71 | * |
||||||
72 | * Sets router addresses. |
||||||
73 | * |
||||||
74 | * @param \O2System\Kernel\Http\Router\Addresses $addresses |
||||||
75 | * |
||||||
76 | * @return static |
||||||
77 | */ |
||||||
78 | public function setAddresses(Router\Addresses $addresses) |
||||||
79 | { |
||||||
80 | $this->addresses = $addresses; |
||||||
81 | |||||||
82 | return $this; |
||||||
83 | } |
||||||
84 | |||||||
85 | // ------------------------------------------------------------------------ |
||||||
86 | |||||||
87 | /** |
||||||
88 | * Router::handle |
||||||
89 | * |
||||||
90 | * @param \O2System\Kernel\Http\Message\Uri|null $uri |
||||||
91 | * |
||||||
92 | * @return bool |
||||||
93 | * @throws \ReflectionException |
||||||
94 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||||
95 | */ |
||||||
96 | public function handle(Message\Uri $uri = null) |
||||||
97 | { |
||||||
98 | $this->uri = is_null($uri) ? new KernelMessageUri() : $uri; |
||||||
99 | |||||||
100 | // Handle Extension Request |
||||||
101 | if ($this->uri->segments->count()) { |
||||||
102 | $this->handleExtensionRequest(); |
||||||
103 | } else { |
||||||
104 | $uriPath = urldecode( |
||||||
105 | parse_url($_SERVER[ 'REQUEST_URI' ], PHP_URL_PATH) |
||||||
106 | ); |
||||||
107 | |||||||
108 | $uriPathParts = explode('public/', $uriPath); |
||||||
109 | $uriPath = end($uriPathParts); |
||||||
110 | |||||||
111 | if ($uriPath !== '/') { |
||||||
112 | $this->uri = $this->uri->withSegments(new KernelMessageUriSegments( |
||||||
113 | array_filter(explode('/', $uriPath))) |
||||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
114 | ); |
||||||
115 | } |
||||||
116 | |||||||
117 | unset($uriPathParts, $uriPath); |
||||||
118 | } |
||||||
119 | |||||||
120 | // Load app addresses config |
||||||
121 | $this->addresses = config()->loadFile('addresses', true); |
||||||
0 ignored issues
–
show
The function
config was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
122 | |||||||
123 | // Try to translate from uri string |
||||||
124 | if (false !== ($action = $this->addresses->getTranslation($this->uri->segments->__toString()))) { |
||||||
125 | if ( ! $action->isValidHttpMethod(input()->server('REQUEST_METHOD')) && ! $action->isAnyHttpMethod()) { |
||||||
126 | output()->sendError(405); |
||||||
127 | } else { |
||||||
128 | // Checks if action closure is an array |
||||||
129 | if (is_array($closureSegments = $action->getClosure())) { |
||||||
130 | $this->uri->segments->exchangeArray($closureSegments); |
||||||
131 | |||||||
132 | $this->handleSegmentsRequest(); |
||||||
133 | } else { |
||||||
134 | if (false !== ($parseSegments = $action->getParseUriString($this->uri->segments->__toString()))) { |
||||||
135 | $uriSegments = $parseSegments; |
||||||
136 | } else { |
||||||
137 | $uriSegments = []; |
||||||
138 | } |
||||||
139 | |||||||
140 | $this->uri = $this->uri->withSegments(new KernelMessageUriSegments($uriSegments)); |
||||||
141 | |||||||
142 | $this->parseAction($action, $uriSegments); |
||||||
143 | if ( ! empty(services()->has('controller'))) { |
||||||
144 | return true; |
||||||
145 | } |
||||||
146 | } |
||||||
147 | } |
||||||
148 | } else { |
||||||
149 | $this->handleSegmentsRequest(); |
||||||
150 | } |
||||||
151 | |||||||
152 | // break the loop if the controller has been set |
||||||
153 | if (services()->has('controller')) { |
||||||
154 | return true; |
||||||
155 | } |
||||||
156 | |||||||
157 | // Let's the app do the rest when there is no controller found |
||||||
158 | // the app should redirect to PAGE 404 |
||||||
159 | } |
||||||
160 | |||||||
161 | // ------------------------------------------------------------------------ |
||||||
162 | |||||||
163 | /** |
||||||
164 | * Router::handleExtensionRequest |
||||||
165 | */ |
||||||
166 | protected function handleExtensionRequest() |
||||||
167 | { |
||||||
168 | $lastSegment = $this->uri->segments->last(); |
||||||
169 | |||||||
170 | if (strpos($lastSegment, '.json') !== false) { |
||||||
171 | output()->setContentType('application/json'); |
||||||
0 ignored issues
–
show
The method
setContentType() does not exist on O2System\Kernel\Cli\Output .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
172 | $lastSegment = str_replace('.json', '', $lastSegment); |
||||||
173 | $this->uri->segments->pop(); |
||||||
174 | $this->uri->segments->push($lastSegment); |
||||||
175 | } elseif (strpos($lastSegment, '.xml') !== false) { |
||||||
176 | output()->setContentType('application/xml'); |
||||||
177 | $lastSegment = str_replace('.xml', '', $lastSegment); |
||||||
178 | $this->uri->segments->pop(); |
||||||
179 | $this->uri->segments->push($lastSegment); |
||||||
180 | } elseif (strpos($lastSegment, '.js') !== false) { |
||||||
181 | output()->setContentType('application/x-javascript'); |
||||||
182 | $lastSegment = str_replace('.js', '', $lastSegment); |
||||||
183 | $this->uri->segments->pop(); |
||||||
184 | $this->uri->segments->push($lastSegment); |
||||||
185 | } elseif (strpos($lastSegment, '.css') !== false) { |
||||||
186 | output()->setContentType('text/css'); |
||||||
187 | $lastSegment = str_replace('.css', '', $lastSegment); |
||||||
188 | $this->uri->segments->pop(); |
||||||
189 | $this->uri->segments->push($lastSegment); |
||||||
190 | } |
||||||
191 | } |
||||||
192 | |||||||
193 | // ------------------------------------------------------------------------ |
||||||
194 | |||||||
195 | /** |
||||||
196 | * Router::handleModuleRequest |
||||||
197 | */ |
||||||
198 | public function handleSegmentsRequest() |
||||||
199 | { |
||||||
200 | // Try to get route from controller |
||||||
201 | if ($numOfUriSegments = $this->uri->segments->count()) { |
||||||
202 | $uriSegments = $this->uri->segments->getArrayCopy(); |
||||||
203 | |||||||
204 | $namespaces = [ |
||||||
205 | 'App\Controllers\\', |
||||||
206 | 'App\Http\Controllers\\', |
||||||
207 | 'O2System\Reactor\Http\Controllers\\', |
||||||
208 | ]; |
||||||
209 | |||||||
210 | for ($i = 0; $i <= $numOfUriSegments; $i++) { |
||||||
211 | $uriRoutedSegments = array_slice($uriSegments, 0, ($numOfUriSegments - $i)); |
||||||
212 | |||||||
213 | foreach ($namespaces as $namespace) { |
||||||
214 | $controllerClassName = $namespace . implode('\\', |
||||||
215 | array_map('studlycase', $uriRoutedSegments)); |
||||||
216 | |||||||
217 | if (class_exists($controllerClassName)) { |
||||||
218 | $uriSegments = array_diff($uriSegments, $uriRoutedSegments); |
||||||
219 | $this->setController(new Router\DataStructures\Controller($controllerClassName), |
||||||
220 | $uriSegments); |
||||||
221 | break; |
||||||
222 | } |
||||||
223 | } |
||||||
224 | |||||||
225 | // break the loop if the controller has been set |
||||||
226 | if (services()->has('controller')) { |
||||||
227 | break; |
||||||
228 | } |
||||||
229 | } |
||||||
230 | } |
||||||
231 | } |
||||||
232 | |||||||
233 | // ------------------------------------------------------------------------ |
||||||
234 | |||||||
235 | /** |
||||||
236 | * Router::parseAction |
||||||
237 | * |
||||||
238 | * @param \O2System\Kernel\Http\Router\DataStructures\Action $action |
||||||
239 | * @param array $uriSegments |
||||||
240 | * |
||||||
241 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||||||
242 | * @throws \ReflectionException |
||||||
243 | */ |
||||||
244 | protected function parseAction(Router\DataStructures\Action $action, array $uriSegments = []) |
||||||
245 | { |
||||||
246 | $closure = $action->getClosure(); |
||||||
247 | if (empty($closure)) { |
||||||
248 | output()->sendError(204); |
||||||
249 | } |
||||||
250 | |||||||
251 | if ($closure instanceof Controller) { |
||||||
252 | $uriSegments = empty($uriSegments) |
||||||
253 | ? $action->getClosureParameters() |
||||||
254 | : $uriSegments; |
||||||
255 | $this->setController( |
||||||
256 | (new Router\DataStructures\Controller($closure)) |
||||||
0 ignored issues
–
show
$closure of type O2System\Kernel\Http\Controller is incompatible with the type string expected by parameter $filePath of O2System\Kernel\Http\Rou...ntroller::__construct() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
257 | ->setRequestMethod('index'), |
||||||
258 | $uriSegments |
||||||
259 | ); |
||||||
260 | } elseif ($closure instanceof Router\DataStructures\Controller) { |
||||||
261 | $this->setController($closure, $action->getClosureParameters()); |
||||||
262 | } elseif (is_array($closure)) { |
||||||
263 | $this->uri = (new Message\Uri()) |
||||||
264 | ->withSegments(new Message\Uri\Segments('')) |
||||||
265 | ->withQuery(''); |
||||||
266 | $this->handle($this->uri->addSegments($closure)); |
||||||
267 | } else { |
||||||
268 | if (class_exists($closure)) { |
||||||
269 | $this->setController( |
||||||
270 | (new Router\DataStructures\Controller($closure)) |
||||||
271 | ->setRequestMethod('index'), |
||||||
272 | $uriSegments |
||||||
273 | ); |
||||||
274 | } elseif (preg_match("/([a-zA-Z0-9\\\]+)(@)([a-zA-Z0-9\\\]+)/", $closure, $matches)) { |
||||||
275 | $this->setController( |
||||||
276 | (new Router\DataStructures\Controller($matches[ 1 ])) |
||||||
277 | ->setRequestMethod($matches[ 3 ]), |
||||||
278 | $uriSegments |
||||||
279 | ); |
||||||
280 | } elseif (is_string($closure) && $closure !== '') { |
||||||
281 | if (is_json($closure)) { |
||||||
282 | output()->setContentType('application/json'); |
||||||
283 | output()->send($closure); |
||||||
0 ignored issues
–
show
The method
send() does not exist on O2System\Kernel\Cli\Output .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
284 | } else { |
||||||
285 | output()->send($closure); |
||||||
286 | } |
||||||
287 | } elseif (is_array($closure) || is_object($closure)) { |
||||||
288 | output()->send($closure); |
||||||
289 | } elseif (is_numeric($closure)) { |
||||||
290 | output()->sendError($closure); |
||||||
291 | } else { |
||||||
292 | output()->sendError(204); |
||||||
293 | exit(EXIT_ERROR); |
||||||
0 ignored issues
–
show
|
|||||||
294 | } |
||||||
295 | } |
||||||
296 | } |
||||||
297 | |||||||
298 | // ------------------------------------------------------------------------ |
||||||
299 | |||||||
300 | /** |
||||||
301 | * Router::setController |
||||||
302 | * |
||||||
303 | * @param \O2System\Kernel\Http\Router\DataStructures\Controller $controller |
||||||
304 | * @param array $uriSegments |
||||||
305 | * |
||||||
306 | * @throws \ReflectionException |
||||||
307 | */ |
||||||
308 | protected function setController( |
||||||
309 | Router\DataStructures\Controller $controller, |
||||||
310 | array $uriSegments = [] |
||||||
311 | ) { |
||||||
312 | if ( ! $controller->isValid()) { |
||||||
313 | output()->sendError(400); |
||||||
314 | } |
||||||
315 | |||||||
316 | // Add Controller PSR4 Namespace |
||||||
317 | loader()->addNamespace($controller->getNamespaceName(), $controller->getFileInfo()->getPath()); |
||||||
0 ignored issues
–
show
The function
loader was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
318 | |||||||
319 | $controllerMethod = $controller->getRequestMethod(); |
||||||
320 | $controllerMethod = empty($controllerMethod) ? reset($uriSegments) : $controllerMethod; |
||||||
321 | $controllerMethod = camelcase($controllerMethod); |
||||||
322 | |||||||
323 | // Set default controller method to index |
||||||
324 | if ( ! $controller->hasMethod($controllerMethod) && |
||||||
325 | ! $controller->hasMethod('route') |
||||||
326 | ) { |
||||||
327 | $controllerMethod = 'index'; |
||||||
328 | } |
||||||
329 | |||||||
330 | // has route method, controller method set to index as default |
||||||
331 | if (empty($controllerMethod)) { |
||||||
332 | $controllerMethod = 'index'; |
||||||
333 | } |
||||||
334 | |||||||
335 | if (camelcase(reset($uriSegments)) === $controllerMethod) { |
||||||
336 | array_shift($uriSegments); |
||||||
337 | } |
||||||
338 | |||||||
339 | $controllerMethodParams = $uriSegments; |
||||||
340 | |||||||
341 | if ($controller->hasMethod('route')) { |
||||||
342 | $controller->setRequestMethod('route'); |
||||||
343 | $controller->setRequestMethodArgs([ |
||||||
344 | $controllerMethod, |
||||||
345 | $controllerMethodParams, |
||||||
346 | ]); |
||||||
347 | } elseif ($controller->hasMethod($controllerMethod)) { |
||||||
348 | $method = $controller->getMethod($controllerMethod); |
||||||
349 | |||||||
350 | // Method doesn't need any parameters |
||||||
351 | if ($method->getNumberOfParameters() == 0) { |
||||||
352 | // But there is parameters requested |
||||||
353 | if (count($controllerMethodParams)) { |
||||||
354 | output()->sendError(404); |
||||||
355 | } else { |
||||||
356 | $controller->setRequestMethod($controllerMethod); |
||||||
357 | } |
||||||
358 | } else { |
||||||
359 | $parameters = []; |
||||||
360 | |||||||
361 | if (count($controllerMethodParams)) { |
||||||
362 | if (is_numeric(key($controllerMethodParams))) { |
||||||
363 | $parameters = $controllerMethodParams; |
||||||
364 | } else { |
||||||
365 | foreach ($method->getParameters() as $index => $parameter) { |
||||||
366 | if (isset($uriSegments[ $parameter->name ])) { |
||||||
367 | $parameters[ $index ] = $controllerMethodParams[ $parameter->name ]; |
||||||
368 | } else { |
||||||
369 | $parameters[ $index ] = null; |
||||||
370 | } |
||||||
371 | } |
||||||
372 | } |
||||||
373 | } |
||||||
374 | |||||||
375 | $controller->setRequestMethod($controllerMethod); |
||||||
376 | $controller->setRequestMethodArgs($parameters); |
||||||
377 | } |
||||||
378 | } |
||||||
379 | |||||||
380 | // Set Controller |
||||||
381 | services()->add($controller, 'controller'); |
||||||
382 | } |
||||||
383 | } |