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:
- Throwable (Interface)
- Exception (Class implementing Throwable) - Used for standard user-land exceptions.
- Error (Class implementing Throwable) - Used for internal PHP engine errors.
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
- Direct Implementation Restriction: PHP prevents
user-defined classes from implementing
Throwabledirectly. Your custom exception classes must extend theExceptionclass or one of its subclasses instead. - Engine Errors: The
Errorclass represents internal PHP engine errors. You should rarely throw anErrorclass manually; instead, throw custom classes that extendException. - Global Handling: Catching
Throwableis ideal at the entry point of your application (like a front controller or a routing system) to prevent exposing sensitive stack traces to users.