From aa633a5a24ba30ec782815126235349e30cc6841 Mon Sep 17 00:00:00 2001 From: Alexandre Gomes Gaigalas Date: Wed, 25 Mar 2026 04:15:22 -0300 Subject: [PATCH] Extract handlers, adopt Fluent NamespaceLookup, update docs Move Error, Exception and Status from Routes/ to Handlers/ as standalone handler classes. Replace the internal sideRoutes array with appendHandler() on Router. Rename exceptionRoute()/errorRoute()/ statusRoute() to onException()/onError()/onStatus(). Use Fluent's NamespaceLookup for routine instantiation in AbstractRoute, replacing manual reflection-based class resolution. Update docs/README.md error handling section to reflect the new handler API and add onStatus() example. --- composer.json | 1 + docs/README.md | 18 ++- example/full.php | 4 +- src/DispatchContext.php | 105 ++++++++++-------- src/DispatchEngine.php | 2 +- .../Error.php => Handlers/ErrorHandler.php} | 5 +- .../ExceptionHandler.php} | 5 +- .../Status.php => Handlers/StatusHandler.php} | 6 +- src/RouteProvider.php | 2 +- src/Router.php | 64 +++++++---- src/Routes/AbstractRoute.php | 26 ++--- tests/RouterTest.php | 6 +- tests/Routes/ErrorTest.php | 10 +- tests/Routes/ExceptionTest.php | 16 +-- tests/Routes/StatusTest.php | 14 +-- 15 files changed, 160 insertions(+), 124 deletions(-) rename src/{Routes/Error.php => Handlers/ErrorHandler.php} (82%) rename src/{Routes/Exception.php => Handlers/ExceptionHandler.php} (82%) rename src/{Routes/Status.php => Handlers/StatusHandler.php} (68%) diff --git a/composer.json b/composer.json index 91229b6..24173e6 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "require": { "php": ">=8.5", "psr/container": "^2.0", + "respect/fluent": "^2.0", "psr/http-factory": "^1.0", "psr/http-message": "^2.0", "psr/http-server-handler": "^1.0", diff --git a/docs/README.md b/docs/README.md index 909998c..2503880 100644 --- a/docs/README.md +++ b/docs/README.md @@ -546,24 +546,32 @@ You can use any combination of the above but also need to implement the `Routina ## Error Handling -Respect\Rest provides two special ways to handle errors. The first one is using exception -routes: +Respect\Rest provides handlers for exceptions and errors. Register an exception +handler with `onException`: ```php -$r3->exceptionRoute('InvalidArgumentException', function (InvalidArgumentException $e) { +$r3->onException('InvalidArgumentException', function (InvalidArgumentException $e) { return 'Sorry, this error happened: ' . $e->getMessage(); }); ``` Whenever an uncaught exception appears on any route, it will be caught and forwarded to -this side route. Similarly, there is a route for PHP errors: +this handler. Similarly, there is a handler for PHP errors: ```php -$r3->errorRoute(function (array $err) { +$r3->onError(function (array $err) { return 'Sorry, these errors happened: ' . var_export($err, true); }); ``` +You can also handle specific HTTP status codes: + +```php +$r3->onStatus(404, function () { + return 'Page not found'; +}); +``` + *** See also: diff --git a/example/full.php b/example/full.php index 2258c4f..1195ee6 100644 --- a/example/full.php +++ b/example/full.php @@ -184,11 +184,11 @@ public function get(string $id): string throw new RuntimeException('Something went wrong!'); }); -$r3->exceptionRoute('RuntimeException', function (RuntimeException $e) { +$r3->onException('RuntimeException', function (RuntimeException $e) { return 'Caught exception: ' . $e->getMessage(); }); -$r3->errorRoute(function (array $err) { +$r3->onError(function (array $err) { return 'Error occurred: ' . ($err[0]['message'] ?? 'unknown'); }); diff --git a/src/DispatchContext.php b/src/DispatchContext.php index 8814149..b97a2f5 100644 --- a/src/DispatchContext.php +++ b/src/DispatchContext.php @@ -10,9 +10,13 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\StreamFactoryInterface; use Respect\Parameter\Resolver; +use Respect\Rest\Handlers\ErrorHandler; +use Respect\Rest\Handlers\ExceptionHandler; +use Respect\Rest\Handlers\StatusHandler; use Respect\Rest\Routes\AbstractRoute; use Throwable; +use function in_array; use function is_a; use function rawurldecode; use function rtrim; @@ -50,7 +54,7 @@ final class DispatchContext implements ContainerInterface private string $effectivePath = ''; /** @var array */ - private array $sideRoutes = []; + private array $handlers = []; private Resolver|null $resolver = null; @@ -138,23 +142,22 @@ public function response(): ResponseInterface|null } $route = $this->route; + $isHandler = in_array($route, $this->handlers, true); + $previousErrorHandler = $isHandler ? null : $this->installErrorHandler(); try { - $errorHandler = $this->prepareForErrorForwards($route); $preRoutineResult = $this->routinePipeline()->processBy($this, $route); - if ($preRoutineResult !== null) { - if ($preRoutineResult instanceof AbstractRoute) { - return $this->forward($preRoutineResult); - } + if ($preRoutineResult instanceof AbstractRoute) { + return $this->forward($preRoutineResult); + } - if ($preRoutineResult instanceof ResponseInterface) { - return $this->finalizeResponse($preRoutineResult); - } + if ($preRoutineResult instanceof ResponseInterface) { + return $this->finalizeResponse($preRoutineResult); + } - if ($preRoutineResult === false) { - return $this->finalizeResponse(''); - } + if ($preRoutineResult === false) { + return $this->finalizeResponse(''); } $rawResult = $route->dispatchTarget($this->method(), $this->params, $this); @@ -164,20 +167,28 @@ public function response(): ResponseInterface|null } $processedResult = $this->routinePipeline()->processThrough($this, $route, $rawResult); - $errorResponse = $this->forwardErrors($errorHandler, $route); - if ($errorResponse !== null) { - return $errorResponse; + if (!$isHandler) { + $errorResponse = $this->forwardCollectedErrors(); + if ($errorResponse !== null) { + return $errorResponse; + } } return $this->finalizeResponse($processedResult); } catch (Throwable $e) { - $exceptionResponse = $this->catchExceptions($e, $route); - if ($exceptionResponse === null) { - throw $e; + if (!$isHandler) { + $exceptionResponse = $this->catchExceptions($e); + if ($exceptionResponse !== null) { + return $exceptionResponse; + } } - return $exceptionResponse; + throw $e; + } finally { + if ($previousErrorHandler !== null) { + set_error_handler($previousErrorHandler); + } } } @@ -193,10 +204,10 @@ public function setRoutinePipeline(RoutinePipeline $routinePipeline): void $this->routinePipeline = $routinePipeline; } - /** @param array $sideRoutes */ - public function setSideRoutes(array $sideRoutes): void + /** @param array $handlers */ + public function setHandlers(array $handlers): void { - $this->sideRoutes = $sideRoutes; + $this->handlers = $handlers; } public function setResponder(Responder $responder): void @@ -228,19 +239,19 @@ public function get(string $id): mixed throw new NotFoundException(sprintf('No entry found for "%s"', $id)); } - /** @return callable|null The previous error handler, or null */ - protected function prepareForErrorForwards(AbstractRoute $route): callable|null + /** @return callable|null The previous error handler, or null if no ErrorHandler is registered */ + private function installErrorHandler(): callable|null { - foreach ($route->sideRoutes as $sideRoute) { - if ($sideRoute instanceof Routes\Error) { + foreach ($this->handlers as $handler) { + if ($handler instanceof ErrorHandler) { return set_error_handler( static function ( int $errno, string $errstr, string $errfile = '', int $errline = 0, - ) use ($sideRoute): bool { - $sideRoute->errors[] = [$errno, $errstr, $errfile, $errline]; + ) use ($handler): bool { + $handler->errors[] = [$errno, $errstr, $errfile, $errline]; return true; }, @@ -251,54 +262,50 @@ static function ( return null; } - protected function forwardErrors(callable|null $errorHandler, AbstractRoute $route): ResponseInterface|null + private function forwardCollectedErrors(): ResponseInterface|null { - if ($errorHandler !== null) { - set_error_handler($errorHandler); - } - - foreach ($route->sideRoutes as $sideRoute) { - if ($sideRoute instanceof Routes\Error && $sideRoute->errors) { - return $this->forward($sideRoute); + foreach ($this->handlers as $handler) { + if ($handler instanceof ErrorHandler && $handler->errors) { + return $this->forward($handler); } } return null; } - protected function catchExceptions(Throwable $e, AbstractRoute $route): ResponseInterface|null + private function catchExceptions(Throwable $e): ResponseInterface|null { - foreach ($route->sideRoutes as $sideRoute) { - if (!$sideRoute instanceof Routes\Exception) { + foreach ($this->handlers as $handler) { + if (!$handler instanceof ExceptionHandler) { continue; } - if (is_a($e, $sideRoute->class)) { - $sideRoute->exception = $e; + if (is_a($e, $handler->class)) { + $handler->exception = $e; - return $this->forward($sideRoute); + return $this->forward($handler); } } return null; } - protected function forwardToStatusRoute(ResponseInterface $preparedResponse): ResponseInterface|null + private function forwardToStatusRoute(ResponseInterface $preparedResponse): ResponseInterface|null { $statusCode = $preparedResponse->getStatusCode(); - foreach ($this->sideRoutes as $sideRoute) { + foreach ($this->handlers as $handler) { if ( - $sideRoute instanceof Routes\Status - && ($sideRoute->statusCode === $statusCode || $sideRoute->statusCode === null) + $handler instanceof StatusHandler + && ($handler->statusCode === $statusCode || $handler->statusCode === null) ) { $this->hasStatusOverride = true; // Run routine negotiation (e.g. Accept) before forwarding, // since the normal route-selection phase was skipped - $this->routinePipeline()->matches($this, $sideRoute, $this->params); + $this->routinePipeline()->matches($this, $handler, $this->params); - $result = $this->forward($sideRoute); + $result = $this->forward($handler); // Preserve the original status code on the forwarded response return $result?->withStatus($statusCode); @@ -308,7 +315,7 @@ protected function forwardToStatusRoute(ResponseInterface $preparedResponse): Re return null; } - protected function finalizeResponse(mixed $response): ResponseInterface + private function finalizeResponse(mixed $response): ResponseInterface { return $this->responder()->finalize( $response, diff --git a/src/DispatchEngine.php b/src/DispatchEngine.php index e621ed9..bedb724 100644 --- a/src/DispatchEngine.php +++ b/src/DispatchEngine.php @@ -60,7 +60,7 @@ public function dispatchContext(DispatchContext $context): DispatchContext } $context->setRoutinePipeline($this->routinePipeline); - $context->setSideRoutes($this->routeProvider->getSideRoutes()); + $context->setHandlers($this->routeProvider->getHandlers()); if (!$this->isRoutelessDispatch($context) && $context->route === null) { $this->routeDispatch($context); diff --git a/src/Routes/Error.php b/src/Handlers/ErrorHandler.php similarity index 82% rename from src/Routes/Error.php rename to src/Handlers/ErrorHandler.php index 9dc6091..df5688a 100644 --- a/src/Routes/Error.php +++ b/src/Handlers/ErrorHandler.php @@ -2,11 +2,12 @@ declare(strict_types=1); -namespace Respect\Rest\Routes; +namespace Respect\Rest\Handlers; use Respect\Rest\DispatchContext; +use Respect\Rest\Routes\Callback; -final class Error extends Callback +final class ErrorHandler extends Callback { /** @var callable */ public $callback; diff --git a/src/Routes/Exception.php b/src/Handlers/ExceptionHandler.php similarity index 82% rename from src/Routes/Exception.php rename to src/Handlers/ExceptionHandler.php index ccc5d1e..f5aa703 100644 --- a/src/Routes/Exception.php +++ b/src/Handlers/ExceptionHandler.php @@ -2,12 +2,13 @@ declare(strict_types=1); -namespace Respect\Rest\Routes; +namespace Respect\Rest\Handlers; use Respect\Rest\DispatchContext; +use Respect\Rest\Routes\Callback; use Throwable; -final class Exception extends Callback +final class ExceptionHandler extends Callback { /** @var callable */ public $callback; diff --git a/src/Routes/Status.php b/src/Handlers/StatusHandler.php similarity index 68% rename from src/Routes/Status.php rename to src/Handlers/StatusHandler.php index a3da99d..dd03594 100644 --- a/src/Routes/Status.php +++ b/src/Handlers/StatusHandler.php @@ -2,9 +2,11 @@ declare(strict_types=1); -namespace Respect\Rest\Routes; +namespace Respect\Rest\Handlers; -final class Status extends Callback +use Respect\Rest\Routes\Callback; + +final class StatusHandler extends Callback { /** @var callable */ public $callback; diff --git a/src/RouteProvider.php b/src/RouteProvider.php index 75fb1cb..8306185 100644 --- a/src/RouteProvider.php +++ b/src/RouteProvider.php @@ -12,7 +12,7 @@ interface RouteProvider public function getRoutes(): array; /** @return array */ - public function getSideRoutes(): array; + public function getHandlers(): array; public function getBasePath(): string; } diff --git a/src/Router.php b/src/Router.php index a3954a1..b3ae4c2 100644 --- a/src/Router.php +++ b/src/Router.php @@ -11,11 +11,14 @@ use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; -use ReflectionClass; +use Respect\Fluent\Factories\NamespaceLookup; +use Respect\Fluent\Resolvers\Ucfirst; use Respect\Rest\Routes\AbstractRoute; +use Respect\Rest\Routines\Routinable; use Throwable; use function array_pop; +use function assert; use function class_exists; use function count; use function interface_exists; @@ -53,23 +56,28 @@ final class Router implements MiddlewareInterface, RequestHandlerInterface, Rout protected array $routes = []; /** @var array */ - protected array $sideRoutes = []; + protected array $handlers = []; private DispatchEngine|null $dispatchEngine = null; + private NamespaceLookup $routineLookup; + public function __construct( protected string $basePath, private ResponseFactoryInterface&StreamFactoryInterface $factory, ) { $this->basePath = rtrim($basePath, '/'); + $this->routineLookup = new NamespaceLookup( + new Ucfirst(), + Routinable::class, + 'Respect\\Rest\\Routines', + ); } public function always(string $routineName, mixed ...$params): static { - /** @var class-string $routineClassName */ - $routineClassName = 'Respect\\Rest\\Routines\\' . $routineName; - $routineClass = new ReflectionClass($routineClassName); - $routineInstance = $routineClass->newInstanceArgs($params); + $routineInstance = $this->routineLookup->create($routineName, $params); + assert($routineInstance instanceof Routinable); $this->globalRoutines[] = $routineInstance; foreach ($this->routes as $route) { @@ -79,11 +87,18 @@ public function always(string $routineName, mixed ...$params): static return $this; } + public function withRoutineNamespace(string $namespace): static + { + $this->routineLookup = $this->routineLookup->withNamespace($namespace); + + return $this; + } + public function appendRoute(AbstractRoute $route): static { $this->routes[] = $route; - $route->sideRoutes = &$this->sideRoutes; $route->basePath = $this->basePath; + $route->setRoutineLookup($this->routineLookup); foreach ($this->globalRoutines as $routine) { $route->appendRoutine($routine); @@ -94,12 +109,13 @@ public function appendRoute(AbstractRoute $route): static return $this; } - public function appendSideRoute(AbstractRoute $route): static + public function appendHandler(AbstractRoute $handler): static { - $this->sideRoutes[] = $route; + $this->handlers[] = $handler; + $handler->setRoutineLookup($this->routineLookup); foreach ($this->globalRoutines as $routine) { - $route->appendRoutine($routine); + $handler->appendRoutine($routine); } return $this; @@ -170,34 +186,34 @@ public function dispatchContext(DispatchContext $context): DispatchContext return $this->dispatchEngine()->dispatchContext($context); } - public function exceptionRoute(string $className, callable $callback): Routes\Exception + public function onException(string $className, callable $callback): Handlers\ExceptionHandler { - $route = new Routes\Exception($className, $callback); - $this->appendSideRoute($route); + $handler = new Handlers\ExceptionHandler($className, $callback); + $this->appendHandler($handler); - return $route; + return $handler; } - public function errorRoute(callable $callback): Routes\Error + public function onError(callable $callback): Handlers\ErrorHandler { - $route = new Routes\Error($callback); - $this->appendSideRoute($route); + $handler = new Handlers\ErrorHandler($callback); + $this->appendHandler($handler); - return $route; + return $handler; } - public function statusRoute(int|null $statusCode, callable $callback): Routes\Status + public function onStatus(int|null $statusCode, callable $callback): Handlers\StatusHandler { - $route = new Routes\Status($statusCode, $callback); - $this->appendSideRoute($route); + $handler = new Handlers\StatusHandler($statusCode, $callback); + $this->appendHandler($handler); - return $route; + return $handler; } /** @return array */ - public function getSideRoutes(): array + public function getHandlers(): array { - return $this->sideRoutes; + return $this->handlers; } public function factoryRoute(string $method, string $path, string $className, callable $factory): Routes\Factory diff --git a/src/Routes/AbstractRoute.php b/src/Routes/AbstractRoute.php index d1e0db6..8c8fe47 100644 --- a/src/Routes/AbstractRoute.php +++ b/src/Routes/AbstractRoute.php @@ -4,8 +4,8 @@ namespace Respect\Rest\Routes; -use ReflectionClass; use ReflectionFunctionAbstract; +use Respect\Fluent\Factories\NamespaceLookup; use Respect\Rest\DispatchContext; use Respect\Rest\Routines\IgnorableFileExtension; use Respect\Rest\Routines\Routinable; @@ -15,6 +15,7 @@ use function array_merge; use function array_pop; use function array_shift; +use function assert; use function end; use function explode; use function implode; @@ -32,7 +33,6 @@ use function strripos; use function strtoupper; use function substr; -use function ucfirst; use function usort; /** @@ -74,11 +74,10 @@ abstract class AbstractRoute /** @var array */ public array $routines = []; - /** @var array */ - public array $sideRoutes = []; - public string|null $basePath = null; + private NamespaceLookup $routineLookup; + public function __construct(string $method, public string $pattern = '') { $this->method = strtoupper($method); @@ -148,6 +147,11 @@ public function appendRoutine(Routinable $routine): static return $this; } + public function setRoutineLookup(NamespaceLookup $lookup): void + { + $this->routineLookup = $lookup; + } + public function createUri(mixed ...$params): string { $params = preg_replace('#(? $className */ - $className = 'Respect\\Rest\\Routines\\' . ucfirst($method); - $reflection = new ReflectionClass($className); - // phpcs:ignore SlevomatCodingStandard.PHP.RequireExplicitAssertion - /** @var Routinable $instance */ - $instance = $reflection->newInstanceArgs($arguments); - - return $this->appendRoutine($instance); + $routine = $this->routineLookup->create($method, $arguments); + assert($routine instanceof Routinable); + + return $this->appendRoutine($routine); } } diff --git a/tests/RouterTest.php b/tests/RouterTest.php index a4251e5..7fa1fcf 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -1861,7 +1861,7 @@ public function testDispatchEngineHandlePreservesExceptionRoutes(): void $router->get('/', static function (): void { throw new InvalidArgumentException('boom'); }); - $router->exceptionRoute(InvalidArgumentException::class, static function () { + $router->onException(InvalidArgumentException::class, static function () { return 'caught'; }); @@ -1882,7 +1882,7 @@ public function testExceptionRouteRespectsGlobalAcceptRoutine(): void $router->get('/', static function (): never { throw new InvalidArgumentException('boom'); }); - $router->exceptionRoute(InvalidArgumentException::class, static function (InvalidArgumentException $e) { + $router->onException(InvalidArgumentException::class, static function (InvalidArgumentException $e) { return $e->getMessage(); }); @@ -1900,7 +1900,7 @@ public function testExceptionRouteReturningEmptyStringDoesNotRethrow(): void $router->get('/', static function (): never { throw new InvalidArgumentException('boom'); }); - $router->exceptionRoute(InvalidArgumentException::class, static function () { + $router->onException(InvalidArgumentException::class, static function () { return ''; }); diff --git a/tests/Routes/ErrorTest.php b/tests/Routes/ErrorTest.php index 062f3b0..7787f73 100644 --- a/tests/Routes/ErrorTest.php +++ b/tests/Routes/ErrorTest.php @@ -14,13 +14,13 @@ use const E_USER_WARNING; -/** @covers Respect\Rest\Routes\Error */ +/** @covers Respect\Rest\Handlers\ErrorHandler */ final class ErrorTest extends TestCase { /** - * @covers Respect\Rest\Routes\Error::getReflection - * @covers Respect\Rest\Routes\Error::runTarget - * @covers Respect\Rest\Router::errorRoute + * @covers Respect\Rest\Handlers\ErrorHandler::getReflection + * @covers Respect\Rest\Handlers\ErrorHandler::runTarget + * @covers Respect\Rest\Router::onError */ #[RunInSeparateProcess] public function testMagicConstuctorCanCreateRoutesToErrors(): void @@ -28,7 +28,7 @@ public function testMagicConstuctorCanCreateRoutesToErrors(): void $router = new Router('', new Psr17Factory()); $called = false; $phpUnit = $this; - $router->errorRoute(static function ($err) use (&$called, $phpUnit) { + $router->onError(static function ($err) use (&$called, $phpUnit) { $called = true; $phpUnit->assertContains( 'Oops', diff --git a/tests/Routes/ExceptionTest.php b/tests/Routes/ExceptionTest.php index f0bdd9c..83e2901 100644 --- a/tests/Routes/ExceptionTest.php +++ b/tests/Routes/ExceptionTest.php @@ -12,20 +12,20 @@ use RuntimeException; use Throwable; -/** @covers Respect\Rest\Routes\Exception */ +/** @covers Respect\Rest\Handlers\ExceptionHandler */ final class ExceptionTest extends TestCase { /** - * @covers Respect\Rest\Routes\Exception::getReflection - * @covers Respect\Rest\Routes\Exception::runTarget - * @covers Respect\Rest\Router::exceptionRoute + * @covers Respect\Rest\Handlers\ExceptionHandler::getReflection + * @covers Respect\Rest\Handlers\ExceptionHandler::runTarget + * @covers Respect\Rest\Router::onException */ public function testMagicConstuctorCanCreateRoutesToExceptions(): void { $router = new Router('', new Psr17Factory()); $called = false; $phpUnit = $this; - $router->exceptionRoute('RuntimeException', static function ($e) use (&$called, $phpUnit) { + $router->onException('RuntimeException', static function ($e) use (&$called, $phpUnit) { $called = true; $phpUnit->assertEquals( 'Oops', @@ -54,7 +54,7 @@ public function testMagicConstuctorCanCreateRoutesToExceptions(): void public function testExceptionRouteCatchesSubclassViaInheritance(): void { $router = new Router('', new Psr17Factory()); - $router->exceptionRoute('RuntimeException', static fn($e) => 'caught: ' . $e->getMessage()); + $router->onException('RuntimeException', static fn($e) => 'caught: ' . $e->getMessage()); $router->get('/', static function (): void { throw new PDOException('db error'); }); @@ -67,7 +67,7 @@ public function testExceptionRouteCatchesSubclassViaInheritance(): void public function testThrowableExceptionRouteCatchesAll(): void { $router = new Router('', new Psr17Factory()); - $router->exceptionRoute('Throwable', static fn(Throwable $e) => 'caught: ' . $e::class); + $router->onException('Throwable', static fn(Throwable $e) => 'caught: ' . $e::class); $router->get('/', static function (): void { throw new RuntimeException('test'); }); @@ -80,7 +80,7 @@ public function testThrowableExceptionRouteCatchesAll(): void public function testExceptionRouteWorksViaHandle(): void { $router = new Router('', new Psr17Factory()); - $router->exceptionRoute('Throwable', static fn(Throwable $e) => 'handled: ' . $e->getMessage()); + $router->onException('Throwable', static fn(Throwable $e) => 'handled: ' . $e->getMessage()); $router->get('/', static function (): void { throw new RuntimeException('boom'); }); diff --git a/tests/Routes/StatusTest.php b/tests/Routes/StatusTest.php index b9c080f..6675f5c 100644 --- a/tests/Routes/StatusTest.php +++ b/tests/Routes/StatusTest.php @@ -10,14 +10,14 @@ use Psr\Http\Message\ServerRequestInterface; use Respect\Rest\Router; -/** @covers Respect\Rest\Routes\Status */ +/** @covers Respect\Rest\Handlers\StatusHandler */ final class StatusTest extends TestCase { public function testStatusRoute404(): void { $router = new Router('', new Psr17Factory()); $router->get('/exists', static fn() => 'ok'); - $router->statusRoute(404, static fn(ServerRequestInterface $r) => 'Not found: ' . $r->getUri()->getPath()); + $router->onStatus(404, static fn(ServerRequestInterface $r) => 'Not found: ' . $r->getUri()->getPath()); $response = $router->handle(new ServerRequest('GET', '/nope')); @@ -29,7 +29,7 @@ public function testStatusRoute405(): void { $router = new Router('', new Psr17Factory()); $router->get('/resource', static fn() => 'ok'); - $router->statusRoute(405, static fn() => 'Method not allowed'); + $router->onStatus(405, static fn() => 'Method not allowed'); $response = $router->handle(new ServerRequest('DELETE', '/resource')); @@ -42,7 +42,7 @@ public function testStatusRoute400FromWhenFailure(): void { $router = new Router('', new Psr17Factory()); $router->get('/guarded', static fn() => 'ok')->when(static fn() => false); - $router->statusRoute(400, static fn() => 'Bad request'); + $router->onStatus(400, static fn() => 'Bad request'); $response = $router->handle(new ServerRequest('GET', '/guarded')); @@ -54,7 +54,7 @@ public function testStatusRouteDoesNotInterfereWithNormalRoutes(): void { $router = new Router('', new Psr17Factory()); $router->get('/hello', static fn() => 'world'); - $router->statusRoute(404, static fn() => 'not found'); + $router->onStatus(404, static fn() => 'not found'); $response = $router->handle(new ServerRequest('GET', '/hello')); @@ -77,7 +77,7 @@ public function testStatusRouteReturnsArrayAsJson(): void { $router = new Router('', new Psr17Factory()); $router->get('/exists', static fn() => 'ok'); - $router->statusRoute( + $router->onStatus( 404, static fn(ServerRequestInterface $r) => ['error' => 'Not found', 'path' => $r->getUri()->getPath()], ); @@ -93,7 +93,7 @@ public function testStatusRouteWorksViaDispatch(): void { $router = new Router('', new Psr17Factory()); $router->get('/exists', static fn() => 'ok'); - $router->statusRoute(404, static fn() => 'custom 404'); + $router->onStatus(404, static fn() => 'custom 404'); $response = $router->dispatch(new ServerRequest('GET', '/nope'))->response();