Error Handling in PHP: try/catch and Advanced Techniques

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Error Handling in PHP: From try/catch to Production-Grade Solutions

\n\n

Every PHP developer encounters errors. Basic try/catch is just the tip of the iceberg. In this article, we will explore advanced error handling techniques that will transform fragile code into a reliable system capable of correctly responding to any failures.

\n\n

1. Deep Dive into try/catch: The finally Block and Multiple catch Blocks

\n\n

Standard try/catch is the foundation, but many forget about the finally block, which always executes regardless of whether an exception occurred. This is the ideal place for releasing resources (files, database connections).

\n\n
<?php\nfunction processFile(string $filename): void {\n    $handle = fopen($filename, 'r');\n    if (!$handle) {\n        throw new \\RuntimeException("Could not open file: $filename");\n    }\n    \n    try {\n        // Processing the file\n        while (($line = fgets($handle)) !== false) {\n            // ... processing logic\n        }\n    } catch (\\RuntimeException $e) {\n        // Specific I/O error handling\n        error_log("File read error: " . $e->getMessage());\n        throw $e; // Re-throw\n    } catch (\\Exception $e) {\n        // Handling any other exceptions\n        error_log("Unknown error: " . $e->getMessage());\n        throw $e;\n    } finally {\n        fclose($handle); // Always close the file\n    }\n}\n?>
\n\n

Important: The order of catch blocks matters. More specific exceptions (e.g., \\RuntimeException) should come before general ones (\\Exception). Otherwise, the specific block will never execute.

\n\n

2. Custom Exception Classes: Semantics and Hierarchy

\n\n

Built-in exceptions (PDOException, RuntimeException) are often not informative enough. Create your own exception hierarchy to separate errors by type and simplify their handling.

\n\n
<?php\n// Base application exception\nclass AppException extends \\Exception {}\n\n// Validation errors\nclass ValidationException extends AppException {\n    private array $errors;\n    \n    public function __construct(array $errors, string $message = "Validation error") {\n        parent::__construct($message, 422);\n        $this->errors = $errors;\n    }\n    \n    public function getErrors(): array {\n        return $this->errors;\n    }\n}\n\n// Access errors\nclass AccessDeniedException extends AppException {\n    public function __construct(string $message = "Access denied") {\n        parent::__construct($message, 403);\n    }\n}\n\n// Usage example\nfunction registerUser(array $data): void {\n    $errors = [];\n    \n    if (empty($data['email'])) {\n        $errors['email'] = 'Email is required';\n    }\n    if (empty($data['password'])) {\n        $errors['password'] = 'Password is required';\n    }\n    \n    if (!empty($errors)) {\n        throw new ValidationException($errors);\n    }\n    \n    // Registration logic...\n}\n\ntry {\n    registerUser(['email' => '']);\n} catch (ValidationException $e) {\n    // Specific handling: return JSON with errors\n    http_response_code(422);\n    echo json_encode(['errors' => $e->getErrors()]);\n} catch (AccessDeniedException $e) {\n    http_response_code(403);\n    echo json_encode(['error' => $e->getMessage()]);\n} catch (AppException $e) {\n    // General handling for all other application errors\n    http_response_code(500);\n    echo json_encode(['error' => 'Internal server error']);\n}\n?>
\n\n

Tip: Use HTTP codes (400, 403, 422, 500) as exception codes — this simplifies integration with REST APIs.

\n\n

3. Global Error Handler: ErrorHandler and ShutdownHandler

\n\n

Not all errors can be caught with try/catch. Fatal errors (E_ERROR), warnings (E_WARNING), and parse errors

Blogs

Book Recommendations