feat(router): add error boundary with exception callback
This commit is contained in:
@@ -50,7 +50,7 @@ class Router
|
|||||||
*
|
*
|
||||||
* @var callable $notFoundCallback
|
* @var callable $notFoundCallback
|
||||||
*/
|
*/
|
||||||
public static $notFoundCallback = 'Libs\Router::defaultNotFound';
|
public static $notFoundCallback = Router::defaultNotFound(...);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default callback function for when
|
* Default callback function for when
|
||||||
@@ -64,6 +64,54 @@ class Router
|
|||||||
echo '<h2 style="text-align: center;margin: 25px 0px;">Error 404 - Page Not Found</h2>';
|
echo '<h2 style="text-align: center;margin: 25px 0px;">Error 404 - Page Not Found</h2>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The callback function to be executed when the router's boundary
|
||||||
|
* catches an exception thrown anywhere in the matched route chain.
|
||||||
|
* It receives the exception as a named argument: the handler must
|
||||||
|
* declare its parameter as $exception (typeable as \Throwable).
|
||||||
|
*
|
||||||
|
* @var callable $exceptionCallback
|
||||||
|
*/
|
||||||
|
public static $exceptionCallback = Router::defaultException(...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default callback function for exception responses.
|
||||||
|
*
|
||||||
|
* The HTTP status comes from the exception's code only when it is an
|
||||||
|
* integer in the 400-599 range; anything else (0, arbitrary codes, the
|
||||||
|
* SQLSTATE string carried by PDOException) answers 500. The body is
|
||||||
|
* negotiated from the request's Accept header: JSON when the client asks
|
||||||
|
* for application/json, plain text otherwise. The trace is serialized
|
||||||
|
* from getTraceAsString() because json_encode() of a Throwable yields an
|
||||||
|
* empty object: its properties are protected.
|
||||||
|
*
|
||||||
|
* @param \Throwable $exception
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function defaultException(\Throwable $exception): void
|
||||||
|
{
|
||||||
|
$code = $exception->getCode();
|
||||||
|
http_response_code(is_int($code) && $code >= 400 && $code <= 599 ? $code : 500);
|
||||||
|
|
||||||
|
$accept = $_SERVER['HTTP_ACCEPT'] ?? '';
|
||||||
|
|
||||||
|
if (str_contains($accept, 'application/json')) {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
print(json_encode([
|
||||||
|
'error' => $exception->getMessage(),
|
||||||
|
'exception' => get_class($exception),
|
||||||
|
'file' => $exception->getFile(),
|
||||||
|
'line' => $exception->getLine(),
|
||||||
|
'trace' => explode(PHP_EOL, $exception->getTraceAsString()),
|
||||||
|
]));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print($exception->getMessage() . PHP_EOL . $exception->getTraceAsString());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __construct
|
* __construct
|
||||||
*/
|
*/
|
||||||
@@ -363,12 +411,19 @@ class Router
|
|||||||
/**
|
/**
|
||||||
* Applies the route configuration.
|
* Applies the route configuration.
|
||||||
*
|
*
|
||||||
|
* This method is the framework's error boundary: any \Throwable thrown
|
||||||
|
* while running the matched route's callback chain, while printing the
|
||||||
|
* returned data, or while resolving the not-found callback is caught
|
||||||
|
* here and rendered through $exceptionCallback. A failing handler is
|
||||||
|
* deliberately left uncaught (double failure falls back to PHP itself).
|
||||||
|
*
|
||||||
* @param string|null $path (optional) Path to use. If not defined, it detects the current path.
|
* @param string|null $path (optional) Path to use. If not defined, it detects the current path.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function apply(?string $path = null): void
|
public static function apply(?string $path = null): void
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
$path = $path ?? static::currentPath();
|
$path = $path ?? static::currentPath();
|
||||||
$routers = match ($_SERVER['REQUEST_METHOD']) { // Selects an array of routers based on the method
|
$routers = match ($_SERVER['REQUEST_METHOD']) { // Selects an array of routers based on the method
|
||||||
'POST' => static::$post,
|
'POST' => static::$post,
|
||||||
@@ -408,5 +463,8 @@ class Router
|
|||||||
|
|
||||||
// If no router matches, call $notFoundCallBack
|
// If no router matches, call $notFoundCallBack
|
||||||
Synapsis::resolve(static::$notFoundCallback);
|
Synapsis::resolve(static::$notFoundCallback);
|
||||||
|
} catch (\Throwable $exception) {
|
||||||
|
Synapsis::resolve(static::$exceptionCallback, ['exception' => $exception]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user