Skip to content
Error Handling
step 1/7

Reading — step 1 of 7

Learn

~3 min readClasses, Inheritance, Errors

PHP has TWO error systems: the legacy error model (warnings, notices, fatal errors) and the modern exception model. Modern PHP code uses exceptions exclusively. The PHP manual treats this thoroughly; we'll focus on the exception model since that's what you'll write.

Throwing exceptions

<?php
function parseAge(string $s): int {
    if ($s === '') {
        throw new InvalidArgumentException('empty input');
    }
    if (!is_numeric($s)) {
        throw new InvalidArgumentException("not a number: $s");
    }
    $n = (int) $s;
    if ($n < 0) {
        throw new RangeException("negative age: $n");
    }
    return $n;
}

Use throw new ExceptionClass("message"). Common built-ins:

  • Exception — generic
  • LogicException, RuntimeException — broad categories
  • InvalidArgumentException, RangeException — for bad parameters
  • TypeError, ValueError (PHP 8+) — automatic for type-related issues

try / catch / finally

try {
    $age = parseAge($input);
    echo "got $age";
} catch (InvalidArgumentException $e) {
    echo "bad input: " . $e->getMessage();
} catch (RangeException $e) {
    echo "out of range: " . $e->getMessage();
} catch (Exception $e) {
    echo "other error: " . $e->getMessage();
} finally {
    echo "cleanup";
}

Most-specific exceptions first. The finally block always runs (success or exception).

Multi-catch (PHP 8+)

try {
    doWork();
} catch (TypeError | ValueError $e) {
    echo "argument error: " . $e->getMessage();
}

Matches multiple types in one block.

Custom exception classes

class ValidationException extends Exception {
    public function __construct(
        public readonly string $field,
        string $message,
    ) {
        parent::__construct($message);
    }
}

throw new ValidationException('email', 'must contain @');

try {
    // ...
} catch (ValidationException $e) {
    echo "field {$e->field}: {$e->getMessage()}";
}

Inherit from Exception (or another exception class). Add fields for structured error data.

The exception hierarchy

PHP 7 unified the hierarchy. All exceptions and errors implement \Throwable:

Throwable
├─ Error                 (engine errors — TypeError, ParseError, etc.)
│  ├─ TypeError
│  ├─ ValueError         (PHP 8+)
│  └─ ArithmeticError
│     └─ DivisionByZeroError
└─ Exception             (your application errors)
   ├─ LogicException
   │  ├─ InvalidArgumentException
   │  └─ DomainException
   └─ RuntimeException
      ├─ OutOfBoundsException
      └─ UnexpectedValueException

Catch \Throwable to catch everything (including engine errors). Catch Exception for application errors only — leaving Errors to bubble up.

Error suppression and assert

  • @expression — suppresses errors silently. AVOID — hides bugs.
  • assert($condition, 'message') — like assert in other languages. Disabled in production by default.
  • Use proper exception throwing instead of these.

Errors vs Exceptions

In modern PHP (7+):

  • TypeError thrown for type mismatches at function boundaries
  • ValueError thrown for invalid argument values to internal functions
  • DivisionByZeroError thrown for intdiv($x, 0) (note: 1/0 warns and returns INF in PHP for floats)

These are catchable as exceptions — not the silent failures of PHP 5.

Common mistakes

  • Catching Exception to handle TypeErrors — TypeError extends Error, not Exception. Catch \Throwable if you need both.
  • Throwing stringsthrow "oops" is a fatal error. Must throw an Exception subclass.
  • Suppressing with @ — silent failure. Always throw or log.
  • Forgetting \ in custom codeException works at the global namespace; inside namespaces, use \Exception or use Exception;.
  • Empty catch blocks — silent swallow. At minimum, log the exception.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…