64 lines
2.2 KiB
PHP
64 lines
2.2 KiB
PHP
<?php
|
|
// core/Router.php
|
|
|
|
class Router {
|
|
private array $routes = [];
|
|
|
|
public function get(string $path, callable|array $handler): void {
|
|
$this->routes['GET'][$path] = $handler;
|
|
}
|
|
|
|
public function post(string $path, callable|array $handler): void {
|
|
$this->routes['POST'][$path] = $handler;
|
|
}
|
|
|
|
public function dispatch(): void {
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
|
|
|
// Strip base path
|
|
$basePath = parse_url(APP_URL, PHP_URL_PATH);
|
|
if ($basePath && str_starts_with($uri, $basePath)) {
|
|
$uri = substr($uri, strlen($basePath));
|
|
}
|
|
$uri = '/' . ltrim($uri, '/');
|
|
if ($uri !== '/' && str_ends_with($uri, '/')) {
|
|
$uri = rtrim($uri, '/');
|
|
}
|
|
|
|
$routes = $this->routes[$method] ?? [];
|
|
|
|
// Two-pass: static routes (no {param}) matched first, dynamic routes second.
|
|
// This ensures /verifikator/warga/tambah is never swallowed by /verifikator/warga/{id}.
|
|
$staticRoutes = array_filter($routes, fn($p) => !str_contains($p, '{'), ARRAY_FILTER_USE_KEY);
|
|
$dynamicRoutes = array_filter($routes, fn($p) => str_contains($p, '{'), ARRAY_FILTER_USE_KEY);
|
|
|
|
foreach ([$staticRoutes, $dynamicRoutes] as $group) {
|
|
foreach ($group as $pattern => $handler) {
|
|
$regex = $this->buildRegex($pattern);
|
|
if (preg_match($regex, $uri, $matches)) {
|
|
array_shift($matches);
|
|
$params = array_values($matches);
|
|
|
|
if (is_array($handler)) {
|
|
[$controllerName, $action] = $handler;
|
|
$controller = new $controllerName();
|
|
$controller->$action(...$params);
|
|
} else {
|
|
$handler(...$params);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 404
|
|
http_response_code(404);
|
|
require APP_ROOT . '/app/views/public/404.php';
|
|
}
|
|
|
|
private function buildRegex(string $pattern): string {
|
|
$pattern = preg_replace('/\{(\w+)\}/', '([^/]+)', $pattern);
|
|
return '#^' . $pattern . '$#';
|
|
}
|
|
} |