PHP Exceptions
last modified October 22, 2026
In this article, we show how to use exceptions in PHP. Exceptions
are a mechanism for error handling in PHP, allowing developers to handle errors
gracefully and maintain control over the program flow. Exceptions are thrown
using the throw keyword and caught using the try-catch
block.
Main Features of PHP Exceptions
- Error Handling: Exceptions provide a structured way to handle errors and unexpected situations.
- Custom Exceptions: Developers can create custom exception classes to handle specific types of errors.
- Graceful Degradation: Exceptions allow programs to degrade gracefully by catching and handling errors without crashing.
- Debugging: Exceptions provide detailed error information, making debugging easier.
- Control Flow: Exceptions allow developers to control the flow of the program by catching and handling errors at specific points.
Exceptions are instances of the Exception class or its subclasses.
They are thrown using the throw keyword and caught using the
try-catch block.
Basic Usage of Exceptions
The following example demonstrates how to throw and catch an exception in PHP.
<?php
declare(strict_types=1);
function divide(int $a, int $b): int
{
if ($b === 0) {
throw new Exception("Division by zero is not allowed.");
}
return intdiv($a, $b);
}
try {
echo divide(10, 0);
} catch (Exception $e) {
echo "Caught exception: " . $e->getMessage();
}
In this program, the divide function throws an exception if the
second argument is zero. The exception is caught in the try-catch
block, and the error message is displayed.
$ php main.php Caught exception: Division by zero is not allowed.
Custom Exceptions
The following example demonstrates how to create and use a custom exception class in PHP.
<?php
declare(strict_types=1);
class DivisionByZeroException extends Exception
{
public function __construct()
{
parent::__construct("Division by zero is not allowed.");
}
}
function divide(int $a, int $b): int
{
if ($b === 0) {
throw new DivisionByZeroException();
}
return intdiv($a, $b);
}
try {
echo divide(10, 0);
} catch (DivisionByZeroException $e) {
echo "Caught custom exception: " . $e->getMessage();
}
In this program, the DivisionByZeroException class extends the
Exception class. The divide function throws this
custom exception if the second argument is zero. The exception is caught and
handled in the try-catch block.
$ php main.php Caught custom exception: Division by zero is not allowed.
Multiple Catch Blocks
The following example demonstrates how to use multiple catch blocks
to handle different types of exceptions.
<?php
declare(strict_types=1);
class DivisionByZeroException extends Exception {}
class NegativeArgumentException extends Exception {}
function divide(int $a, int $b): int
{
if ($b === 0) {
throw new DivisionByZeroException("Division by zero is not allowed.");
}
if ($a < 0 || $b < 0) {
throw new NegativeArgumentException("Arguments must be positive.");
}
return intdiv($a, $b);
}
try {
echo divide(-10, 5);
} catch (DivisionByZeroException $e) {
echo "Caught DivisionByZeroException: " . $e->getMessage();
} catch (NegativeArgumentException $e) {
echo "Caught NegativeArgumentException: " . $e->getMessage();
}
In this program, the divide function throws different exceptions
based on the input values. The exceptions are caught and handled in separate
catch blocks.
$ php main.php Caught NegativeArgumentException: Arguments must be positive.
Finally Block
The following example demonstrates how to use the finally block to
execute code regardless of whether an exception is thrown.
<?php
declare(strict_types=1);
function divide(int $a, int $b): int
{
if ($b === 0) {
throw new Exception("Division by zero is not allowed.");
}
return intdiv($a, $b);
}
try {
echo divide(10, 0);
} catch (Exception $e) {
echo "Caught exception: " . $e->getMessage();
} finally {
echo "\nFinally block executed.";
}
In this program, the finally block is executed after the
try-catch block, regardless of whether an exception is thrown.
$ php main.php Caught exception: Division by zero is not allowed. Finally block executed.
Rethrowing Exceptions
The following example demonstrates how to rethrow an exception after catching it.
<?php
declare(strict_types=1);
function divide(int $a, int $b): int
{
if ($b === 0) {
throw new Exception("Division by zero is not allowed.");
}
return intdiv($a, $b);
}
try {
try {
echo divide(10, 0);
} catch (Exception $e) {
echo "Caught exception: " . $e->getMessage();
throw $e; // Rethrow the exception
}
} catch (Exception $e) {
echo "\nRethrown exception: " . $e->getMessage();
}
In this program, the exception is caught, logged, and then rethrown to be
handled by an outer catch block.
$ php main.php Caught exception: Division by zero is not allowed. Rethrown exception: Division by zero is not allowed.
Exception Hierarchy
The following example demonstrates how to catch exceptions based on their hierarchy.
<?php
declare(strict_types=1);
class CustomException extends Exception {}
class AnotherException extends CustomException {}
function testException()
{
throw new AnotherException("Another exception occurred.");
}
try {
testException();
} catch (AnotherException $e) {
echo "Caught AnotherException: " . $e->getMessage();
} catch (CustomException $e) {
echo "Caught CustomException: " . $e->getMessage();
} catch (Exception $e) {
echo "Caught Exception: " . $e->getMessage();
}
In this program, the AnotherException is caught first because it
is more specific than CustomException and Exception.
$ php main.php Caught AnotherException: Another exception occurred.
Exceptions and Errors
Since PHP 7, most engine failures are reported as Error
objects instead of fatal errors. Both Exception and
Error implement the Throwable interface, so catching
Throwable handles both.
<?php
declare(strict_types=1);
function half(int $n): int
{
return intdiv($n, 2);
}
$tests = [
fn() => half(10),
fn() => half("10"),
fn() => intdiv(1, 0),
fn() => 1 % 0,
];
foreach ($tests as $test) {
try {
echo $test(), "\n";
} catch (TypeError $e) {
echo "TypeError: ", $e->getMessage(), "\n";
} catch (DivisionByZeroError $e) {
echo "DivisionByZeroError: ", $e->getMessage(), "\n";
}
}
The hierarchy is Throwable with the two branches
Exception and Error. Passing a string to an
int parameter under strict_types raises a
TypeError; integer division by zero (intdiv and the
modulo operator) raises a DivisionByZeroError. Both are
Error subclasses, so a catch (Exception $e) block would
not catch them.
$ php throwable.php 5 TypeError: half(): Argument #1 ($n) must be of type int, string given, called in /home/user/throwable.php on line 12 DivisionByZeroError: Division by zero DivisionByZeroError: Modulo by zero
Catching multiple types
A single catch block can handle several exception types separated
by the | character.
<?php
function parse(string $json): array
{
$data = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
if (!is_array($data)) {
throw new UnexpectedValueException("JSON must contain an object.");
}
return $data;
}
foreach (['{"a": 1}', '{bad', '12'] as $input) {
try {
print_r(parse($input));
} catch (JsonException | UnexpectedValueException $e) {
echo get_class($e), ": ", $e->getMessage(), "\n";
}
}
The parse function can throw a JsonException (thanks
to the JSON_THROW_ON_ERROR flag) or an
UnexpectedValueException. Both are handled in the same block. The
caught object's class is printed with get_class.
$ php multicatch.php
Array
(
[a] => 1
)
JsonException: Syntax error
UnexpectedValueException: JSON must contain an object.
Exception chaining
An exception can wrap the one that caused it. The previous exception is passed
as the third argument of the constructor and is available through
getPrevious.
<?php
class ConfigException extends RuntimeException {}
function loadConfig(string $path): array
{
try {
$content = file_get_contents($path)
?: throw new RuntimeException("Cannot read $path");
return json_decode($content, true, flags: JSON_THROW_ON_ERROR);
} catch (Throwable $e) {
throw new ConfigException("Configuration failed", 1, $e);
}
}
try {
loadConfig('/no/such/file.json');
} catch (ConfigException $e) {
echo $e->getMessage(), " (code ", $e->getCode(), ")\n";
echo "Caused by: ", $e->getPrevious()->getMessage(), "\n";
}
The low-level failure is wrapped in a domain-specific
ConfigException. The caller deals with one meaningful exception
type, while the original cause stays available for logging. Note that
throw is an expression since PHP 8, so it can be used on the right
side of the ?: operator. (The file_get_contents call
also emits a warning here, which is not shown.)
$ php chaining.php Configuration failed (code 1) Caused by: Cannot read /no/such/file.json
Global exception handler
The set_exception_handler function registers a callback for
exceptions that are not caught anywhere.
<?php
set_exception_handler(function (Throwable $e) {
echo "Unhandled ", get_class($e), ": ", $e->getMessage(), "\n";
echo "Thrown at line ", $e->getLine(), "\n";
});
echo "Start\n";
throw new LogicException("Something went wrong");
echo "Never reached\n";
The handler receives the uncaught exception and runs instead of PHP's default
fatal error message. After it returns, the script terminates, so the last
echo never executes. The getLine method returns the
line where the exception was created.
$ php handler.php Start Unhandled LogicException: Something went wrong Thrown at line 9
Source
PHP Exceptions - Documentation
In this article, we have shown how to use exceptions in PHP for
error handling. Exceptions are a powerful tool for managing errors and
maintaining control over the program flow.
Author
List all PHP tutorials.