Skip to content

PHP Exceptions — Complete Guide to Try, Catch, and Throw

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about PHP Exceptions. We cover key concepts, practical examples, and best practices to help you master this topic.

PHP exceptions provide an object-oriented approach to error handling using try/catch blocks, allowing controlled error recovery and cleaner code than traditional error functions.

What You'll Learn

By the end of this tutorial, you'll use try/catch/finally blocks, throw exceptions, create custom exception classes, catch multiple exception types, and implement best practices.

Why Exceptions Matter

Exceptions separate error handling code from normal flow, propagate automatically through the call stack, and provide detailed context about what went wrong and where.

Real-World Use

A database wrapper throws exceptions on connection failure, query errors, and validation issues. The controller catches these and returns appropriate HTTP responses without crashing.

Exceptions Learning Path

flowchart LR
  A[Errors] --> B[Exceptions]
  B --> C[Classes/Objects]
  C --> D[Inheritance]
  D --> E[Interfaces]
  A --> F{You Are Here}
  style F fill:#f90,color:#fff

Basic Try/Catch

<?php
try {
    $result = 10 / 0;
} catch (DivisionByZeroError $e) {
    echo "Caught: " . $e->getMessage();
} finally {
    echo "This always runs";
}

Throwing Exceptions

<?php
function findUser(int $id): array {
    $users = [1 => ["name" => "Alice"], 2 => ["name" => "Bob"]];
    if (!isset($users[$id])) {
        throw new InvalidArgumentException("User not found: $id");
    }
    return $users[$id];
}
try {
    $user = findUser(5);
} catch (InvalidArgumentException $e) {
    echo $e->getMessage();  // User not found: 5
}

Custom Exception Classes

<?php
class DatabaseException extends RuntimeException {
    public function __construct(string $message, int $code = 0, ?Throwable $previous = null) {
        parent::__construct($message, $code, $previous);
    }
}
class UserNotFoundException extends RuntimeException {
    public function __construct(int $userId) {
        parent::__construct("User #$userId not found", 404);
    }
}
try {
    throw new UserNotFoundException(42);
} catch (UserNotFoundException $e) {
    http_response_code($e->getCode());
    echo $e->getMessage();
}

Multiple Catch Blocks

<?php
try {
    $file = fopen("config.json", "r");
    if (!$file) throw new RuntimeException("Cannot open file");
    $content = fread($file, filesize("config.json"));
    $data = json_decode($content, flags: JSON_THROW_ON_ERROR);
} catch (RuntimeException $e) {
    echo "File error: " . $e->getMessage();
} catch (JsonException $e) {
    echo "JSON error: " . $e->getMessage();
} finally {
    if (isset($file) && $file) fclose($file);
}

Exception Propagation

<?php
function inner(): void {
    throw new RuntimeException("Inner error");
}
function outer(): void {
    try {
        inner();
    } catch (RuntimeException $e) {
        throw new RuntimeException("Outer error", 0, $e);
    }
}
try {
    outer();
} catch (RuntimeException $e) {
    echo $e->getMessage();           // Outer error
    echo $e->getPrevious()->getMessage();  // Inner error
}

Common Mistakes

1. Catching Too Broad an Exception

Catching Throwable catches everything including system errors. Catch specific exception types.

2. Empty Catch Blocks

Swallowing exceptions without logging or handling hides bugs. At minimum, log the error.

3. Using Exceptions for Control Flow

Exceptions are for exceptional conditions, not regular control flow. Don't throw/catch for expected conditions like validation errors.

4. Not Cleaning Up Resources in Finally

Always close files and database connections in finally blocks to prevent resource leaks.

5. Exposing Exception Details to Users

Exception messages may contain sensitive information. Log the details and show a generic message to users.

Practice Questions

1. What is the base class for all PHP exceptions?

Throwable interface. Error and Exception implement Throwable. Catch Throwable to catch everything.

2. What is the purpose of the finally block?

It always executes after try/catch, regardless of whether an exception was thrown. Used for cleanup.

3. How do you create a custom exception?

Extend the Exception class or a subclass like RuntimeException. Add custom properties and methods as needed.

4. What is exception chaining?

Passing a previous exception to a new exception's constructor, preserving the original error context.

5. Challenge: Create a custom exception hierarchy for a user management system.

<?php
class UserException extends RuntimeException {}
class UserNotFoundException extends UserException {}
class UserValidationException extends UserException {}
class DuplicateEmailException extends UserException {}
try {
    // Simulate different error conditions
    throw new DuplicateEmailException("Email already registered");
} catch (UserNotFoundException $e) {
    http_response_code(404);
} catch (UserValidationException $e) {
    http_response_code(400);
} catch (UserException $e) {
    http_response_code(500);
}

FAQ

What is the difference between Exception and Error?

Exception is for recoverable errors. Error (Throwable) includes fatal errors like out of memory. Catch Exception for application errors.

Can I throw any object as an exception?

No. Only objects implementing Throwable can be thrown. Throw Exception or its subclasses.

What is the @ operator's effect on exceptions?

@ suppresses errors but does NOT suppress exceptions. Use try/catch for exceptions.

How do I log all uncaught exceptions?

Use set_exception_handler() to register a global handler for uncaught exceptions.

Can I have multiple finally blocks?

No. There is only one finally block per try/catch.

Mini Project: Exception Handler

Build a global exception handler that logs and returns appropriate responses.

<?php
set_exception_handler(function(Throwable $e): void {
    $log = sprintf("[%s] %s in %s:%d\n",
        date("Y-m-d H:i:s"), $e->getMessage(), $e->getFile(), $e->getLine());
    file_put_contents("exceptions.log", $log, FILE_APPEND);
    $code = $e->getCode() >= 100 && $e->getCode() < 600 ? $e->getCode() : 500;
    http_response_code($code);
    header("Content-Type: application/json");
    echo json_encode(["error" => $code === 500 ? "Server error" : $e->getMessage()]);
});
throw new RuntimeException("Something broke", 400);

What's Next

PHP Classes Objects PHP Inheritance PHP Interfaces

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro