fix a few bugs

This commit is contained in:
Sky Johnson 2025-09-10 08:38:55 -05:00
parent a8b0f2a410
commit 4d377c8f6f
3 changed files with 57 additions and 18 deletions

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
tests/trees
/vendor/
index.php

View File

@ -15,12 +15,12 @@ class Router
*/
public function add(string $method, string $route, callable $handler): Router
{
// Expand the route into segments and make dynamic segments into a common placeholder
// 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, '/')));
// Push each segment into the routes array as a node, except if this is the root node
// push each segment into the routes array as a node, except if this is the root node
$node = &$this->routes;
foreach ($segments as $segment) {
// skip an empty segment, which allows us to register handlers for the root node
@ -43,20 +43,33 @@ class Router
*/
public function lookup(string $method, string $uri): array
{
// normalize uri to be tolerant of trailing slashes
$uri = '/' . trim($uri, '/');
// node is a reference to our current location in the node tree
$node = $this->routes;
// params will hold any dynamic segments we find
$params = [];
// init the response array
$res = ['code' => 0, 'handler' => null, 'params' => []];
// if the URI is just a slash, we can return the handler for the root node
if ($uri === '/') {
return isset($node[$method])
? ['code' => 200, 'handler' => $node[$method], 'params' => null]
: ['code' => 405, 'handler' => null, 'params' => null];
if (!$this->checkForHandlers($node)) {
$res['code'] = 404;
return $res;
}
if (isset($node[$method])) {
$res['code'] = 200;
$res['handler'] = $node[$method];
} else {
$res['code'] = 405;
}
return $res;
}
// We'll split up the URI into segments and traverse the node tree
// we'll split up the URI into segments and traverse the node tree
foreach (explode('/', trim($uri, '/')) as $segment) {
// if there is a node for this segment, move to it
if (isset($node[$segment])) {
@ -66,19 +79,31 @@ class Router
// if there is a dynamic segment, move to it and store the value
if (isset($node[':x'])) {
$params[] = $segment;
$res['params'][] = $segment;
$node = $node[':x'];
continue;
}
// if we can't find a node for this segment, return 404
return ['code' => 404, 'handler' => null, 'params' => []];
$res['code'] = 404;
return $res;
}
// if no handlers exist at this node, it's not a valid endpoint - return 404
if (!$this->checkForHandlers($node)) {
$res['code'] = 404;
return $res;
}
// if we found a handler for the method, return it and any params. if not, return a 405
return isset($node[$method])
? ['code' => 200, 'handler' => $node[$method], 'params' => $params ?? []]
: ['code' => 405, 'handler' => null, 'params' => []];
if (isset($node[$method])) {
$res['code'] = 200;
$res['handler'] = $node[$method];
} else {
$res['code'] = 405;
}
return $res;
}
/**
@ -98,6 +123,21 @@ class Router
return $this->routes;
}
/**
* Check if a given node has any method handlers.
*/
private function checkForHandlers(array $node): bool
{
$hasHandlers = false;
foreach (['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] as $m) {
if (isset($node[$m])) {
$hasHandlers = true;
break;
}
}
return $hasHandlers;
}
/**
* Register a GET route.
*/

View File

@ -1,13 +1,12 @@
<?php
require_once 'color.php';
require_once __DIR__ . '/../src/RouterInterface.php';
require_once __DIR__ . '/../src/SegmentRouter.php';
require_once __DIR__ . '/../src/Router.php';
const ROUTES = __DIR__ . '/routes/';
const TREES = __DIR__ . '/trees/';
use Sharkk\Router\SegmentRouter;
use Sharkk\Router\Router;
// if there's a flag, reset the opcache
if (in_array('-f', $argv)) {
@ -18,7 +17,7 @@ if (in_array('-f', $argv)) {
// create the trees directory if it doesn't exist
if (!is_dir(TREES)) mkdir(TREES);
$r = new SegmentRouter();
$r = new Router();
// Blog lookups
$blog = readAndAddRoutes(ROUTES . 'blog.txt', $r);