Catch PHP Errors and Exceptions Using Throwable

In modern PHP, handling runtime issues efficiently requires a solid understanding of the Throwable interface. This article explains how PHP uses Throwable to unify exception and error handling, allowing developers to catch both traditional exceptions and previously uncaught fatal errors in a single try-catch block. You will learn the hierarchy of these classes, view practical code examples, and discover best practices for implementing robust error handling in your PHP applications.

Understanding the Throwable Interface

Introduced in PHP 7, Throwable is the base interface for any object that can be thrown via the throw statement. Before its introduction, fatal errors (such as TypeError, ParseError, or DivisionByZeroError) would immediately halt script execution, making them impossible to catch using standard try-catch blocks.

By introducing Throwable, PHP reorganized its exception and error hierarchy:

Because both Exception and Error implement Throwable, you can catch both types of issues simultaneously.

How to Catch Both Errors and Exceptions

To catch both fatal errors and standard exceptions, type-hint the Throwable interface in your catch block.

Here is a practical code example:

<?php

try {
    // This triggers a TypeError (which extends Error) in PHP 8+
    $result = strlen([1, 2, 3]); 
} catch (Throwable $t) {
    // This block catches both Errors and Exceptions
    echo "Caught a throwable: " . $t->getMessage() . "\n";
    echo "Type: " . get_class($t);
}

In this example, passing an array to strlen() triggers a TypeError. Instead of crashing the script, the Throwable block intercepts the error, allowing you to log it or degrade gracefully.

Catching Exceptions and Errors Separately

While catching Throwable is useful for global exception handlers or logging middleware, it is often better practice to handle specific errors and exceptions individually. This allows for more precise recovery logic.

<?php

try {
    // Code that might fail
    $value = 10 / 0; // Triggers DivisionByZeroError
} catch (DivisionByZeroError $e) {
    // Handle specific division by zero error
    echo "Cannot divide by zero.";
} catch (Exception $e) {
    // Handle standard exceptions
    echo "An exception occurred: " . $e->getMessage();
} catch (Throwable $t) {
    // Fallback for any other unexpected Error or Exception
    echo "An unexpected error occurred: " . $t->getMessage();
}

Key Rules to Keep in Mind

  1. Direct Implementation Restriction: PHP prevents user-defined classes from implementing Throwable directly. Your custom exception classes must extend the Exception class or one of its subclasses instead.
  2. Engine Errors: The Error class represents internal PHP engine errors. You should rarely throw an Error class manually; instead, throw custom classes that extend Exception.
  3. Global Handling: Catching Throwable is ideal at the entry point of your application (like a front controller or a routing system) to prevent exposing sensitive stack traces to users.