Error Handling
Comprehensive error handling system with beautiful debug views and production-ready error pages.
Overview
The Larafony error handling system captures all uncaught exceptions and PHP errors, rendering them through
beautiful Blade templates. It automatically switches between detailed debug views for development and
clean, user-friendly error pages for production based on the APP_DEBUG environment variable.
Key Features
- Native PHP Exception Handlers - Uses
set_exception_handler()andset_error_handler()for reliable error capture - Interactive Debug View - VS Code-inspired dark theme with clickable stack frames and code snippets
- Environment Awareness - Automatically detects
APP_DEBUGto show appropriate views - Status Code Detection - Distinguishes between 404 (NotFoundError) and 500 (other exceptions)
- Graceful Fallback - Provides plain HTML if Blade rendering fails
- Fatal Error Handling - Catches fatal errors through shutdown functions
Configuration
Environment Setup
Control error display mode through your .env file:
# Development: Show detailed debug traces
APP_DEBUG=true
# Production: Show user-friendly error pages
APP_DEBUG=false
Service Provider Registration
Register the ErrorHandlerServiceProvider in your bootstrap/app.php.
Important: It must be registered AFTER ViewServiceProvider because it depends on ViewManager.
$app->withServiceProviders([
ConfigServiceProvider::class,
DatabaseServiceProvider::class,
HttpServiceProvider::class,
RouteServiceProvider::class,
ViewServiceProvider::class, // ViewManager must be registered first
WebServiceProvider::class,
ErrorHandlerServiceProvider::class, // Must be LAST
]);
Error Views
Create three Blade views in resources/views/blade/errors/:
404 Error Page (errors.404)
Rendered when NotFoundError exception is thrown:
<!DOCTYPE html>
<html lang="en">
<head>
<title>404 - Page Not Found</title>
<style>
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
/* Beautiful purple gradient with animated stars */
}
</style>
</head>
<body>
<h1>404 - Page Not Found</h1>
<p>The page you're looking for doesn't exist.</p>
<a href="/">Go Home</a>
</body>
</html>
500 Error Page (errors.500)
Rendered for all other exceptions in production mode:
<!DOCTYPE html>
<html lang="en">
<head>
<title>500 - Internal Server Error</title>
<style>
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
/* Matching blue gradient theme */
}
</style>
</head>
<body>
<h1>500 - Internal Server Error</h1>
<p>Something went wrong on our end.</p>
<a href="/">Go Home</a>
</body>
</html>
Debug View (errors.debug)
Rendered in debug mode with full exception details and interactive backtrace:
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{ $exception['class'] }} | Debug</title>
<!-- VS Code dark theme styling -->
</head>
<body>
<!-- Exception details -->
<div class="header">
<div class="exception-class">{{ $exception['class'] }}</div>
<div class="exception-message">{{ $exception['message'] }}</div>
<div class="exception-location">
{{ $exception['file'] }}:{{ $exception['line'] }}
</div>
</div>
<!-- Sidebar with clickable stack frames -->
<div class="sidebar">
@foreach($backtrace as $index => $frame)
<div class="frame" data-frame="{{ $index }}">
{{ $frame['class'] }}::{{ $frame['function'] }}()
<br>{{ $frame['file'] }}:{{ $frame['line'] }}
</div>
@endforeach
</div>
<!-- Code snippets with line numbers -->
<div class="content">
@foreach($backtrace as $index => $frame)
<div id="frame-{{ $index }}">
@foreach($frame['snippet']['lines'] as $lineNum => $lineContent)
<div class="code-line {{ $lineNum == $frame['snippet']['errorLine'] ? 'error-line' : '' }}">
<span class="line-number">{{ $lineNum }}</span>
<span class="line-content">{{ $lineContent }}</span>
</div>
@endforeach
</div>
@endforeach
</div>
<!-- JavaScript for frame navigation -->
<script>
document.querySelectorAll('.frame').forEach(frame => {
frame.addEventListener('click', function() {
// Switch to selected frame's code
});
});
</script>
</body>
</html>
Usage Examples
Throwing 404 Errors
Throw NotFoundError to trigger a 404 response:
use Larafony\Framework\Core\Exceptions\NotFoundError;
public function findForRoute(string|int $value): static
{
$result = self::query()->where('id', '=', $value)->first();
if ($result === null) {
throw new NotFoundError(
sprintf('Model %s with id %s not found', static::class, $value)
);
}
return $result;
}
Custom Exception Handling
Create custom exceptions extending NotFoundError:
namespace App\Exceptions;
use Larafony\Framework\Core\Exceptions\NotFoundError;
class ResourceNotFoundException extends NotFoundError
{
public function __construct(string $resource, string|int $id)
{
parent::__construct(
sprintf('Resource "%s" with ID "%s" was not found', $resource, $id)
);
}
}
// Usage
throw new ResourceNotFoundException('User', 42);
Programmatic Backtrace Generation
Generate backtraces programmatically for logging or debugging:
use Larafony\Framework\ErrorHandler\Backtrace;
$backtrace = new Backtrace();
try {
// Risky operation
throw new \RuntimeException('Something went wrong');
} catch (\Throwable $e) {
$trace = $backtrace->generate($e);
foreach ($trace->frames as $frame) {
echo $frame->file . ':' . $frame->line . PHP_EOL;
echo $frame->class . '::' . $frame->function . '()' . PHP_EOL;
// Access code snippet
foreach ($frame->snippet->lines as $lineNum => $lineContent) {
echo $lineNum . ': ' . $lineContent . PHP_EOL;
}
}
}
Architecture
Core Components
DetailedErrorHandler- Main error handler implementing ErrorHandler contractBacktrace- Factory for generating TraceCollection from exceptionsTraceCollection- Immutable collection of TraceFrame objectsTraceFrame- Readonly value object representing a stack frameCodeSnippet- Extracts code context around error linesErrorHandlerServiceProvider- Integrates error handler into service container
Modern PHP Features
The error handling system leverages PHP 8.5 features:
// Property hooks for immutable public access
class TraceCollection
{
public private(set) array $frames; // Public read, private write
}
// Readonly value objects
readonly class TraceFrame
{
public function __construct(
public string $file,
public int $line,
public ?string $class,
public string $function,
public array $args,
public CodeSnippet $snippet
) {}
}
Comparison with Other Frameworks
| Feature | Larafony | Laravel | Symfony |
|---|---|---|---|
| Registration | set_exception_handler() |
withExceptions() in bootstrap |
Event listener on kernel.exception |
| Debug Views | Custom Blade templates | Whoops library | Symfony Profiler |
| Configuration | ENV (APP_DEBUG) |
Config files + ENV | YAML/PHP config |
| Approach | Native PHP handlers + Blade | Laravel exceptions + middleware | Event-driven architecture |
Learn More
This implementation is explained in detail with step-by-step tutorials, tests, and best practices at masterphp.eu
View on Packagist View on GitHub