Skip to content
Happy Programming Guide
Start learning
PHP

Handling Errors and Exceptions in PHP

The difference between errors and exceptions in PHP, how try/catch/finally works, when to throw your own, and why you must never show a raw error to a visitor.

Food and Coding at NorthEnd Coffee shop, Banani, Dhaka

PHP has two things that can go wrong, and the distinction matters:

  • Exceptions — problems your code can anticipate and handle. A missing file, a failed database connection, invalid input.
  • Errors — problems in the code itself. Calling a method on null, a type mismatch, running out of memory.

Since PHP 7 both can be caught, but you handle them very differently.

The basic shape#

PHP
<?php
try {
    $data = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    $data = [];
    error_log("Bad JSON: " . $e->getMessage());
}
?>

The try block runs. If the named exception is thrown, the catch block runs instead of the script stopping. If nothing goes wrong, the catch is skipped.

Catch what you expect#

PHP
<?php
try {
    $pdo = new PDO($dsn, $user, $pass);
} catch (PDOException $e) {
    error_log("Database unavailable: " . $e->getMessage());
    http_response_code(503);
    exit("Sorry, we cannot reach the database right now.");
}
?>

Catching PDOException specifically means a genuine bug elsewhere in the block still surfaces rather than being swallowed.

To handle several kinds differently:

PHP
<?php
try {
    $result = process($input);
} catch (InvalidArgumentException $e) {
    $message = "That input was not valid: " . $e->getMessage();
} catch (RuntimeException $e) {
    $message = "Something went wrong while processing.";
} catch (Throwable $e) {
    error_log($e);                   // last resort — log everything
    $message = "Unexpected problem.";
}
?>

Throwable is the parent of both Exception and Error, so it catches absolutely everything. Put it last and always log what it caught.

finally#

PHP
<?php
$handle = fopen("data.txt", "r");

try {
    return processFile($handle);
} finally {
    fclose($handle);      // runs whether it succeeded, failed, or returned
}
?>

Throwing your own#

PHP
<?php
function setAge(int $age): int {
    if ($age < 0) {
        throw new InvalidArgumentException("Age cannot be negative, got $age");
    }
    if ($age > 130) {
        throw new InvalidArgumentException("Age of $age is not plausible");
    }
    return $age;
}
?>

This is better than returning false and hoping the caller checks. An exception cannot be ignored by accident, and the message travels with it.

Use the built-in types where they fit: InvalidArgumentException for bad input, RuntimeException for things that fail at run time, LogicException for mistakes that should have been caught in development.

Reading what you caught#

PHP
<?php
catch (Throwable $e) {
    error_log(sprintf(
        "%s: %s in %s:%d",
        get_class($e),
        $e->getMessage(),
        $e->getFile(),
        $e->getLine()
    ));
}
?>

$e->getTraceAsString() gives you the full call chain, which is what you actually need when the failure is several functions deep.

A global safety net#

PHP
<?php
set_exception_handler(function (Throwable $e) {
    error_log($e);
    http_response_code(500);
    include __DIR__ . "/templates/error-500.php";
    exit;
});
?>

Anything not caught anywhere else lands here, so visitors get a designed page rather than a white screen or a stack trace.

Turning warnings into exceptions#

Some older PHP functions emit a warning and carry on returning false, which is easy to miss:

PHP
<?php
set_error_handler(function (int $severity, string $message, string $file, int $line) {
    throw new ErrorException($message, 0, $severity, $file, $line);
});

try {
    $contents = file_get_contents("missing.txt");   // now throws
} catch (ErrorException $e) {
    $contents = "";
}
?>

Questions people ask#

What is the difference between Exception and Error?

Exception is for conditions your code should anticipate. Error signals a fault in the code itself, such as a TypeError. Both implement Throwable.

Should I create my own exception classes?

Once your application has distinct failure kinds, yes: class PaymentDeclinedException extends RuntimeException {} lets callers catch exactly that case.

Why is my catch block not running?

You are probably catching the wrong type. catch (Exception $e) does not catch a TypeError, which is an Error. Use Throwable temporarily to confirm what is actually thrown.

Does finally run if I return inside try?

Yes. It runs before the value is handed back, which is what makes it right for cleanup.

Where to go next#

Next lessonPHP security best practices

Keep reading

PHP

PHP Security Best Practices

The security work that actually matters in PHP: prepared statements, output escaping, CSRF tokens, password hashing, file uploads and session handling.

6 min read

Keep going — pick your next guide

The fastest way to improve is to read one guide, then build the thing it describes. Start with the basics, or jump straight to a project.

Ask a question or share what worked

Your email address will not be published. Required fields are marked *