Understanding PHP Exceptions
In PHP programming, exceptions are used to handle unexpected errors and runtime problems. The purpose of using exceptions is to handle these problems in an
An exception is an object that represents an error or an unexpected condition that interrupts the normal flow of your program. Instead of returning an error code that the caller might forget to check, code "throws" an exception; the runtime then unwinds the call stack until it finds a matching catch block. If no block catches it, the script halts with a fatal error.
This chapter covers how to throw and catch exceptions, the methods every exception exposes (getMessage(), getCode(), getLine(), getFile()), the finally block, multiple catch blocks, custom exception classes, and the difference between Exception and Error.
What is a PHP Exception?
A PHP exception is an object that descends from the built-in Exception class (or, more broadly, from the Throwable interface). When something goes wrong — a missing file, an invalid argument, a failed database connection — you create one of these objects and throw it. Throwing immediately stops the current execution path and hands control to the nearest enclosing handler.
Use an exception when a function cannot meaningfully continue and the caller is the right place to decide what to do next. Don't use them for ordinary control flow (a normal "user not found" lookup is better expressed with a return value).
Throwing an Exception
The throw keyword raises an exception, followed by a new instance of an exception class. The constructor accepts an optional message, an integer code, and a previous exception (for chaining):
<?php
function divide(int $a, int $b): float
{
if ($b === 0) {
throw new InvalidArgumentException('Division by zero is not allowed.');
}
return $a / $b;
}
try {
echo divide(10, 0);
} catch (InvalidArgumentException $e) {
echo 'Error: ' . $e->getMessage();
}
?>Output:
Error: Division by zero is not allowed.The code inside try runs normally until throw fires. From that point the rest of the try block is skipped and the matching catch block runs.
Handling Exceptions with try / catch
You wrap code that might fail in a try block and recover from the failure in a catch block. The variable in catch (here $e) holds the exception object, which exposes several read-only methods:
| Method | Returns |
|---|---|
getMessage() | The human-readable message |
getCode() | The integer code passed to the constructor |
getLine() | The line where the exception was thrown |
getFile() | The file where it was thrown |
getPrevious() | The chained "previous" exception, if any |
getTraceAsString() | The stack trace as a string |
<?php
try {
throw new Exception('Something failed', 42);
} catch (Exception $e) {
echo 'Message: ' . $e->getMessage() . PHP_EOL;
echo 'Code: ' . $e->getCode() . PHP_EOL;
}
?>Output:
Message: Something failed
Code: 42Catching Multiple Exception Types
A single try block can have several catch blocks. PHP checks them top to bottom and runs the first one whose type matches. Since PHP 7.1 you can also catch several unrelated types in one block with the pipe (|) operator:
<?php
try {
throw new RuntimeException('Network is down');
} catch (InvalidArgumentException $e) {
echo 'Bad argument: ' . $e->getMessage();
} catch (RuntimeException | LogicException $e) {
echo 'Runtime/logic problem: ' . $e->getMessage();
}
?>Output:
Runtime/logic problem: Network is downOrder matters: list more specific exception types before their parent classes, otherwise the broad catch swallows everything first.
The finally Block
The finally block is optional but useful. Its code always runs — whether or not an exception was thrown, and even if the try or catch block executes a return. This makes it the right place for cleanup such as closing a file handle or releasing a lock:
<?php
try {
echo 'Open resource' . PHP_EOL;
throw new Exception('Boom');
} catch (Exception $e) {
echo 'Caught: ' . $e->getMessage() . PHP_EOL;
} finally {
echo 'Cleanup always runs' . PHP_EOL;
}
?>Output:
Open resource
Caught: Boom
Cleanup always runsCustom Exception Classes
Beyond the built-in types you can define your own exception classes by extending Exception. A custom class lets you carry extra data (such as a failed value) and lets callers catch your specific error type without accidentally catching unrelated ones:
<?php
class InsufficientFundsException extends Exception
{
private float $shortfall;
public function __construct(float $shortfall)
{
$this->shortfall = $shortfall;
parent::__construct("Short by $shortfall");
}
public function getShortfall(): float
{
return $this->shortfall;
}
}
try {
throw new InsufficientFundsException(25.5);
} catch (InsufficientFundsException $e) {
echo $e->getMessage() . PHP_EOL;
echo 'Need ' . $e->getShortfall() . ' more.';
}
?>Output:
Short by 25.5
Need 25.5 more.PHP also ships a family of ready-made SPL exceptions — InvalidArgumentException, RuntimeException, LengthException, and others — so you often don't need to invent your own.
Exception vs. Error
Since PHP 7, internal engine failures (such as a type error or calling an undefined method) are thrown as Error objects, not Exception objects. Both implement the Throwable interface. A plain catch (Exception $e) will not catch an Error. To handle both, catch the interface:
<?php
try {
$result = 10 % 0; // throws a DivisionByZeroError
} catch (Throwable $e) {
echo get_class($e) . ': ' . $e->getMessage();
}
?>Output:
DivisionByZeroError: Modulo by zeroAs a rule, reserve Exception for problems your application can recover from, and let Error represent bugs you should fix rather than catch.
Related Topics
- PHP Errors — the difference between errors and exceptions.
- PHP
tryStatement — a focused look at thetryconstruct. set_exception_handler()— a global fallback for uncaught exceptions.- PHP Classes and Objects — needed to build custom exception classes.
- PHP Functions — where exceptions are most often thrown.
Conclusion
Exceptions give PHP a structured way to deal with failures: a function throws when it can't continue, and the caller catches to recover, log, or rethrow. Combine try, catch, and finally to separate the happy path from error handling and cleanup, use custom classes to model your domain's failures, and remember that engine-level problems arrive as Error (catchable via Throwable). Practicing these patterns in your own projects will make your code far more robust.