PHP Exceptions and Error Handling

PHP Exceptions and Error Handling

PHP 8 has a unified Throwable hierarchy. Understanding it lets you write robust, failure-resilient applications.

1 - The Hierarchy

Throwable
├── Error          (engine-level)
│   ├── TypeError
│   ├── ValueError
│   └── ArithmeticError
└── Exception      (application-level)
    ├── RuntimeException
    ├── InvalidArgumentException
    ├── LogicException
    └── ... custom exceptions

2 - try / catch / finally

try {
    riskyOperation();
} catch (InvalidArgumentException $e) {
    echo "Bad input: " . $e->getMessage();
} catch (RuntimeException | LogicException $e) {
    echo "App error: " . $e->getMessage();
} catch (Throwable $e) {
    echo "Unexpected: " . $e->getMessage();
} finally {
    echo "Always runs.";
}

3 - Custom Exception

class NotFoundException extends RuntimeException {
    public function __construct(string $resource, int|string $id) {
        parent::__construct(
            "$resource #$id not found.",
            404
        );
    }
}

function findUser(int $id): array {
    $user = queryDb($id);
    if (!$user) {
        throw new NotFoundException("User", $id);
    }
    return $user;
}

4 - JSON_THROW_ON_ERROR

try {
    $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
    echo "Invalid JSON: " . $e->getMessage();
}

Note: Always catch the most specific exception type first. A catch block for Throwable at the end acts as a safety net but should log and rethrow in most cases, not silently swallow errors.

-Tip-