Error Handling in PHP: From Basic Constructs to Professional Patterns
Any PHP developer sooner or later encounters a situation where the code does not behave as expected. A file is not found, the database is unavailable, the user entered incorrect data — all these are exceptional situations. Competent error handling turns a fragile application into a reliable system. In this article, we will cover all aspects of working with errors in PHP: from simple try/catch to creating custom exception classes and global handlers.
1. Basics: The try/catch Construct and throw
In PHP, exceptions are objects of the Exception class (or its descendants). The basic mechanism consists of three keywords:
try— the block in which potentially dangerous code is executed;catch— the block that catches the exception;throw— the statement that "throws" an exception.
Let's look at a simple example:
<?phpfunction divide($a, $b) { if ($b == 0) { throw new Exception('Division by zero is impossible'); } return $a / $b;}
try { echo divide(10, 0);} catch (Exception $e) { echo 'Error: ' . $e->getMessage();}?>When execution reaches throw, PHP immediately leaves the current function and looks for a suitable catch block. If a catch is found, execution continues from there. If not, a fatal error occurs.
Important: starting from PHP 7, most internal errors (e.g., division by zero) began throwing exceptions of the DivisionByZeroError class. To catch them, you need to specify the corresponding type.
2. Advanced Features: Multiple catch Blocks and finally
Often, different types of exceptions can occur in a single try block. PHP allows you to create multiple catch blocks for different classes:
<?phpclass ValidationException extends Exception {}class DatabaseException extends Exception {}
try { // code that may throw different exceptions} catch (ValidationException $e) { echo 'Validation error: ' . $e->getMessage();} catch (DatabaseException $e) { echo 'Database error: ' . $e->getMessage();} catch (Exception $e) { // General handler for all other exceptions echo 'Unknown error: ' . $e->getMessage();}?>The order of catch blocks matters: PHP checks them sequentially, and the first one matching the type catches the exception. Therefore, the general catch (Exception $e) is always placed last.
The finally block is executed in any case — both after successful completion of try, and after catching an exception, and even if there is a return statement in catch. This is an ideal place for closing files, releasing resources, or writing to a log:
<?phpfunction readConfig($filename) { $handle = fopen($filename, 'r'); if (!$handle) { throw new Exception('Failed to open file'); } try { // reading data return fread($handle, filesize($filename)); } finally { fclose($handle); // the file will close even on exception }}?>3. Custom Exception Classes and Global Handlers
Creating your own exception classes is a powerful tool for structuring code. It is enough to inherit from Exception:
<?phpclass InvalidEmailException extends Exception { public function __construct($email, $code = 0, Throwable $previous = null) { $message = "Email '$email' has an invalid format"; parent::__construct($message, $code, $previous); }}
function validateEmail($ema