Lab 09: Error Handling & Exceptions
Objective
Use PHP's exception system: try/catch/finally, custom exception classes, exception chaining, error handlers, and PHP 8's never return type. Handle errors gracefully without crashing.
Background
PHP has two parallel error systems: the legacy trigger_error() / set_error_handler() system and the modern exception system. PHP 7+ converted most fatal errors into Error exceptions. PHP 8 added never return type (functions that always throw or exit), match exhaustiveness, and throw as an expression. Good error handling separates robust production apps from fragile scripts.
Time
30 minutes
Prerequisites
Lab 07 (OOP)
Tools
PHP 8.3 CLI
Docker image:
zchencow/innozverse-php:latest
Lab Instructions
Step 1: Try / Catch / Finally
💡
finallyalways runs — even if thetryblock returns early or an exception is thrown. Use it to release resources: close database connections, unlock files, stop timers. If bothcatchandfinallythrow, thefinallyexception wins (it overwrites the caught one).
📸 Verified Output:
Step 2: Custom Exception Classes
💡 Custom exceptions carry domain context —
ValidationExceptionholds an array of field errors,NotFoundExceptionencodes the HTTP 404 code. CatchingAppExceptioncatches all domain errors in one block; catching subclasses lets you handle each case specifically. Always extend from a base domain exception.
📸 Verified Output:
Step 3: Exception Chaining
💡 Exception chaining (
new Exception($msg, $code, $previous)) preserves the original cause. Log systems and APM tools (like Sentry) usegetPrevious()to show the full causal chain. Always chain when wrapping exceptions — never silently swallow the original error.
📸 Verified Output:
Step 4: Custom Error Handler & Throwable
💡
Throwableis the top-level interface in PHP's exception hierarchy — bothError(language errors: TypeError, ParseError, ArithmeticError) andException(user-thrown) implement it.catch (\Exception $e)missesErrorsubclasses. Usecatch (\Throwable $e)for truly catch-all handlers (logging, shutdown handlers).
📸 Verified Output:
Step 5: never Return Type
💡
neverreturn type (PHP 8.1) tells the type system "this function never returns." The type checker can then eliminate dead code warnings after calls toabort(). It's used in Laravel'sabort()helper, Symfony'sThrowableInterface, and any exception-throwing utility function.
📸 Verified Output:
Step 6: Result Pattern (No Exceptions for Control Flow)
💡 The Result pattern (common in Rust, Haskell, Go) uses return values instead of exceptions for expected failures. Reserve exceptions for unexpected situations (network down, disk full). Use Result for expected failures (invalid input, not found). This makes error paths explicit and composable.
📸 Verified Output:
Step 7: Logging Errors
📸 Verified Output:
Step 8: Complete — API Error Middleware
💡 Middleware error handling is how Laravel/Symfony handle exceptions — a top-level try/catch converts exceptions to HTTP responses.
ValidationHttpException→ 422,HttpException→ its status code,Throwable→ 500 (never expose internal errors to clients). This pattern keeps controllers clean.
📸 Verified Output:
Verification
Summary
PHP exception handling is mature and expressive. You've covered try/catch/finally, custom exception hierarchies, chaining, Throwable, never, the Result pattern, structured logging, and API middleware error handling. These patterns form the backbone of robust PHP applications.
Further Reading
Last updated
