feat: initial foundation for collaborative canteen ordering system in vanilla PHP
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Core\Routing;
|
||||
|
||||
use App\Core\Http\Request;
|
||||
use App\Core\Http\Response;
|
||||
|
||||
class Router
|
||||
{
|
||||
private array $routes = [];
|
||||
private $notFoundHandler = null;
|
||||
|
||||
public function get(string $path, array|callable $handler): self
|
||||
{
|
||||
$this->addRoute('GET', $path, $handler);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function post(string $path, array|callable $handler): self
|
||||
{
|
||||
$this->addRoute('POST', $path, $handler);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function put(string $path, array|callable $handler): self
|
||||
{
|
||||
$this->addRoute('PUT', $path, $handler);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function delete(string $path, array|callable $handler): self
|
||||
{
|
||||
$this->addRoute('DELETE', $path, $handler);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function any(string $path, array|callable $handler): self
|
||||
{
|
||||
foreach (['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] as $method) {
|
||||
$this->addRoute($method, $path, $handler);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setNotFound(callable $handler): void
|
||||
{
|
||||
$this->notFoundHandler = $handler;
|
||||
}
|
||||
|
||||
private function addRoute(string $method, string $path, array|callable $handler): void
|
||||
{
|
||||
// Normalize path
|
||||
$path = '/' . trim($path, '/');
|
||||
if ($path === '//') {
|
||||
$path = '/';
|
||||
}
|
||||
|
||||
// Convert {param} to regex pattern (?P<param>[^/]+)
|
||||
$pattern = preg_replace('/\{([a-zA-Z0-9_]+)\}/', '(?P<$1>[^/]+)', $path);
|
||||
$regex = '#^' . $pattern . '$#';
|
||||
|
||||
$this->routes[] = [
|
||||
'method' => strtoupper($method),
|
||||
'path' => $path,
|
||||
'regex' => $regex,
|
||||
'handler' => $handler,
|
||||
];
|
||||
}
|
||||
|
||||
public function dispatch(Request $request, Response $response): Response
|
||||
{
|
||||
$method = $request->getMethod();
|
||||
$path = $request->getPath();
|
||||
|
||||
foreach ($this->routes as $route) {
|
||||
if ($route['method'] !== $method) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match($route['regex'], $path, $matches)) {
|
||||
// Extract named matches for params
|
||||
$params = [];
|
||||
foreach ($matches as $key => $value) {
|
||||
if (is_string($key)) {
|
||||
$params[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$handler = $route['handler'];
|
||||
|
||||
if (is_callable($handler)) {
|
||||
$result = call_user_func($handler, $request, $response, $params);
|
||||
} elseif (is_array($handler) && count($handler) === 2) {
|
||||
[$controllerClass, $action] = $handler;
|
||||
$controller = new $controllerClass();
|
||||
$result = call_user_func([$controller, $action], $request, $response, $params);
|
||||
} else {
|
||||
throw new \RuntimeException('Invalid route handler specified.');
|
||||
}
|
||||
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (is_string($result)) {
|
||||
return $response->setContent($result);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
// 404 Not Found
|
||||
if ($this->notFoundHandler) {
|
||||
$result = call_user_func($this->notFoundHandler, $request, $response);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
}
|
||||
return $response->setStatusCode(404)->setContent((string)$result);
|
||||
}
|
||||
|
||||
return $response->setStatusCode(404)->setContent('<h1>404 Not Found</h1><p>The requested route does not exist.</p>');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user