Catch Unhandled Exceptions Globally in PHP
This article explains how to implement global exception handling in
PHP using the built-in set_exception_handler() function.
You will learn how to register a custom callback to catch any uncaught
exceptions, log error details securely, and display a user-friendly
error page instead of exposing sensitive system stack traces.
What is set_exception_handler()?
In PHP, any exception thrown outside of a try-catch
block is considered an “uncaught exception.” By default, this results in
a fatal error that halts script execution and can display sensitive path
information to your users.
The set_exception_handler() function allows you to
define a custom global function to handle these uncaught exceptions.
Once registered, whenever an exception escapes a try-catch
block, PHP bypasses the default error display and passes the exception
object directly to your custom handler.
Implementing a Global Exception Handler
To set up a global exception handler, you pass a callable (such as a
function name or an anonymous function) to
set_exception_handler().
Since PHP 7, both engine errors and exceptions implement the
Throwable interface. It is best practice to type-hint the
handler’s parameter as Throwable to ensure it catches both
standard exceptions and modern PHP engine errors.
Here is a basic implementation using an anonymous function:
<?php
// Register the global exception handler
set_exception_handler(function (Throwable $exception) {
// 1. Log the error internally for debugging
error_log(sprintf(
"Uncaught Exception: %s in %s on line %d",
$exception->getMessage(),
$exception->getFile(),
$exception->getLine()
));
// 2. Clear any output buffers (optional)
if (ob_get_length()) {
ob_clean();
}
// 3. Set HTTP status code to 500 (Internal Server Error)
http_response_code(500);
// 4. Display a clean, user-friendly message
echo "<h1>Something went wrong</h1>";
echo "<p>We are experiencing technical difficulties. Please try again later.</p>";
// 5. Terminate the script
exit(1);
});
// Triggering an unhandled exception to test the handler
throw new Exception("Database connection failed!");Key Considerations
1. Production vs. Development Environments
In a development environment, you want to see detailed stack traces. In production, you must hide these details to prevent security leaks. Use environment variables to toggle the verbosity of your global handler:
set_exception_handler(function (Throwable $exception) {
error_log($exception); // Always log the full error privately
if (getenv('APP_ENV') === 'development') {
// Show detailed diagnostics to developers
echo "<h2>Debug Info:</h2>";
echo "<pre>" . print_r($exception, true) . "</pre>";
} else {
// Show generic message to public users
echo "A system error occurred. Our team has been notified.";
}
exit(1);
});2. Execution Flow Termination
Once the global exception handler finishes executing, the PHP script
terminates automatically. You do not need to call exit() or
die() unless you want to return a specific exit status code
(e.g., exit(1) to signal an error to command-line
runners).
3. Restoring the Previous Handler
If you need to temporarily override the global handler and then
revert to your previous configuration, PHP provides the
restore_exception_handler() function:
set_exception_handler('firstHandler');
set_exception_handler('secondHandler');
// This reverts the handler back to 'firstHandler'
restore_exception_handler();