# Exceptions and Error Handling — PHP

Source: https://www.skillbyai.com/en/php/e-errors

> 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.

![A tree with one root node branching into two nodes, each with several leaf nodes below.](assets/figures/php/section-4-map.svg) — Figure 4.1 — Throwable, Exception and Error.

## Domain exceptions, chaining and JSON errors

Specific catches, preserved causes and throwing JSON functions.

```php
<?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.

**Quiz:** Which class does a TypeError extend in PHP 8?

- [ ] Exception
- [ ] RuntimeException
- [x] Error
- [ ] JsonException

*Answer:* Error. TypeError is an Error, signalling an engine-level type problem, typically a bug.
