Error and Exception Handling
PHP's try/catch/finally, throwing exceptions, and how PHP's error model has evolved.
What you'll learn
- Throw an exception with a descriptive message and catch it with `try`/`catch`
- Explain what `finally` guarantees, regardless of whether an exception was thrown
- Predict the output of a script combining a thrown exception with a `finally` block
Explanation
PHP handles error conditions with exceptions: throw new InvalidArgumentException("Cannot divide by zero"); raises an exception carrying a message, and try { ... } catch (InvalidArgumentException $e) { ... } catches it -- $e->getMessage() retrieves the message you threw it with. If an exception is thrown but nothing catches it, PHP halts the script with a fatal error.
An optional finally block runs every time, whether or not an exception was thrown or caught -- it's the right place for cleanup code that must happen regardless of outcome (closing a file handle, for example), and it still runs even if the exception was caught and handled successfully.
PHP's error model has genuinely modernized: many conditions that used to be silent warnings or notices in older PHP are now real, catchable exceptions in PHP 8 -- for example, dividing by zero with intdiv() or the % operator throws DivisionByZeroError, and calling a method on null throws an Error. This makes failure modes far more visible and testable than PHP's historically loose reputation suggests.
Exceptions and Errors both implement the Throwable interface, but they're conventionally treated differently: exceptions typically represent conditions your own code anticipates and can recover from (like this lesson's custom InvalidArgumentException), while Errors (like TypeError, DivisionByZeroError) more often indicate a programming mistake.
Guided lab
Predict: try/catch/finally with a custom exception
Read this script and predict exactly what it sends to the browser.
<?php
function safeDivide(int $a, int $b): int {
if ($b === 0) {
throw new InvalidArgumentException("Cannot divide by zero");
}
return intdiv($a, $b);
}
try {
echo safeDivide(10, 2) . "\n";
echo safeDivide(10, 0) . "\n";
} catch (InvalidArgumentException $e) {
echo "Error: " . $e->getMessage() . "\n";
} finally {
echo "Done.\n";
}Stuck? Get a hint.
Common mistakes
- Catching a broader or unrelated exception class than the one actually thrown, so the real exception goes uncaught and halts the script.
- Forgetting `finally` runs even when the exception was successfully caught and handled -- it's not just for the unhandled case.
- Assuming every PHP runtime problem still produces a silent warning like in much older PHP -- many now throw real, catchable exceptions/errors in PHP 8.
Knowledge check
Takeaway
Catch the specific exception type you expect, and use `finally` for cleanup that must run regardless of whether an exception occurred.
Summary
`throw` raises an exception carrying a message; `try`/`catch` handles it by type; `finally` always runs; PHP 8 converted many former silent warnings into real, catchable exceptions/errors.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.