symfony/error-handler
Symfony ErrorHandler provides robust error and exception handling tools for PHP. Enable debug mode, register an error handler, and use DebugClassLoader for better stack traces. Convert PHP notices/warnings into exceptions and wrap risky code with ErrorHandler::call for safer debugging.
Install the package (via Composer):
composer require symfony/error-handler
Enable debugging in development (e.g., AppServiceProvider boot method):
if (app()->environment('local', 'staging')) {
\Symfony\Component\ErrorHandler\Debug::enable();
}
First use case: Wrap risky operations (e.g., file I/O, external APIs):
use Symfony\Component\ErrorHandler\ErrorHandler;
$data = ErrorHandler::call(function () {
return json_decode(file_get_contents('config.json'), true);
});
Customize production errors (e.g., app/Exceptions/Handler.php):
use Symfony\Component\ErrorHandler\HtmlErrorRenderer;
HtmlErrorRenderer::setTemplate(__DIR__.'/../resources/views/errors/production.blade.php');
HttpClient or queue jobs in ErrorHandler::call() to force exceptions:
$response = ErrorHandler::call(function () use ($client) {
return $client->get('https://api.example.com/data');
});
AppServiceProvider:
\Symfony\Component\ErrorHandler\ErrorHandler::register();
\Symfony\Component\ErrorHandler\Debug::enable();
\Symfony\Component\ErrorHandler\DebugClassLoader::enable();
if (app()->environment('staging')) {
\Symfony\Component\ErrorHandler\ErrorHandler::register(
new \Symfony\Component\ErrorHandler\ImpersonatingLogger(
new \Sentry\Laravel\Integration\SentryLogger()
)
);
}
HtmlErrorRenderer to mask sensitive data:
HtmlErrorRenderer::setTemplate(function (Throwable $exception) {
$trace = str_replace(['password=xxx', 'api_key=yyy'], '[REDACTED]', $exception->getTraceAsString());
return view('errors.production', ['trace' => $trace]);
});
render() in App\Exceptions\Handler:
public function render($request, Throwable $exception)
{
if ($this->shouldReturnJson($request, $exception)) {
return parent::render($request, $exception);
}
return ErrorHandler::renderHtmlResponse($exception);
}
use Symfony\Component\ErrorHandler\ErrorHandler;
public function testCriticalPath()
{
$result = ErrorHandler::call(function () {
// Risky operation
return $this->service->processPayment();
});
$this->assertNotNull($result);
}
@-silenced errors:
- name: Check for silenced errors
run: |
grep -r "@" --include="*.php" app/ || true
if [ $? -eq 0 ]; then
echo "::error::Silenced errors found. Use ErrorHandler::call() instead."
exit 1
fi
\Symfony\Component\ErrorHandler\ErrorHandler::register(
new class implements \Symfony\Component\ErrorHandler\ErrorHandlerInterface {
public function handle(Throwable $exception, bool $notify = true)
{
if ($exception instanceof \DeprecatedError) {
\Log::warning('Deprecation: ' . $exception->getMessage());
}
return false; // Let Laravel handle it
}
}
);
Double Error Handling:
ErrorHandler::call() with Laravel’s try-catch. Use one or the other per operation.ErrorHandler::call() for low-level operations (e.g., file I/O) and try-catch for business logic.Debug Mode in Production:
Debug::enable() exposes full stack traces and sensitive paths. Never enable in production.if (!app()->environment('production')) {
Debug::enable();
}
Performance Overhead:
ErrorHandler::call() adds microsecond latency per call. Avoid wrapping high-frequency operations (e.g., loop iterations).Template Caching:
HtmlErrorRenderer templates must be cached in production. Use Laravel’s blade cache:
php artisan view:cache
Silenced Errors:
@ operator bypasses ErrorHandler::call(). Use ErrorHandler::call() instead:
// Bad: Silenced error
$data = @json_decode(file_get_contents('file.json'));
// Good: Forced exception
$data = ErrorHandler::call(function () {
return json_decode(file_get_contents('file.json'));
});
Stack Trace Analysis:
Debug::enable() to color-code stack traces and click file links in local/staging.Autoloading Issues:
DebugClassLoader::enable() to diagnose missing classes during development.Custom Error Logging:
ErrorHandlerInterface to log errors to external services (e.g., Datadog, Humio):
ErrorHandler::register(new class implements ErrorHandlerInterface {
public function handle(Throwable $exception, bool $notify)
{
\Log::critical('Custom error: ' . $exception->getMessage());
return false; // Let Laravel handle it
}
});
Deprecation Warnings:
php.ini to avoid noise:
error_reporting = E_ALL & ~E_DEPRECATED
ErrorHandler::call().Custom Error Renderers:
ErrorRendererInterface for API responses or custom formats:
use Symfony\Component\ErrorHandler\ErrorRendererInterface;
class ApiErrorRenderer implements ErrorRendererInterface
{
public function render(Throwable $exception): string
{
return json_encode([
'error' => 'Server Error',
'code' => $exception->getCode(),
]);
}
}
Error Subscribers:
ErrorHandler::register() to modify exceptions before rendering:
ErrorHandler::register(new class implements ErrorHandlerInterface {
public function handle(Throwable $exception, bool $notify)
{
if ($exception instanceof \TypeError) {
$exception = new \RuntimeException('Type error: ' . $exception->getMessage());
}
return false;
}
});
Laravel Service Provider:
ErrorHandler to Laravel’s container for dependency injection:
$this->app->singleton(\Symfony\Component\ErrorHandler\ErrorHandlerInterface::class, function () {
return new class implements ErrorHandlerInterface {
// Custom logic
};
});
Queue Job Wrappers:
ErrorHandler::call():
abstract class ErrorHandlingJob implements ShouldQueue
{
public function handle()
{
return ErrorHandler::call([$this, 'execute']);
}
abstract protected function execute();
}
How can I help you explore Laravel packages today?