symfony/error-handler
Symfony ErrorHandler provides robust PHP error and exception handling with better debugging tools. Enable debug mode, convert warnings/notices into exceptions, and safely wrap code execution with ErrorHandler::call, even bypassing the @ silencer.
Install the Package
composer require symfony/error-handler
Enable Debug Mode in Development
Add to bootstrap/app.php (Laravel 10+) or app/Providers/AppServiceProvider.php:
use Symfony\Component\ErrorHandler\Debug;
if (app()->environment('local')) {
Debug::enable();
}
First Use Case: Wrap Risky Operations
Replace ad-hoc try-catch blocks with ErrorHandler::call():
use Symfony\Component\ErrorHandler\ErrorHandler;
$data = ErrorHandler::call(function () {
return json_decode(file_get_contents('config.json'), true);
});
Customize Production Errors (Optional)
Override Laravel’s default error page by creating a template at resources/views/errors/custom.blade.php and configuring the renderer:
use Symfony\Component\ErrorHandler\HtmlErrorRenderer;
HtmlErrorRenderer::setTemplate(__DIR__.'/../../resources/views/errors/custom.blade.php');
Local Development:
Enable Debug::enable() to get interactive stack traces with file links and variable inspection (similar to Laravel’s dd() but for errors).
Debug::enable(); // Auto-detects dev environment via $_SERVER['APP_DEBUG']
Staging/Production Debugging:
Use ErrorHandler::register() to log errors to a file or external service (e.g., Sentry) without exposing details to users:
ErrorHandler::register(
new class implements ErrorHandlerInterface {
public function handle(Exception|Error $error, bool $display): void {
Log::error('Unhandled error', ['exception' => $error]);
if (!$display) {
// Custom fallback UI or redirect
}
}
}
);
Replace repetitive try-catch with ErrorHandler::call():
use Symfony\Component\ErrorHandler\ErrorHandler;
public function importData(Request $request)
{
return ErrorHandler::call(function () use ($request) {
$data = $request->file('csv')->get();
// Process data...
return response()->json(['status' => 'success']);
});
}
Wrap CLI commands to fail fast on errors:
use Symfony\Component\ErrorHandler\ErrorHandler;
$results = ErrorHandler::call(function () {
Artisan::call('migrate');
Artisan::call('queue:work');
return true;
});
if (!$results) {
exit(1); // Fail the script
}
Extend Laravel’s error handling by combining Symfony’s renderer with Laravel’s views:
// In AppServiceProvider::boot()
HtmlErrorRenderer::setTemplate(
app_path('Http/Controllers/ErrorController.php')
->getRealPath().'/../../resources/views/errors/symfony.blade.php'
);
Silence or log deprecation notices selectively:
// Log deprecations to a file
ErrorHandler::register(
new class implements ErrorHandlerInterface {
public function handle(Exception|Error $error, bool $display): void {
if ($error instanceof DeprecatedClassError) {
Log::warning('Deprecation', ['error' => $error]);
}
}
}
);
Debug Mode in Production:
Debug::enable() in production. Use environment checks:
if (app()->environment('local')) {
Debug::enable();
}
ErrorHandler::call() and Exceptions:
@ operator). Use ErrorHandler::call() for operations where you expect errors but want to handle them gracefully.Custom Templates and Caching:
HtmlErrorRenderer::setTemplate(), ensure the template is cacheable (e.g., compiled Blade views) to avoid performance hits during error rendering.Deprecation Notices:
ErrorHandler does not suppress deprecation notices (E_DEPRECATED). To silence them:
ErrorHandler::register(
new class implements ErrorHandlerInterface {
public function handle(Exception|Error $error, bool $display): void {
if ($error instanceof DeprecatedClassError) {
return; // Suppress deprecation notices
}
}
}
);
File Links in Stack Traces:
Debug::enable() is called before the error occurs. For dynamic debugging (e.g., in middleware), use:
Debug::enable(); // Must be called early (e.g., in AppServiceProvider)
Inspect Errors in Real-Time:
Use Debug::enable() with Xdebug for interactive debugging of errors:
Debug::enable();
// Trigger an error to see the interactive trace
throw new RuntimeException('Test error');
Log Errors to Laravel’s Log System:
Combine Symfony’s ErrorHandler with Laravel’s logging:
ErrorHandler::register(
new class implements ErrorHandlerInterface {
public function handle(Exception|Error $error, bool $display): void {
Log::error('Symfony Error', ['exception' => $error]);
}
}
);
Custom Error Context: Attach additional context to errors (e.g., user ID, request data):
$error = new RuntimeException('Failed to process');
$error->setContext(['user_id' => auth()->id(), 'request' => $request->all()]);
throw $error;
Avoid Double Error Handling:
Laravel already has a global error handler (App\Exceptions\Handler). Use Symfony’s ErrorHandler for low-level operations (e.g., CLI scripts, file I/O) and let Laravel handle HTTP errors.
Create a Custom ErrorRenderer:
Extend HtmlErrorRenderer to add Laravel-specific features (e.g., user notifications):
use Symfony\Component\ErrorHandler\HtmlErrorRenderer;
class LaravelErrorRenderer extends HtmlErrorRenderer {
protected function renderException(Exception $exception): string {
// Add Laravel-specific logic (e.g., notify admin)
return parent::renderException($exception);
}
}
Integrate with Laravel’s Exception Handler:
Override Laravel’s render() method to use Symfony’s renderer:
// In App\Exceptions\Handler
public function render($request, Throwable $exception) {
if ($exception instanceof \Symfony\Component\ErrorHandler\Error\Error) {
return SymfonyErrorRenderer::render($exception);
}
return parent::render($request, $exception);
}
Add Error Metadata:
Use Symfony’s Error class to attach custom metadata:
$error = new \Symfony\Component\ErrorHandler\Error\Error(
E_ERROR,
'Custom error message',
['file' => 'custom.php', 'line' => 100]
);
throw $error;
CLI Error Handling:
For Artisan commands, combine Symfony’s ErrorHandler with Laravel’s HandleExceptions:
use Symfony\Component\ErrorHandler\ErrorHandler;
use Illuminate\Foundation\Exceptions\Handler;
public function handle(): void {
ErrorHandler::call(function () {
// Command logic
});
}
How can I help you explore Laravel packages today?