Files
2026-06-07 13:12:42 +03:00

103 lines
2.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Core;
use App\Core\Database;
final class Router
{
/**
* @var array<int, array{method: string, pattern: string, handler: callable}>
*/
private array $routes = [];
public function get(string $pattern, callable $handler): void
{
$this->routes[] = ['method' => 'GET', 'pattern' => $pattern, 'handler' => $handler];
}
public function post(string $pattern, callable $handler): void
{
$this->routes[] = ['method' => 'POST', 'pattern' => $pattern, 'handler' => $handler];
}
public function dispatch(string $method, string $uri): void
{
$path = parse_url($uri, PHP_URL_PATH) ?: '/';
$path = rtrim($path, '/') ?: '/';
foreach ($this->routes as $route) {
if ($route['method'] !== $method) {
continue;
}
$params = $this->match($route['pattern'], $path);
if ($params === null) {
continue;
}
($route['handler'])(...$params);
return;
}
$this->redirectLegacyPath($path);
http_response_code(404);
view('pages/404', [
'title' => 'Страница не найдена - Рыбсток',
'breadcrumbs' => [
['title' => 'Главная', 'url' => '/'],
['title' => 'Страница не найдена'],
],
]);
}
private function redirectLegacyPath(string $path): void
{
try {
$statement = Database::pdo()->prepare(
'SELECT new_path, http_code
FROM redirects
WHERE old_path = :old_path
AND is_active = 1
LIMIT 1'
);
$statement->execute(['old_path' => $path]);
$redirect = $statement->fetch();
} catch (\Throwable) {
return;
}
if (!$redirect) {
return;
}
header('Location: ' . $redirect['new_path'], true, (int) $redirect['http_code']);
exit;
}
/**
* @return array<int, string>|null
*/
private function match(string $pattern, string $path): ?array
{
$regex = preg_replace('#\{([a-zA-Z_][a-zA-Z0-9_]*)\}#', '([^/]+)', $pattern);
$regex = '#^' . rtrim((string) $regex, '/') . '$#u';
if ($pattern === '/') {
$regex = '#^/$#u';
}
if (!preg_match($regex, $path, $matches)) {
return null;
}
array_shift($matches);
return array_map('urldecode', $matches);
}
}