aboutsummaryrefslogtreecommitdiffstats
path: root/src/app/Router.php
blob: 9bfe09dc68d1167dbb9afbf634af36a3456015f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?php

declare(strict_types=1);
// Regex-based HTTP router that dispatches requests to controller methods.

namespace App;

class Router
{
    private array $routes = [];

    public function get(string $path, string $controller, string $method): void
    {
        $this->routes[] = ['GET', $path, $controller, $method];
    }

    public function post(string $path, string $controller, string $method): void
    {
        $this->routes[] = ['POST', $path, $controller, $method];
    }

    public function dispatch(): void
    {
        $requestMethod = $_SERVER['REQUEST_METHOD'];
        $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        $uri = rtrim($uri, '/') ?: '/';

        foreach ($this->routes as [$method, $path, $controller, $action]) {
            if ($method !== $requestMethod) {
                continue;
            }

            // Convert route placeholders to named regex capture groups.
            // Example with route '/post/{id}' and request '/post/42':
            //   1. preg_replace turns '{id}' into '(?P<id>[^/]+)'
            //      result: '#^/post/(?P<id>[^/]+)$#'
            //   2. preg_match against '/post/42' produces:
            //      [0 => '/post/42', 'id' => '42', 1 => '42']
            //   3. array_filter keeps only string keys: ['id' => '42']
            //   4. PostController->show('42') is called
            $pattern = preg_replace('#\{(\w+)\}#', '(?P<$1>[^/]+)', $path);
            $pattern = '#^' . $pattern . '$#';

            if (preg_match($pattern, $uri, $matches)) {
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                $controllerClass = "App\\Controllers\\$controller";
                $instance = new $controllerClass();
                $instance->$action(...array_values($params));
                return;
            }
        }

        http_response_code(404);
        echo '404 Not Found';
    }
}