Error Handling & Logging
Rhapsody includes a robust global error handler that catches exceptions, converts PHP errors to exceptions, and handles fatal shutdown errors. In development you get detailed Whoops pages; in production you get clean, user‑friendly error pages while all errors are logged.
How It Works
The Rhapsody\Core\ErrorHandler class is registered early in index.php. It sets three PHP handlers:
set_error_handler()– converts warnings, notices, etc. intoErrorException.set_exception_handler()– catches all uncaught exceptions.register_shutdown_function()– catches fatal errors (e.g., missing class, parse errors).
Configuration
The error handler reads the logging array from config.php. Add the error_log_path to specify where errors should be written:
'logging' => [
'error_log_path' => __DIR__ . '/storage/logs/errors.log',
// other log paths...
],
Development vs. Production
Development Mode (APP_ENV=development)
When an error occurs, the handler displays a Whoops error page with:
- Full stack trace with clickable file links.
- Code snippet around the error line.
- Environment and request data.
- Interactive console to inspect variables.
Errors are still logged, but the visual feedback helps you debug quickly.
Production Mode (APP_ENV=production)
The handler shows a generic error page to the user – no sensitive details are leaked. Meanwhile, the full error (including stack trace) is written to the configured log file.
By default, HTTP status codes are respected:
- 404 Not Found – when a route doesn’t match.
- 500 Internal Server Error – for all other exceptions.
Custom Error Views
You can create custom Twig templates for error pages. Place them in views/errors/ with the status code as the filename:
views/errors/404.twig– shown when a page is not found.views/errors/500.twig– shown for server errors.- You can add any HTTP status code (e.g.,
403.twig,503.twig).
Example 404.twig:
&lcur;% extends "layouts/main.twig" %}
&lcur;% block title %}404 Not Found&lcur;% endblock %}
&lcur;% block content %}
<div class="text-center py-20">
<h1 class="text-6xl font-bold">404</h1>
<p class="text-xl mt-4">Page not found.</p>
<a href="&lcur;&lcur; base_url }}/">Go Home</a>
</div>
&lcur;% endblock %}
If no custom view exists for a status code, the handler falls back to a simple HTML error message.
Throwing HTTP Exceptions
For more control, you can throw Rhapsody\Core\Exceptions\HttpException (optional class – you need to create it). Example:
use Rhapsody\Core\Exceptions\HttpException;
public function show(string $id): Response
{
$post = $this->postModel->findById($id);
if (!$post) {
throw new HttpException(404, 'Post not found.');
}
// ...
}
The error handler will then use the provided status code and message, and show the corresponding error view (if exists).
Logging Format
Each error is logged using the Rhapsody\Core\Logger class. A typical log entry looks like:
[2025-06-15 14:32:10] ERROR: [PDOException] SQLSTATE[42S02]: Base table or view not found in /var/www/app/Models/User.php:45
Stack trace:
#0 /var/www/app/Models/User.php(45): PDO->prepare()
#1 /var/www/app/Controllers/UserController.php(22): App\Models\User->findById()
#2 /var/www/core/Router.php(89): App\Controllers\UserController->show()
...
Manual Logging
Even without errors, you can write to the same log file using the Logger class:
$logger = new \Core\Logger($config['logging']['error_log_path']);
$logger->log('User 123 logged out.', 'INFO');
Disabling the Error Handler
In rare cases (e.g., running unit tests), you may want to disable the custom handler. Call restore_error_handler() and restore_exception_handler() manually.