Initial commit: Uruk Prize Platform architecture, backend core, mobile app, gateway caller & deployment pipeline
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core;
|
||||
|
||||
class Router
|
||||
{
|
||||
private array $routes = [];
|
||||
|
||||
public function get(string $path, array|callable $handler, array $middlewares = []): void
|
||||
{
|
||||
$this->addRoute('GET', $path, $handler, $middlewares);
|
||||
}
|
||||
|
||||
public function post(string $path, array|callable $handler, array $middlewares = []): void
|
||||
{
|
||||
$this->addRoute('POST', $path, $handler, $middlewares);
|
||||
}
|
||||
|
||||
public function put(string $path, array|callable $handler, array $middlewares = []): void
|
||||
{
|
||||
$this->addRoute('PUT', $path, $handler, $middlewares);
|
||||
}
|
||||
|
||||
public function delete(string $path, array|callable $handler, array $middlewares = []): void
|
||||
{
|
||||
$this->addRoute('DELETE', $path, $handler, $middlewares);
|
||||
}
|
||||
|
||||
public function options(string $path, array|callable $handler): void
|
||||
{
|
||||
$this->addRoute('OPTIONS', $path, $handler, []);
|
||||
}
|
||||
|
||||
private function addRoute(string $method, string $path, array|callable $handler, array $middlewares): void
|
||||
{
|
||||
$pattern = preg_replace('/\{([a-zA-Z0-9_]+)\}/', '(?P<$1>[^/]+)', $path);
|
||||
$pattern = '#^' . $pattern . '$#';
|
||||
|
||||
$this->routes[] = [
|
||||
'method' => $method,
|
||||
'path' => $path,
|
||||
'pattern' => $pattern,
|
||||
'handler' => $handler,
|
||||
'middlewares' => $middlewares,
|
||||
];
|
||||
}
|
||||
|
||||
public function dispatch(Request $request): void
|
||||
{
|
||||
// Handle preflight CORS OPTIONS requests immediately
|
||||
if ($request->getMethod() === 'OPTIONS') {
|
||||
Response::json(['status' => 'ok']);
|
||||
}
|
||||
|
||||
$method = $request->getMethod();
|
||||
$path = $request->getPath();
|
||||
|
||||
foreach ($this->routes as $route) {
|
||||
if ($route['method'] !== $method) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match($route['pattern'], $path, $matches)) {
|
||||
$params = [];
|
||||
foreach ($matches as $key => $value) {
|
||||
if (is_string($key)) {
|
||||
$params[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->runMiddlewares($route['middlewares'], $request, function () use ($route, $request, $params) {
|
||||
$this->executeHandler($route['handler'], $request, $params);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Response::notFound("Endpoint not found: [{$method}] {$path}");
|
||||
}
|
||||
|
||||
private function runMiddlewares(array $middlewares, Request $request, callable $target): void
|
||||
{
|
||||
$pipeline = array_reduce(
|
||||
array_reverse($middlewares),
|
||||
function ($next, $middlewareClass) {
|
||||
return function ($req) use ($next, $middlewareClass) {
|
||||
$instance = new $middlewareClass();
|
||||
$instance->handle($req, $next);
|
||||
};
|
||||
},
|
||||
$target
|
||||
);
|
||||
|
||||
$pipeline($request);
|
||||
}
|
||||
|
||||
private function executeHandler(array|callable $handler, Request $request, array $params): void
|
||||
{
|
||||
if (is_callable($handler)) {
|
||||
call_user_func($handler, $request, $params);
|
||||
return;
|
||||
}
|
||||
|
||||
[$controllerClass, $methodName] = $handler;
|
||||
if (!class_exists($controllerClass)) {
|
||||
Response::error("Controller class {$controllerClass} not found", 500);
|
||||
}
|
||||
|
||||
$controller = new $controllerClass();
|
||||
if (!method_exists($controller, $methodName)) {
|
||||
Response::error("Method {$methodName} not found on controller {$controllerClass}", 500);
|
||||
}
|
||||
|
||||
$controller->$methodName($request, $params);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user