LearnContact
Lesson 3920 min read

Exception Handling

Learn how to catch, throw, and handle errors gracefully in PHP using try, catch, finally, and custom exceptions.

Introduction

In the previous lesson, you saw PHP's traditional error system — notices, warnings, and fatal errors. Exceptions are PHP's more structured, modern way to handle problems: instead of an error simply appearing and possibly halting your script, you can catch it, inspect it, and decide exactly how your program should respond.

Exceptions are especially useful for expected, recoverable failure conditions — a division by zero, a failed file read, or invalid input — where you want your program to respond gracefully instead of crashing.

What You Will Learn
  • What an exception is and how it differs from a traditional error.
  • How to use try, catch, and finally blocks.
  • How to catch a specific exception type, like DivisionByZeroError.
  • How to throw your own exceptions with throw new Exception().
  • How to catch multiple exception types in a single catch block.
  • A first look at custom exception classes.

What Is an Exception?

An exception is an object that represents an error or unexpected condition. When something goes wrong, PHP code can "throw" an exception, which immediately stops normal execution and looks for a matching "catch" block to handle it. If no catch block is found anywhere, the script stops with a fatal error.

Targeted Handling

You can catch specific problems and respond to them individually, rather than letting the whole script crash.

Carries Information

An exception object carries a message, and sometimes an error code, describing exactly what went wrong.

Interrupts Flow

Throwing an exception immediately stops the current code path and jumps to the nearest matching catch block.

try, catch, and finally

The basic structure for handling exceptions is a try block, which contains code that might fail, followed by one or more catch blocks that handle specific problems, and an optional finally block that always runs regardless of what happened.

try-catch-basic.php
<?php
try {
    $result = 10 / 0;
} catch (DivisionByZeroError $e) {
    echo "Caught an error: " . $e->getMessage();
} finally {
    echo "\nThis always runs, error or not.";
}
Output
Caught an error: Division by zero
This always runs, error or not.

The finally block is useful for cleanup work — like closing a file or a database connection — that needs to happen whether or not an exception occurred.

finally-cleanup.php
<?php
function processFile() {
    echo "Opening file...\n";
    try {
        throw new Exception("Something went wrong while reading.");
    } catch (Exception $e) {
        echo "Error: " . $e->getMessage() . "\n";
    } finally {
        echo "Closing file (always happens).";
    }
}

processFile();
Output
Opening file...
Error: Something went wrong while reading.
Closing file (always happens).

Catching a Specific Exception Type

PHP has several built-in exception and error classes for common problems. Catching the specific type you expect, rather than a generic Exception, makes your intent clearer and prevents you from accidentally catching unrelated problems.

specific-catch.php
<?php
function divide(int $a, int $b): float {
    return $a / $b;
}

try {
    echo divide(10, 0);
} catch (DivisionByZeroError $e) {
    echo "Cannot divide by zero: " . $e->getMessage();
}
Output
Cannot divide by zero: Division by zero
ClassThrown When
DivisionByZeroErrorDividing a number by zero using / or intdiv().
TypeErrorA function argument or return value does not match its declared type.
ValueErrorA function receives an argument of the correct type but an invalid value.
ExceptionThe general-purpose base class for most application-level exceptions you throw yourself.

Throwing Your Own Exceptions

You are not limited to catching exceptions PHP throws automatically — you can throw your own using the throw keyword, anywhere your code detects a problem it cannot handle on the spot.

throw-example.php
<?php
function withdraw(float $balance, float $amount): float {
    if ($amount > $balance) {
        throw new Exception("Insufficient funds.");
    }
    return $balance - $amount;
}

try {
    echo withdraw(100, 250);
} catch (Exception $e) {
    echo "Transaction failed: " . $e->getMessage();
}
Output
Transaction failed: Insufficient funds.
Why Throw Your Own Exceptions

Throwing an exception lets a function signal "I cannot complete this, and here is why" without needing special return values like -1 or null that the caller might forget to check.

Catching Multiple Exception Types

Sometimes the same block of code might fail in more than one way, and you want to handle several exception types with identical logic. PHP lets you list multiple types in a single catch block using the pipe (|) syntax.

multi-catch.php
<?php
function riskyOperation(int $choice) {
    if ($choice === 1) {
        throw new TypeError("Wrong type provided.");
    }
    throw new ValueError("Invalid value provided.");
}

try {
    riskyOperation(1);
} catch (TypeError | ValueError $e) {
    echo "Caught: " . get_class($e) . " - " . $e->getMessage();
}
Output
Caught: TypeError - Wrong type provided.

This is useful when the recovery action is the same no matter which of the listed exceptions occurred, saving you from duplicating the same catch body multiple times.

Custom Exception Classes

Beyond throwing a plain Exception, PHP lets you define your own exception classes by extending the built-in Exception class. This lets you create meaningfully named exceptions, like InsufficientFundsException, that carry extra context specific to your application.

custom-exception-preview.php
<?php
class InsufficientFundsException extends Exception {
}

try {
    throw new InsufficientFundsException("Balance too low for this withdrawal.");
} catch (InsufficientFundsException $e) {
    echo "Custom exception caught: " . $e->getMessage();
}
Output
Custom exception caught: Balance too low for this withdrawal.
A Preview, Not the Full Picture

Defining InsufficientFundsException above uses the class keyword and extends — concepts covered properly starting with the upcoming Object-Oriented Programming lessons. For now, just know that custom exception classes are possible and commonly used in real applications.

Common Mistakes

Avoid These Mistakes
  • Catching the generic Exception class everywhere instead of the specific type you actually expect.
  • Forgetting that code after a throw statement inside the same block never executes.
  • Assuming finally only runs on success — it runs whether or not an exception was thrown.
  • Throwing exceptions for normal, expected control flow instead of genuine error conditions.

Best Practices

  • Catch the most specific exception type available, rather than a broad Exception, whenever you know what might go wrong.
  • Use finally for cleanup code that absolutely must run, like closing files or connections.
  • Include a clear, actionable message when throwing your own exceptions.
  • Reserve exceptions for genuinely exceptional or unrecoverable-without-intervention situations, not routine logic.
  • Use the multi-type catch (TypeA | TypeB $e) syntax when several exception types deserve identical handling.

Frequently Asked Questions

What is the difference between an Error and an Exception in PHP?

Both implement the Throwable interface and can be caught with try/catch. Error and its subclasses (like TypeError and DivisionByZeroError) typically represent internal PHP problems, while Exception and its subclasses are generally used for application-level error conditions you throw yourself.

Does finally run if there is a return statement inside try?

Yes. The finally block always runs before control actually leaves the function, even if try or catch contains a return statement.

Can I catch every possible exception with one generic catch block?

Yes, catching Throwable will catch both Error and Exception hierarchies, but it is usually better to catch specific types so you handle each problem appropriately.

What happens if an exception is thrown but never caught?

PHP produces a fatal error and the script stops, showing an "Uncaught Exception" message.

When should I create a custom exception class?

When you want to represent a specific, meaningful failure in your application (like InsufficientFundsException) so calling code can catch that exact scenario. This is covered further once classes are introduced.

Key Takeaways

  • Exceptions let you catch and respond to problems instead of letting the script crash outright.
  • try holds risky code, catch handles specific problems, and finally always runs afterward.
  • Catching a specific type, like DivisionByZeroError, is clearer and safer than catching a generic Exception.
  • You can throw your own exceptions with throw new Exception("message").
  • A single catch block can handle multiple exception types using the TypeA | TypeB syntax.
  • Custom exception classes extend Exception and will be covered fully once OOP concepts are introduced.

Summary

Exception handling gives PHP a structured, predictable way to deal with problems your code can anticipate. With try, catch, and finally, you can respond to specific failure conditions gracefully, throw your own meaningful exceptions, and even handle several exception types at once.

Next, you will step back and look at Object-Oriented Programming as a whole — the paradigm that custom exception classes, and much of modern PHP, are built on.

Lesson 39 Completed
  • You can write try/catch/finally blocks.
  • You can catch specific exception types like DivisionByZeroError.
  • You can throw your own exceptions with meaningful messages.
  • You can catch multiple exception types in one catch block.
Next Lesson →

Object-Oriented Programming (OOP)