SkillByAIOpen interactive version →

Lesson 10 / 25

Exceptions and Error Handling

Handle errors with exceptions, the Throwable hierarchy and sensible error reporting.

Failing clearly and safely

PHP 7+ turned most fatal errors into exceptions. Everything throwable implements Throwable, with two branches: Exception for application-level problems you may handle (InvalidArgumentException, RuntimeException, PDOException, JsonException) and Error for engine-level problems that usually indicate bugs (TypeError, ValueError, ArgumentCountError, DivisionByZeroError, UnhandledMatchError). Use try/catch/finally, catch specific types, and create domain exceptions (OutOfStockException extends RuntimeException) that carry useful context. When rethrowing, pass the original as the previous exception to keep the chain. Install a top-level handler (frameworks provide one) that logs unexpected throwables and returns a safe error response without leaking stack traces. Configure error reporting: error_reporting = E_ALL everywhere, display_errors = On only in development, log_errors = On in production. Avoid the @ error-suppression operator, which hides real problems, and prefer functions that throw (such as json_decode with JSON_THROW_ON_ERROR) over checking error codes manually.

The Throwable hierarchy

Exceptions model expected failures; Errors usually signal bugs in the code.

Figure 4.1 — Throwable, Exception and Error.

Domain exceptions, chaining and JSON errors

Specific catches, preserved causes and throwing JSON functions.

<?php
declare(strict_types=1);

final class OutOfStockException extends RuntimeException
{
    public static function forSku(string $sku, int $requested, int $available): self
    {
        return new self("{$sku}: requested {$requested}, only {$available} left");
    }
}

function parseOrder(string $json): array
{
    try {
        return json_decode($json, true, flags: JSON_THROW_ON_ERROR);
    } catch (JsonException $e) {
        throw new InvalidArgumentException('Malformed order payload', previous: $e);   // keep the cause
    }
}

try {
    $order = parseOrder($requestBody);
    $inventory->reserve($order['sku'], $order['qty']);
} catch (OutOfStockException $e) {
    http_response_code(409);
    echo json_encode(['error' => $e->getMessage()]);
} catch (InvalidArgumentException $e) {
    http_response_code(400);
    echo json_encode(['error' => 'Invalid request']);
} finally {
    $metrics->increment('orders.attempted');
}

Never show stack traces to users

With display_errors on in production, an error can print file paths, SQL and secrets to the browser. Log details server-side and show a generic message with a reference ID.

Quick check: Which class does a TypeError extend in PHP 8?

  • Exception
  • RuntimeException
  • Error
  • JsonException
Answer

Error — TypeError is an Error, signalling an engine-level type problem, typically a bug.