| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- <?php
- /*
- * Copyright 2026 Alessandro Chiapparini <sprint_planer_web@chiapparini.org>
- * SPDX-License-Identifier: Apache-2.0
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * See the LICENSE file in the project root for the full license text.
- */
- declare(strict_types=1);
- namespace App\Http;
- /**
- * Tiny pattern router.
- *
- * Patterns use `{name}` for path segments. Numeric-looking captures are cast
- * to int; everything else stays a string. Handlers receive (Request, array $params).
- */
- final class Router
- {
- /** @var list<array{method:string,regex:string,names:list<string>,handler:callable}> */
- private array $routes = [];
- public function add(string $method, string $pattern, callable $handler): void
- {
- [$regex, $names] = self::compile($pattern);
- $this->routes[] = [
- 'method' => strtoupper($method),
- 'regex' => $regex,
- 'names' => $names,
- 'handler' => $handler,
- ];
- }
- public function get(string $p, callable $h): void { $this->add('GET', $p, $h); }
- public function post(string $p, callable $h): void { $this->add('POST', $p, $h); }
- public function patch(string $p, callable $h): void { $this->add('PATCH', $p, $h); }
- public function delete(string $p, callable $h): void { $this->add('DELETE', $p, $h); }
- public function dispatch(Request $req): Response
- {
- $methodMatchedButNotPath = false;
- foreach ($this->routes as $r) {
- if (!preg_match($r['regex'], $req->path, $m)) {
- continue;
- }
- if ($r['method'] !== $req->method) {
- $methodMatchedButNotPath = true;
- continue;
- }
- $params = [];
- foreach ($r['names'] as $i => $name) {
- $val = $m[$i + 1] ?? '';
- $params[$name] = ctype_digit($val) ? (int) $val : $val;
- }
- $result = ($r['handler'])($req, $params);
- if ($result instanceof Response) {
- return $result;
- }
- if (is_string($result)) {
- return Response::html($result);
- }
- if (is_array($result) || is_object($result) || is_bool($result) || is_null($result)) {
- return Response::json($result);
- }
- return Response::text((string) $result);
- }
- if ($methodMatchedButNotPath) {
- return Response::text('Method Not Allowed', 405);
- }
- return Response::text('Not Found', 404);
- }
- /** @return array{0:string,1:list<string>} */
- private static function compile(string $pattern): array
- {
- $names = [];
- $regex = preg_replace_callback(
- '/\{([A-Za-z_][A-Za-z0-9_]*)\}/',
- function ($m) use (&$names) {
- $names[] = $m[1];
- return '([^/]+)';
- },
- $pattern
- );
- return ['#^' . $regex . '$#', $names];
- }
- }
|