W3docs

set_exception_handler()

How PHP's set_exception_handler() registers a global handler for uncaught exceptions, covering signature, return value, a runnable example, and best practices.

Introduction

set_exception_handler() registers a function that PHP calls whenever an uncaught exception reaches the top of the script. Normally an uncaught exception produces a fatal error and a default stack trace; a global handler lets you replace that with consistent logging, a friendly error page, or alerting — in one place, for the whole application. This page covers the function's signature, what it returns, a runnable example, the cases it does not cover, and the gotchas worth knowing.

If you need the basics of throwing and catching first, start with PHP Exceptions and the try/catch chapter.

When is the handler called?

A normal exception is caught by the nearest matching catch block. The global handler only runs when no catch block matches — the exception "escapes" all the way out of the script:

try {
    throw new RuntimeException('handled here');
} catch (RuntimeException $e) {
    // caught locally — the global handler never runs
}

throw new RuntimeException('nothing catches this'); // → global handler runs, then script ends

After the handler returns, execution does not resume — the script terminates. So the handler is your last chance to log and present the failure cleanly, not a way to recover and continue.

Signature and return value

set_exception_handler(?callable $callback): ?callable
  • $callback — a callable that accepts the thrown object as its single argument. Since PHP 7 every exception and error implements Throwable, so type-hint the parameter as Throwable (not just Exception) to also catch Error instances such as TypeError.
  • Returns the previously registered handler (or null if none was set), which you can keep to restore later. Passing null removes the handler.

Runnable example

The script below registers a handler, triggers it with an uncaught exception, and prints a formatted message. It writes to standard error via error_log() with no destination, so it is fully portable:

<?php

function appExceptionHandler(Throwable $e): void
{
    $message = sprintf(
        "Uncaught %s: %s in %s on line %d",
        get_class($e),
        $e->getMessage(),
        $e->getFile(),
        $e->getLine()
    );

    error_log($message);            // goes to the SAPI error log / stderr
    echo "Something went wrong.\n"; // user-facing message
}

set_exception_handler('appExceptionHandler');

throw new RuntimeException('Database is unreachable');

Output (the error_log line goes to stderr, the echo to stdout):

Something went wrong.

with a line like Uncaught RuntimeException: Database is unreachable in /path/to/script.php on line 19 in the error log.

What it does NOT handle

set_exception_handler() only intercepts uncaught exceptions. It does not catch:

  • Fatal errors, parse errors, or warnings — those go through set_error_handler() (for catchable errors) or register_shutdown_function() (for fatals).
  • Exceptions that are already caught by a local try/catch.

For non-exception errors such as warnings and notices, register a separate handler with set_error_handler().

Restoring the previous handler

restore_exception_handler() reverts to whatever handler was active before your last set_exception_handler() call. Use it when a custom handler should apply only to a specific block of code:

<?php

set_exception_handler(function (Throwable $e) {
    echo "Custom: {$e->getMessage()}\n";
});

// ... code that should use the custom handler ...

restore_exception_handler(); // back to the default behavior

Important: your handler must not throw a new exception. If it does, PHP cannot dispatch it again and raises a fatal error instead.

Best Practices for Using set_exception_handler

When using set_exception_handler, there are a few best practices you should follow to ensure that your application handles errors effectively:

  1. Type-hint the handler's parameter as Throwable so it catches both Exception and Error subtypes (PHP 7+).
  2. Make sure the handler never throws a new exception — that triggers an unrecoverable fatal error.
  3. Log enough context to diagnose the failure: exception class, message, file, line, and the stack trace from $e->getTraceAsString().
  4. Keep the user-facing message generic; never leak stack traces or messages to end users in production.
  5. Register the handler as early as possible (e.g. in a bootstrap file) so it covers the whole request.
  6. Pair it with set_error_handler() and register_shutdown_function() to cover warnings, notices, and fatal errors too.
  7. Use restore_exception_handler() to revert to the previous handler when a custom one should be scoped to a block of code.

Conclusion

set_exception_handler() gives you one place to handle every uncaught exception in an application — turning a raw fatal error into consistent logging and a clean user-facing message. Remember its limits: it runs only for uncaught exceptions, execution stops afterward, and the handler itself must never throw. Combine it with set_error_handler() for warnings, trigger-error for custom error signals, and restore_exception_handler() for scoped handling to build robust error reporting.

Practice

Practice
What does the set_exception_handler() function in PHP do?
What does the set_exception_handler() function in PHP do?
Was this page helpful?