Spiral Framework provides multiple ways to handle critical and application-level exceptions.
You can create an HTTP middleware to intercept any specific type of exception thrown inside your controllers. We can use Whoops to demonstrate how to write it.
composer require filp/whoops
And our middleware:
namespace App\Middleware;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class WhoopsMiddleware implements MiddlewareInterface
{
private ResponseFactoryInterface $responseFactory;
public function __construct(ResponseFactoryInterface $responseFactory)
{
$this->responseFactory = $responseFactory;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
try {
return $handler->handle($request);
} catch (\Throwable $e) {
$response = $this->responseFactory->createResponse(500);
$response->getBody()->write($this->renderWhoops($e));
return $response;
}
}
private function renderWhoops(\Throwable $e): string
{
$whoops = new \Whoops\Run();
$whoops->allowQuit(false);
$whoops->writeToOutput(false);
$handler = new \Whoops\Handler\PrettyPageHandler();
$handler->handleUnconditionally(true); // whoops does not know about RoadRunner
$whoops->prependHandler($handler);
return $whoops->handleException($e);
}
}
Make sure to enable this middleware via Bootloader:
use App\Middleware\WhoopsMiddleware;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Bootloader\Http\HttpBootloader;
class WhoopsBootloader extends Bootloader
{
public function boot(HttpBootloader $http)
{
$http->addMiddleware(WhoopsMiddleware::class);
}
}
Note
Do not forget to disable the defaultSpiral\Bootloader\Http\ErrorHandlerBootloader
.
You can use this approach to suppress specific types of exceptions in your application.
You can also handle domain-specific exceptions in Spiral\Core\CoreInterface
. We can use Whoops to demonstrate how to
write it.
composer require filp/whoops
And our interceptor:
namespace App\Interceptor;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use \Spiral\Core\CoreInterface;
use \Spiral\Core\CoreInterceptorInterface;
class WhoopsInterceptor implements CoreInterceptorInterface
{
private ResponseFactoryInterface $responseFactory;
public function __construct(ResponseFactoryInterface $responseFactory)
{
$this->responseFactory = $responseFactory;
}
public function process(string $controller, string $action, array $parameters, CoreInterface $core): ResponseInterface
{
try {
return $core->callAction($controller, $action, $parameters);
} catch (\Throwable $e) {
$response = $this->responseFactory->createResponse(500);
$response->getBody()->write($this->renderWhoops($e));
return $response;
}
}
private function renderWhoops(\Throwable $e): string
{
$whoops = new \Whoops\Run();
$whoops->allowQuit(false);
$whoops->writeToOutput(false);
$handler = new \Whoops\Handler\PrettyPageHandler();
$handler->handleUnconditionally(true); // whoops does not know about RoadRunner
$whoops->prependHandler($handler);
return $whoops->handleException($e);
}
}
Make sure to enable this interceptor via the Bootloader:
use App\Middleware\WhoopsMiddleware;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Bootloader\Http\HttpBootloader;
use Spiral\Bootloader\DomainBootloader;
class AppBootloader extends DomainBootloader
{
protected const INTERCEPTORS = [
//...
WhoopsInterceptor::class,
];
}
In addition to the custom middleware, the framework provides a unified way to handle exception registration (including fatal
exceptions). This functionality is delivered by the spiral/snapshots
package and is intended for exception registration in
external monitoring solutions (for example, Sentry):
You can implement your own snapshot provider via Spiral\Snapshots\SnapshotterInterface
:
use Spiral\Snapshots\Snapshot;
use Spiral\Snapshots\SnapshotInterface;
use Spiral\Snapshots\SnapshotterInterface;
class MySnapshotter implements SnapshotterInterface
{
public function register(\Throwable $e): SnapshotInterface
{
// register exception in log, Sentry or etc
return new Snapshot('unique-id', $e);
}
}
Note
Make sure to bind your implementation toSpiral\Snapshots\SnapshotterInterface
to enable it.