Renamed existing concept of middleware to filters.
This commit is contained in:
parent
ee540d8137
commit
bdb66bc1ba
Notes:
flash
2025-03-02 02:10:42 +00:00
oopsie RouteInfo was not supposed to be in here
10 changed files with 185 additions and 100 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.2502.282243
|
||||
0.2503.20208
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
// HttpRequest.php
|
||||
// Created: 2022-02-08
|
||||
// Updated: 2025-02-28
|
||||
// Updated: 2025-03-02
|
||||
|
||||
namespace Index\Http;
|
||||
|
||||
|
@ -29,7 +29,7 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
* @param HttpUri $uri HTTP request URI.
|
||||
* @param array<string, string[]> $params HTTP request query parameters.
|
||||
* @param array<string, string> $cookies HTTP request cookies.
|
||||
* @param null|array<string, string[]|object[]>|object $parsedBody Parsed body contents.
|
||||
* @param array<string, array<object|string>>|object|null $parsedBody Parsed body contents.
|
||||
* @param ?array<string, HttpUploadedFile[]> $uploadedFiles Parsed files.
|
||||
*/
|
||||
final public function __construct(
|
||||
|
@ -343,6 +343,24 @@ class HttpRequest extends HttpMessage implements ServerRequestInterface {
|
|||
return $this->with(parsedBody: $data);
|
||||
}
|
||||
|
||||
public static function castRequest(ServerRequestInterface $request): HttpRequest {
|
||||
if($request instanceof HttpRequest)
|
||||
return $request;
|
||||
|
||||
return new HttpRequest(
|
||||
$request->getProtocolVersion(),
|
||||
$request->getHeaders(), // @phpstan-ignore-line: dont care
|
||||
$request->getBody(),
|
||||
[],
|
||||
$request->getMethod(),
|
||||
HttpUri::castUri($request->getUri()),
|
||||
$request->getQueryParams(), // @phpstan-ignore-line: dont care
|
||||
$request->getCookieParams(), // @phpstan-ignore-line: dont care
|
||||
$request->getParsedBody(), // @phpstan-ignore-line: dont care
|
||||
$request->getUploadedFiles(), // @phpstan-ignore-line: dont care
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an HttpRequest instance from the current request.
|
||||
*
|
||||
|
|
14
src/Http/Routing/Filter.php
Normal file
14
src/Http/Routing/Filter.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
// Filter.php
|
||||
// Created: 2024-03-28
|
||||
// Updated: 2025-03-02
|
||||
|
||||
namespace Index\Http\Routing;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* Provides an attribute for marking methods in a class as a filter.
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
|
||||
class Filter extends HandlerAttribute {}
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
// HandlerAttribute.php
|
||||
// Created: 2024-03-28
|
||||
// Updated: 2025-01-18
|
||||
// Updated: 2025-03-02
|
||||
|
||||
namespace Index\Http\Routing;
|
||||
|
||||
|
@ -36,10 +36,10 @@ abstract class HandlerAttribute {
|
|||
$handlerInfo = $attrInfo->newInstance();
|
||||
$closure = $methodInfo->getClosure($methodInfo->isStatic() ? null : $handler);
|
||||
|
||||
if($handlerInfo instanceof HttpRoute)
|
||||
$router->add($handlerInfo->method, $handlerInfo->path, $closure);
|
||||
if($handlerInfo instanceof Route)
|
||||
$router->route($handlerInfo->method, $handlerInfo->path, $closure);
|
||||
else
|
||||
$router->use($handlerInfo->path, $closure);
|
||||
$router->filter($handlerInfo->path, $closure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
// HttpMiddleware.php
|
||||
// Created: 2024-03-28
|
||||
// Updated: 2024-03-28
|
||||
|
||||
namespace Index\Http\Routing;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* Provides an attribute for marking methods in a class as middleware.
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
|
||||
class HttpMiddleware extends HandlerAttribute {}
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
// ResolvedRouteInfo.php
|
||||
// Created: 2024-03-28
|
||||
// Updated: 2025-01-18
|
||||
// Updated: 2025-03-02
|
||||
|
||||
namespace Index\Http\Routing;
|
||||
|
||||
|
@ -10,27 +10,27 @@ namespace Index\Http\Routing;
|
|||
*/
|
||||
class ResolvedRouteInfo {
|
||||
/**
|
||||
* @param array{callable, mixed[]}[] $middlewares Middlewares that should be run prior to the route handler.
|
||||
* @param array{callable, mixed[]}[] $filters Filters that should be run prior to the route handler.
|
||||
* @param string[] $supportedMethods HTTP methods that this route accepts.
|
||||
* @param (callable(): mixed)|null $handler Route handler.
|
||||
* @param mixed[] $args Argument list to pass to the middleware and route handlers.
|
||||
* @param mixed[] $args Argument list to pass to the filter and route handlers.
|
||||
*/
|
||||
public function __construct(
|
||||
private array $middlewares,
|
||||
private array $filters,
|
||||
public private(set) array $supportedMethods,
|
||||
private mixed $handler,
|
||||
private array $args,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Run middleware handlers.
|
||||
* Run filter handlers.
|
||||
*
|
||||
* @param mixed[] $args Additional arguments to pass to the middleware handlers.
|
||||
* @return mixed Return value from the first middleware to return anything non-null, otherwise null.
|
||||
* @param mixed[] $args Additional arguments to pass to the filter handlers.
|
||||
* @return mixed Return value from the first filter to return anything non-null, otherwise null.
|
||||
*/
|
||||
public function runMiddleware(array $args): mixed {
|
||||
foreach($this->middlewares as $middleware) {
|
||||
$result = $middleware[0](...array_merge($args, $middleware[1]));
|
||||
public function runFilters(array $args): mixed {
|
||||
foreach($this->filters as $filter) {
|
||||
$result = $filter[0](...array_merge($args, $filter[1]));
|
||||
if($result !== null)
|
||||
return $result;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
// HttpRoute.php
|
||||
// Route.php
|
||||
// Created: 2024-03-28
|
||||
// Updated: 2025-01-18
|
||||
// Updated: 2025-03-02
|
||||
|
||||
namespace Index\Http\Routing;
|
||||
|
||||
|
@ -11,7 +11,7 @@ use Attribute;
|
|||
* Provides an attribute for marking methods in a class as a route.
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
|
||||
class HttpRoute extends HandlerAttribute {
|
||||
class Route extends HandlerAttribute {
|
||||
/**
|
||||
* @param string $method
|
||||
* @param string $path
|
62
src/Http/Routing/RouteInfo.php
Normal file
62
src/Http/Routing/RouteInfo.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
// RouteInfo.php
|
||||
// Created: 2025-03-02
|
||||
// Updated: 2025-03-02
|
||||
|
||||
namespace Index\Http\Routing;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Information of a route.
|
||||
*/
|
||||
abstract class RouteInfo {
|
||||
/** HTTP method this route serves. */
|
||||
public private(set) string $method;
|
||||
|
||||
/**
|
||||
* Names of middleware to run before calling the method handler. Executed in order of registration.
|
||||
*
|
||||
* @var array<string, array<int|string, mixed>>
|
||||
*/
|
||||
public private(set) array $middlewares = [];
|
||||
|
||||
/**
|
||||
* @param string $method HTTP method this route serves.
|
||||
* @param callable(): void $handler Handler for this route.
|
||||
* @throws InvalidArgumentException If $handler is not a callable.
|
||||
*/
|
||||
public function __construct(
|
||||
string $method,
|
||||
public private(set) $handler,
|
||||
) {
|
||||
if(!is_callable($handler))
|
||||
throw new InvalidArgumentException('$handler must be callable');
|
||||
|
||||
$this->method = strtoupper($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds middleware to the pipeline prior execution of the handler. Executed in order of registration/
|
||||
*
|
||||
* @param string $name Name of the middleware, implementation registered in the router.
|
||||
* @param array<int|string, mixed> $args Additional arguments to call the middleware with.
|
||||
* @throws InvalidArgumentException If $name has already been registered.
|
||||
*/
|
||||
public function require(string $name, array $args = []): void {
|
||||
if(array_key_exists($name, $this->middlewares))
|
||||
throw new InvalidArgumentException('middleware in $name has been registered for this route');
|
||||
|
||||
$this->middlewares[$name] = $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a URI to this route.
|
||||
*
|
||||
* @param UriInterface $uri URI to match against.
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function match(UriInterface $uri): bool;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
// Router.php
|
||||
// Created: 2024-03-28
|
||||
// Updated: 2025-02-28
|
||||
// Updated: 2025-03-02
|
||||
|
||||
namespace Index\Http\Routing;
|
||||
|
||||
|
@ -16,7 +16,7 @@ use Psr\Http\Server\RequestHandlerInterface;
|
|||
|
||||
class Router implements RequestHandlerInterface {
|
||||
/** @var array{handler: callable, match?: string, prefix?: string}[] */
|
||||
private array $middlewares = [];
|
||||
private array $filters = [];
|
||||
|
||||
/** @var array<string, array<string, callable>> */
|
||||
private array $staticRoutes = [];
|
||||
|
@ -100,23 +100,27 @@ class Router implements RequestHandlerInterface {
|
|||
return sprintf('#^%s%s#su', $path, $prefixMatch ? '' : '$');
|
||||
}
|
||||
|
||||
public function use(string $path, callable $handler): void {
|
||||
$mwInfo = [];
|
||||
$mwInfo['handler'] = $handler;
|
||||
public function filter(string $path, callable $handler): void {
|
||||
//
|
||||
|
||||
$filter = [];
|
||||
$filter['handler'] = $handler;
|
||||
|
||||
$prepared = self::preparePath($path, true);
|
||||
if($prepared === false) {
|
||||
if(str_ends_with($path, '/'))
|
||||
$path = substr($path, 0, -1);
|
||||
|
||||
$mwInfo['prefix'] = $path;
|
||||
$filter['prefix'] = $path;
|
||||
} else
|
||||
$mwInfo['match'] = $prepared;
|
||||
$filter['match'] = $prepared;
|
||||
|
||||
$this->middlewares[] = $mwInfo;
|
||||
$this->filters[] = $filter;
|
||||
}
|
||||
|
||||
public function add(string $method, string $path, callable $handler): void {
|
||||
public function route(string $method, string $path, callable $handler): void {
|
||||
//
|
||||
|
||||
if($method === '')
|
||||
throw new InvalidArgumentException('$method may not be empty');
|
||||
|
||||
|
@ -149,22 +153,22 @@ class Router implements RequestHandlerInterface {
|
|||
if(str_ends_with($path, '/'))
|
||||
$path = substr($path, 0, -1);
|
||||
|
||||
$middlewares = [];
|
||||
$filters = [];
|
||||
|
||||
foreach($this->middlewares as $mwInfo) {
|
||||
if(array_key_exists('match', $mwInfo)) {
|
||||
if(preg_match($mwInfo['match'], $path, $args) !== 1)
|
||||
foreach($this->filters as $filter) {
|
||||
if(array_key_exists('match', $filter)) {
|
||||
if(preg_match($filter['match'], $path, $args) !== 1)
|
||||
continue;
|
||||
|
||||
array_shift($args);
|
||||
} elseif(array_key_exists('prefix', $mwInfo)) {
|
||||
if($mwInfo['prefix'] !== '' && !str_starts_with($path, $mwInfo['prefix']))
|
||||
} elseif(array_key_exists('prefix', $filter)) {
|
||||
if($filter['prefix'] !== '' && !str_starts_with($path, $filter['prefix']))
|
||||
continue;
|
||||
|
||||
$args = [];
|
||||
} else continue;
|
||||
|
||||
$middlewares[] = [$mwInfo['handler'], $args];
|
||||
$filters[] = [$filter['handler'], $args];
|
||||
}
|
||||
|
||||
$methods = [];
|
||||
|
@ -190,17 +194,18 @@ class Router implements RequestHandlerInterface {
|
|||
$args = [];
|
||||
}
|
||||
|
||||
return new ResolvedRouteInfo($middlewares, array_keys($methods), $handler, $args);
|
||||
return new ResolvedRouteInfo($filters, array_keys($methods), $handler, $args);
|
||||
}
|
||||
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface {
|
||||
$request = HttpRequest::castRequest($request);
|
||||
$response = new HttpResponseBuilder;
|
||||
$args = [$response, $request];
|
||||
|
||||
$routeInfo = $this->resolve($request->getMethod(), $request->getUri()->getPath());
|
||||
$routeInfo = $this->resolve($request->method, $request->uri->path);
|
||||
|
||||
// always run middleware regardless of 404 or 405
|
||||
$result = $routeInfo->runMiddleware($args);
|
||||
// always run filters regardless of 404 or 405
|
||||
$result = $routeInfo->runFilters($args);
|
||||
if($result === null) {
|
||||
if(!$routeInfo->hasHandler()) {
|
||||
if(empty($routeInfo->supportedMethods)) {
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
<?php
|
||||
// RouterTest.php
|
||||
// Created: 2022-01-20
|
||||
// Updated: 2025-02-28
|
||||
// Updated: 2025-03-02
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use Index\Http\{HttpHeaders,HttpRequest,HttpUri,NullStream};
|
||||
use Index\Http\Routing\{HttpMiddleware,HttpRoute,Router,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{Filter,Route,Router,RouteHandler,RouteHandlerCommon};
|
||||
|
||||
/**
|
||||
* This test isn't super representative of the current functionality
|
||||
* it mostly just does the same tests that were done against the previous implementation
|
||||
*/
|
||||
#[CoversClass(HttpMiddleware::class)]
|
||||
#[CoversClass(HttpRoute::class)]
|
||||
#[CoversClass(Filter::class)]
|
||||
#[CoversClass(Route::class)]
|
||||
#[CoversClass(Router::class)]
|
||||
#[CoversClass(RouteHandler::class)]
|
||||
#[CoversClass(RouteHandlerCommon::class)]
|
||||
|
@ -23,52 +23,52 @@ final class RouterTest extends TestCase {
|
|||
public function testRouter(): void {
|
||||
$router1 = new Router;
|
||||
|
||||
$router1->add('GET', '/', fn() => 'get');
|
||||
$router1->add('POST', '/', fn() => 'post');
|
||||
$router1->add('DELETE', '/', fn() => 'delete');
|
||||
$router1->add('PATCH', '/', fn() => 'patch');
|
||||
$router1->add('PUT', '/', fn() => 'put');
|
||||
$router1->add('CUSTOM', '/', fn() => 'wacky');
|
||||
$router1->route('GET', '/', fn() => 'get');
|
||||
$router1->route('POST', '/', fn() => 'post');
|
||||
$router1->route('DELETE', '/', fn() => 'delete');
|
||||
$router1->route('PATCH', '/', fn() => 'patch');
|
||||
$router1->route('PUT', '/', fn() => 'put');
|
||||
$router1->route('CUSTOM', '/', fn() => 'wacky');
|
||||
|
||||
$this->assertEquals('get', $router1->resolve('GET', '/')->dispatch([]));
|
||||
$this->assertEquals('wacky', $router1->resolve('CUSTOM', '/')->dispatch([]));
|
||||
|
||||
$router1->use('/', function() { /* this one intentionally does nothing */ });
|
||||
$router1->filter('/', function() { /* this one intentionally does nothing */ });
|
||||
|
||||
// registration order should matter
|
||||
$router1->use('/deep', fn() => 'deep');
|
||||
$router1->filter('/deep', fn() => 'deep');
|
||||
|
||||
$postRoot = $router1->resolve('POST', '/');
|
||||
$this->assertNull($postRoot->runMiddleware([]));
|
||||
$this->assertNull($postRoot->runFilters([]));
|
||||
$this->assertEquals('post', $postRoot->dispatch([]));
|
||||
|
||||
$this->assertEquals('deep', $router1->resolve('GET', '/deep/nothing')->runMiddleware([]));
|
||||
$this->assertEquals('deep', $router1->resolve('GET', '/deep/nothing')->runFilters([]));
|
||||
|
||||
$router1->use('/user/([A-Za-z0-9]+)/below', fn(string $user) => 'warioware below ' . $user);
|
||||
$router1->filter('/user/([A-Za-z0-9]+)/below', fn(string $user) => 'warioware below ' . $user);
|
||||
|
||||
$router1->add('GET', '/user/static', fn() => 'the static one');
|
||||
$router1->add('GET', '/user/static/below', fn() => 'below the static one');
|
||||
$router1->add('GET', '/user/([A-Za-z0-9]+)', fn(string $user) => $user);
|
||||
$router1->add('GET', '/user/([A-Za-z0-9]+)/below', fn(string $user) => 'below ' . $user);
|
||||
$router1->route('GET', '/user/static', fn() => 'the static one');
|
||||
$router1->route('GET', '/user/static/below', fn() => 'below the static one');
|
||||
$router1->route('GET', '/user/([A-Za-z0-9]+)', fn(string $user) => $user);
|
||||
$router1->route('GET', '/user/([A-Za-z0-9]+)/below', fn(string $user) => 'below ' . $user);
|
||||
|
||||
$this->assertEquals('below the static one', $router1->resolve('GET', '/user/static/below')->dispatch([]));
|
||||
|
||||
$getWariowareBelowFlashwave = $router1->resolve('GET', '/user/flashwave/below');
|
||||
$this->assertEquals('warioware below flashwave', $getWariowareBelowFlashwave->runMiddleware([]));
|
||||
$this->assertEquals('warioware below flashwave', $getWariowareBelowFlashwave->runFilters([]));
|
||||
$this->assertEquals('below flashwave', $getWariowareBelowFlashwave->dispatch([]));
|
||||
|
||||
$router2 = new Router;
|
||||
$router2->use('/', fn() => 'meow');
|
||||
$router2->add('GET', '/rules', fn() => 'rules page');
|
||||
$router2->add('GET', '/contact', fn() => 'contact page');
|
||||
$router2->add('GET', '/25252', fn() => 'numeric test');
|
||||
$router2->filter('/', fn() => 'meow');
|
||||
$router2->route('GET', '/rules', fn() => 'rules page');
|
||||
$router2->route('GET', '/contact', fn() => 'contact page');
|
||||
$router2->route('GET', '/25252', fn() => 'numeric test');
|
||||
|
||||
$getRules = $router2->resolve('GET', '/rules');
|
||||
$this->assertEquals('meow', $getRules->runMiddleware([]));
|
||||
$this->assertEquals('meow', $getRules->runFilters([]));
|
||||
$this->assertEquals('rules page', $getRules->dispatch([]));
|
||||
|
||||
$get25252 = $router2->resolve('GET', '/25252');
|
||||
$this->assertEquals('meow', $get25252->runMiddleware([]));
|
||||
$this->assertEquals('meow', $get25252->runFilters([]));
|
||||
$this->assertEquals('numeric test', $get25252->dispatch([]));
|
||||
}
|
||||
|
||||
|
@ -77,34 +77,34 @@ final class RouterTest extends TestCase {
|
|||
$handler = new class implements RouteHandler {
|
||||
use RouteHandlerCommon;
|
||||
|
||||
#[HttpRoute('GET', '/')]
|
||||
#[Route('GET', '/')]
|
||||
public function getIndex(): string {
|
||||
return 'index';
|
||||
}
|
||||
|
||||
#[HttpRoute('POST', '/avatar')]
|
||||
#[Route('POST', '/avatar')]
|
||||
public function postAvatar(): string {
|
||||
return 'avatar';
|
||||
}
|
||||
|
||||
#[HttpRoute('PUT', '/static')]
|
||||
#[Route('PUT', '/static')]
|
||||
public static function putStatic(): string {
|
||||
return 'static';
|
||||
}
|
||||
|
||||
#[HttpRoute('GET', '/meow')]
|
||||
#[HttpRoute('POST', '/meow')]
|
||||
#[Route('GET', '/meow')]
|
||||
#[Route('POST', '/meow')]
|
||||
public function multiple(): string {
|
||||
return 'meow';
|
||||
}
|
||||
|
||||
#[HttpMiddleware('/mw')]
|
||||
public function useMw(): string {
|
||||
#[Filter('/filter')]
|
||||
public function useFilter(): string {
|
||||
return 'this intercepts';
|
||||
}
|
||||
|
||||
#[HttpRoute('GET', '/mw')]
|
||||
public function getMw(): string {
|
||||
#[Route('GET', '/filter')]
|
||||
public function getFilter(): string {
|
||||
return 'this is intercepted';
|
||||
}
|
||||
|
||||
|
@ -127,29 +127,29 @@ final class RouterTest extends TestCase {
|
|||
$this->assertEquals('meow', $router->resolve('GET', '/meow')->dispatch([]));
|
||||
$this->assertEquals('meow', $router->resolve('POST', '/meow')->dispatch([]));
|
||||
|
||||
// stopping on middleware is the dispatcher's job
|
||||
$getMw = $router->resolve('GET', '/mw');
|
||||
$this->assertEquals('this intercepts', $getMw->runMiddleware([]));
|
||||
$this->assertEquals('this is intercepted', $getMw->dispatch([]));
|
||||
// stopping on filter is the dispatcher's job
|
||||
$getFilter = $router->resolve('GET', '/filter');
|
||||
$this->assertEquals('this intercepts', $getFilter->runFilters([]));
|
||||
$this->assertEquals('this is intercepted', $getFilter->dispatch([]));
|
||||
}
|
||||
|
||||
public function testEEPROMSituation(): void {
|
||||
$router = new Router;
|
||||
|
||||
$router->add('OPTIONS', '/uploads/([A-Za-z0-9\-_]+)(?:\.(t|json))?', function() {});
|
||||
$router->add('GET', '/uploads/([A-Za-z0-9\-_]+)(?:\.(t|json))?', function() {});
|
||||
$router->add('DELETE', '/uploads/([A-Za-z0-9\-_]+)', function() {});
|
||||
$router->route('OPTIONS', '/uploads/([A-Za-z0-9\-_]+)(?:\.(t|json))?', function() {});
|
||||
$router->route('GET', '/uploads/([A-Za-z0-9\-_]+)(?:\.(t|json))?', function() {});
|
||||
$router->route('DELETE', '/uploads/([A-Za-z0-9\-_]+)', function() {});
|
||||
|
||||
$resolved = $router->resolve('DELETE', '/uploads/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
|
||||
|
||||
$this->assertEquals(['OPTIONS', 'GET', 'DELETE'], $resolved->supportedMethods);
|
||||
}
|
||||
|
||||
public function testMiddlewareInterceptionOnRoot(): void {
|
||||
public function testFilterInterceptionOnRoot(): void {
|
||||
$router = new Router;
|
||||
$router->use('/', fn() => 'expected');
|
||||
$router->add('GET', '/', fn() => 'unexpected');
|
||||
$router->add('GET', '/test', fn() => 'also unexpected');
|
||||
$router->filter('/', fn() => 'expected');
|
||||
$router->route('GET', '/', fn() => 'unexpected');
|
||||
$router->route('GET', '/test', fn() => 'also unexpected');
|
||||
|
||||
ob_start();
|
||||
$router->dispatch(new HttpRequest('1.1', [], NullStream::instance(), [], 'GET', HttpUri::createUri('/'), [], []));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue