← Back to PHP Course | Chapter 13: Error Handling | Lesson 3 of 5

PHP Custom Exceptions

A custom exception is your own named kind of problem, like a special alarm just for a broken oven. It makes it clearer what went wrong and lets you respond to it exactly.
Syntax
php
class CustomException extends Exception {
    // optional extra methods and properties
}

try {
    throw new CustomException("message");
} catch (CustomException $e) {
    echo $e->getMessage();
}

Creating a Custom Exception Class

Extending PHP's built-in Exception class lets you define your own exception types (like InvalidUserException), which makes it possible to catch and handle specific failure categories differently instead of treating every error the same way.

Example: Creating a Custom Exception Class

php
<?php
class InvalidUserException extends Exception {}

// Try running this block; jump to `catch` if it throws
try {
    // Throw a new `InvalidUserException` with message "User not found"
    throw new InvalidUserException("User not found");
// Catch InvalidUserException $e
} catch (InvalidUserException $e) {
    // Print `$e->getMessage()` to the output
    echo $e->getMessage();
}
?>

Custom Error Formatting

Because a custom exception class is just a class, you can add your own methods and properties to it -- for example a method that formats a user-friendly message from internal error details.

Example: Custom Error Formatting

php
<?php
// Define the class `AppException`, inheriting from `Exception`
class AppException extends Exception {
    // Define the function `friendlyMessage` with no parameters
    function friendlyMessage() {
        // Return `"Oops: " . $this->getMessage()` from this function
        return "Oops: " . $this->getMessage();
    }
}
// Try running this block; jump to `catch` if it throws
try {
    // Throw a new `AppException` with message "Database unreachable"
    throw new AppException("Database unreachable");
// Catch AppException $e
} catch (AppException $e) {
    // Print `$e->friendlyMessage()` to the output
    echo $e->friendlyMessage();
}
?>

Throwing Custom Exceptions

Throwing a custom exception instead of a generic one turns validation failures into self-documenting code: catching a StockUnavailableException communicates intent far better than a bare Exception ever could.

Example: Throwing Custom Exceptions

php
<?php
class StockUnavailableException extends Exception {}

// Define the function `order` taking `$qty`, `$stock`
function order($qty, $stock) {
    // Check whether `$qty > $stock`
    if ($qty > $stock) {
        // Throw a new `StockUnavailableException` with message "Not enough stock"
        throw new StockUnavailableException("Not enough stock");
    }
}
// Try running this block; jump to `catch` if it throws
try {
    // Call `order(5, 2)`
    order(5, 2);
// Catch StockUnavailableException $e
} catch (StockUnavailableException $e) {
    // Print `$e->getMessage()` to the output
    echo $e->getMessage();
}
?>

Multiple Custom Catches

Catching several custom exception types separately lets each one trigger its own recovery path -- retrying a network call differs completely from asking a user to fix invalid input.

Example: Multiple Custom Catches

php
<?php
class NetworkException extends Exception {}
class ValidationException extends Exception {}

// Define the function `process` taking `$type`
function process($type) {
    if ($type === "network") throw new NetworkException("Connection lost");
    // Throw a new `ValidationException` with message "Bad input"
    throw new ValidationException("Bad input");
}
// Try running this block; jump to `catch` if it throws
try {
    // Call `process("network")`
    process("network");
// Catch NetworkException $e
} catch (NetworkException $e) {
    // Print `"Retrying: " . $e->getMessage()` to the output
    echo "Retrying: " . $e->getMessage();
// Catch ValidationException $e
} catch (ValidationException $e) {
    // Print `"Fix input: " . $e->getMessage()` to the output
    echo "Fix input: " . $e->getMessage();
}
?>

Rethrowing Custom Exceptions

Sometimes you want to log an exception locally and still let a caller further up the stack handle it too; re-throwing it with throw $e inside a catch block does exactly that without losing the original error.

Example: Rethrowing Custom Exceptions

php
<?php
class AppException extends Exception {}

// Define the function `risky` with no parameters
function risky() {
    // Throw a new `AppException` with message "Something broke"
    throw new AppException("Something broke");
}
// Try running this block; jump to `catch` if it throws
try {
    // Try running this block; jump to `catch` if it throws
    try {
        // Call `risky()`
        risky();
    // Catch AppException $e
    } catch (AppException $e) {
        // Call `error_log($e->getMessage())`
        error_log($e->getMessage());
        throw $e;
    }
// Catch AppException $e
} catch (AppException $e) {
    // Print `"Handled higher up: " . $e->getMessage()` to the output
    echo "Handled higher up: " . $e->getMessage();
}
?>
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Not extending Exception (or another Throwable class), so the custom class cannot be thrown.
  2. Overriding the constructor and forgetting to call parent::__construct(), so the message and code are lost.
  3. Catching the base Exception before the custom class, so the custom handler never runs.
🔒

Chapter Quiz — Complete all 5 topics to unlock

0/5 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.