iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

PHP Exceptions

Exceptions are how errors propagate up the call stack. try the risky code, catch a specific exception class, finally clean up.

The shape

PHP
try {
    $config = json_decode($input, true, flags: JSON_THROW_ON_ERROR);
    process($config);
} catch (JsonException $e) {
    echo 'Bad JSON: ', $e->getMessage();
} catch (RuntimeException $e) {
    echo 'Run-time problem: ', $e->getMessage();
} finally {
    cleanup();   // runs whether we caught or not
}

Throw your own

PHP
function deposit(int $cents): void {
    if ($cents <= 0) {
        throw new InvalidArgumentException("cents must be positive: $cents");
    }
    // ...
}

Define a custom exception

PHP
class PaymentDeclined extends RuntimeException
{
    public function __construct(
        public readonly string $reason,
        ?\Throwable $previous = null,
    ) {
        parent::__construct("Payment declined: $reason", 0, $previous);
    }
}

throw new PaymentDeclined('Card expired');

Standard exception hierarchy

Throwable                       ← interface
 ├── Error                       ← engine-level failures (don't catch)
 │    ├── TypeError
 │    ├── ValueError
 │    └── …
 └── Exception                   ← user-land
      ├── RuntimeException
      ├── LogicException
      │    ├── InvalidArgumentException
      │    ├── DomainException
      │    └── …
      └── (your own)

Anti-patterns

  • catch (Exception $e) by itself swallows too much. Catch the most specific subclass that fits.
  • Catching and ignoring (catch (X $e) {}) hides bugs. At minimum log it.
  • Using exceptions for normal control flow — they're for exceptional cases.

Re-throwing with context

PHP
try {
    $api->send($payload);
} catch (NetworkException $e) {
    throw new SyncFailed("Couldn't reach API", previous: $e);
}
Tip: A global error handler (Sentry, Bugsnag, framework's own logger) catches what your code doesn't. Let exceptions bubble; let the handler decide what to do with them.

Example

Example
<?php
try {
    if (random_int(0, 1) === 0) {
        throw new RuntimeException('Oops');
    }
    echo 'OK';
} catch (RuntimeException $e) {
    echo 'Caught: ', $e->getMessage();
} finally {
    echo PHP_EOL, 'cleanup';
}
Try it Yourself »

Exercise

Catch a specific exception type.

(JsonException $e) { echo $e->getMessage(); }

Test yourself

Q1. Base class for user-land exceptions is…
Q2. finally runs…
Q3. Don't catch…

Discussion

Loading…