Error Handling
Throwing and catching exceptions, custom exception types, and the difference between errors and exceptions.
2 min read
PHP represents most runtime problems as exceptions — objects that can be thrown, caught, and handled gracefully rather than crashing the whole script.
try, catch, and throw
<?php
function divide(float $a, float $b): float {
if ($b === 0.0) {
throw new InvalidArgumentException("Cannot divide by zero.");
}
return $a / $b;
}
try {
echo divide(10, 0);
} catch (InvalidArgumentException $e) {
echo "Error: " . $e->getMessage();
}throw raises an exception, immediately halting normal execution and searching outward for a matching catch block. getMessage() retrieves the descriptive text passed when the exception was constructed. If no catch block matches, the exception propagates all the way up and, left unhandled, crashes the script with a fatal error.
Catching multiple exception types
<?php
try {
processPayment();
} catch (InsufficientFundsException | PaymentGatewayException $e) {
echo "Payment failed: " . $e->getMessage();
} catch (Exception $e) {
echo "Unexpected error: " . $e->getMessage();
}A single catch can list several exception types separated by |, handling them identically. Multiple catch blocks are checked in order, so list more specific exception types before general ones — a catch-all catch (Exception $e) placed first would swallow everything below it, since every built-in exception ultimately extends Exception.
finally: always runs
<?php
function readFile(string $path): string {
$handle = fopen($path, "r");
try {
return fread($handle, filesize($path));
} finally {
fclose($handle);
}
}Code inside finally runs whether the try block succeeds, throws, or even returns early — making it the reliable place for cleanup (closing a file handle, releasing a lock) that must happen no matter what.
Custom exception classes
<?php
class InsufficientFundsException extends Exception {
public function __construct(private float $shortfall) {
parent::__construct("Insufficient funds: short by $" . number_format($shortfall, 2));
}
public function getShortfall(): float {
return $this->shortfall;
}
}
throw new InsufficientFundsException(25.50);Extending PHP's built-in Exception class lets you attach domain-specific data ($shortfall here) and a tailored message, while still being catchable as a normal Exception by any code that doesn't need the specifics. This is the standard pattern for representing "things that can go wrong" in a real application's business logic.
Errors vs. exceptions
<?php
try {
strlen(); // ArgumentCountError — a subclass of Error, not Exception
} catch (\Throwable $e) {
echo get_class($e) . ": " . $e->getMessage();
}PHP distinguishes Exception (recoverable problems your code raises deliberately) from Error (serious issues like calling an undefined function or a type mismatch, which PHP itself raises). Both implement the Throwable interface, so catch (\Throwable $e) catches either — useful for a top-level handler that must never let anything slip through uncaught, though everyday application code usually catches specific Exception subclasses instead.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.