Reading — step 1 of 7
Learn
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— genericLogicException,RuntimeException— broad categoriesInvalidArgumentException,RangeException— for bad parametersTypeError,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/0warns and returns INF in PHP for floats)
These are catchable as exceptions — not the silent failures of PHP 5.
Common mistakes
- Catching
Exceptionto handle TypeErrors — TypeError extends Error, not Exception. Catch\Throwableif you need both. - Throwing strings —
throw "oops"is a fatal error. Must throw an Exception subclass. - Suppressing with
@— silent failure. Always throw or log. - Forgetting
\in custom code —Exceptionworks at the global namespace; inside namespaces, use\Exceptionoruse 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…