dev/TrieRouter.php

71 lines
1.7 KiB
PHP

<?php
class TrieRouter implements Router
{
public array $root = [];
// Add route to the trie
public function add(string $method, string $route, callable $handler): Router
{
$node = &$this->root[$method];
// Expand the route into segments and make dynamic segments into a common placeholder
$segments = array_map(function($segment) {
return str_starts_with($segment, ':') ? ':x' : $segment;
}, explode('/', trim($route, '/')));
foreach ($segments as $segment) {
if (!isset($node[$segment])) {
$node[$segment] = ['_children' => [], '_handler' => null];
}
$node = &$node[$segment]['_children'];
}
$node['_handler'] = $handler;
return $this;
}
// Find and handle the route
public function lookup(string $method, string $uri): array
{
$node = &$this->root[$method];
$segments = explode('/', trim($uri, '/'));
$params = [];
foreach ($segments as $segment) {
if (isset($node[$segment])) {
$node = &$node[$segment]['_children'];
} else {
// Handle dynamic parameters (e.g., :id)
$dynamicSegment = $this->matchDynamicSegment($node, $segment);
if ($dynamicSegment) {
$params[] = $segment;
$node = &$node[$dynamicSegment]['_children'];
} else {
return ['code' => 404];
}
}
}
// Check if a handler exists for the current node
if (isset($node['_handler'])) return ['code' => 200, 'handler' => $node['_handler'], 'params' => $params];
return ['code' => 404];
}
// Match dynamic route segments like ':id'
private function matchDynamicSegment(array $node, string $segment)
{
foreach ($node as $key => $value) {
if (strpos($key, ':') === 0) return $key;
}
return null;
}
public function clear(): Router
{
$this->root = [];
return $this;
}
}