Error Handling
Error handling is a fundamental feature of any web application, and this framework provides a clear, robust, and developer-friendly solution for managing both errors and exceptions.
The error handling system is built on top of the popular Whoops library, which offers detailed and user-friendly error pages during development. It allows you to quickly identify, trace, and debug issues with rich stack traces and contextual information.
Configuration
The error handling system can be customized through environment variables and configuration files to suit both development and production environments. This ensures detailed debugging during development while maintaining security in production.
Enable Debugging
To enable debugging and show detailed error pages powered by Whoops, set the APP_DEBUG
variable in your .env
file:
APP_DEBUG=true
When debugging is enabled, exceptions and errors will be displayed with full stack traces, code previews, and request context, useful for fast and effective debugging.
Never enable debugging in production. Always set APP_DEBUG=false
in production environments to avoid exposing
stack traces, internal paths, or sensitive variables to application end users.
Blacklist Sensitive Data
To ensure sensitive information is not shown in debug screens (even in development mode), you can blacklist specific environment or server variables. This prevents data like API keys, tokens, or encryption secrets from being exposed.
You can define the blacklist in your config/app.yaml
file under the debug_blacklist
section:
debug_blacklist:
_ENV:
- APP_KEY
_SERVER:
- APP_KEY
In the example above, the APP_KEY
variable is blacklisted whether it appears in the $_ENV
or $_SERVER
superglobals.
Register Exception Handler
To customize how exceptions are reported and rendered, you can register your own exception handler. This allows you to define logic for logging, rendering custom error pages, or transforming exceptions into specific responses.
By default, every skeleton application includes a pre-defined exception handler located at
App\Exceptions\ExceptionHandler
. This handler is automatically registered in your app/bootstrap.php
file by binding
it to the Zaphyr\Framework\Contracts\Exceptions\Handlers\ExceptionHandlerInterface
:
$application->getContainer()->bindSingleton(
Zaphyr\Framework\Contracts\Exceptions\Handlers\ExceptionHandlerInterface::class,
App\Exceptions\ExceptionHandler::class
);
This setup allows you to easily override or extend the behavior of the framework’s exception handling pipeline.
If you do not register a custom exception handler, the framework will fall back to its built-in default
Zaphyr\Framework\Exceptions\Handlers\ExceptionHandler
.
Handle Exceptions
As mentioned above, every application comes with a default exception handler located at
App\Exceptions\ExceptionHandler
. This handler has one primary responsibility: rendering HTML views for exceptions and
errors via the renderHtmlView
method.
Take a look at your application's resources/views/errors
directory, which contains the HTML templates for error pages.
To customize how HTTP exceptions are rendered, simply add a file named after the corresponding HTTP status code in that
directory, for example, 404.html
, 500.html
, and so on.
The framework will automatically use these templates when rendering exceptions and errors. If no specific template is found for a given status code, a generic error template will be used instead.
Render Exceptions
In some cases, you may want to customize how specific exceptions are rendered instead of relying on the global exception handler. This is useful when certain exceptions should return a tailored response, such as a custom error page or a specific JSON structure.
To achieve this, you can define a render()
method directly on your exception class. This method receives the current
ServerRequestInterface
and should return a valid Response
object. When the exception is thrown, the framework will
automatically use your custom rendering logic if a render()
method is present.
Here’s an example of a custom exception that renders a specific error view:
namespace App\Exceptions;
use Exception;
use Psr\Http\Message\ServerRequestInterface;
use Zaphyr\Framework\Http\Response;
class AppException extends Exception
{
public function render(ServerRequestInterface $request): Response
{
return view('errors/general', statusCode: 500);
}
}
In this example, when the AppException
is thrown, the user will be shown the resources/views/errors/general.html
template with a 500 HTTP status code.
Report Exceptions
By default, exceptions are logged using the framework's built-in logging system. However, you may want to customize
how exceptions are reported, such as sending notifications or integrating with external services. For such cases, the
framework allows you to define a report()
method directly on your custom exception classes.
The report()
method is automatically called by the framework when the exception is thrown. This gives you full control
over how the exception is handled behind the scenes, whether it's writing to a log, sending a Slack alert, or notifying
an external service.
Here’s an example of how to implement a custom exception that reports errors to a Slack channel:
namespace App\Exceptions;
use App\Services\SlackService;
use Exception;
class AppException extends Exception
{
public function report(): void
{
$slack = app()->getContainer()->get(SlackService::class);
$slack->send(
'An error occurred: ' . $this->getMessage(),
[
'context' => [
'file' => $this->getFile(),
'line' => $this->getLine(),
'trace' => $this->getTraceAsString(),
]
]
);
}
}
Ignore Exceptions
You may want to avoid reporting exceptions that are expected or benign, such as validation failures, 404 errors, or specific domain logic exceptions.
To do this, list the exception types you'd like to ignore in the ignore
section of your config/logging.yaml
file.
The logger will silently skip these exceptions.
See the Ignore Errors section in the Logger documentation for full details.
HTTP Exceptions
The framework provides a clean and consistent way to handle HTTP exceptions, which are used to indicate specific HTTP
status codes such as 404 Not Found
, 401 Unauthorized
, or 500 Internal Server Error
. These exceptions are
especially useful for controlling the flow of your application when a particular HTTP error condition needs to be
triggered.
To throw an HTTP exception, simply instantiate the HttpException
class with the appropriate status code and an
optional message:
throw new Zaphyr\Framework\Http\Exceptions\HttpException(404, 'Page not found');
When this exception is thrown, the framework will automatically handle it by returning an HTML error response using a
corresponding error template from your application's resources/views/errors
directory (e.g., 404.html
, 500.html
).
If no specific template exists for a given status code, a generic error view will be rendered.
You can also create your own custom exceptions that extend HttpException
to represent domain-specific errors with
associated status codes.
JSON Responses
If you're building a JSON API or need to return structured error responses in JSON format (e.g., for frontend
applications or third-party consumers), you can use the buildJsonResponse()
method available on the HttpException
class.
This method generates a Zaphyr\Framework\Http\Response
object containing a properly formatted JSON body and the
appropriate HTTP status code:
use Zaphyr\Framework\Http\Exceptions\HttpException;
return (new HttpException(404, 'Page not found'))->buildJsonResponse();
This will return a response like:
{
"error": {
"status": 404,
"message": "Page not found"
}
}
The JSON structure can be customized by extending the HttpException
class and overriding the
buildJsonResponse()
method.
Custom JSON Responses
You can also pass a custom Response
instance to the buildJsonResponse()
method if you need to modify headers, set
cookies, or adjust other response properties before returning the error response. This is particularly useful when
building APIs that require specific headers (e.g., CORS, content type, or rate limiting).
use Zaphyr\Framework\Http\Response;
use Zaphyr\Framework\Http\Exceptions\HttpException;
$response = (new Response())->withHeader('X-Custom-Header', 'ApiError');
return (new HttpException(404, 'Resource not found'))->buildJsonResponse($response);
In this example, the final JSON response will include your custom header alongside the error details, ensuring your API responses remain consistent and extendable.