Error Handling in PHP: try/catch and Beyond
Error handling is one of the key tasks for any web developer. Well-written PHP code not only executes business logic but also correctly responds to unexpected situations: missing files, database problems, invalid user input. In this article, we will thoroughly examine how error handling works in PHP, starting with the basic try/catch and ending with advanced techniques — custom exceptions and global handlers.
1. Basics: The try/catch Construct
The most common way to handle errors in modern PHP is the try/catch construct. It allows you to "catch" an exception (an object of the Throwable class or its descendants) and execute alternative code instead of terminating the script abruptly.
The basic syntax looks like this:
<?phptry { // Code that may throw an exception $file = fopen('data.txt', 'r'); if (!$file) { throw new Exception('Failed to open file'); } // Working with the file...} catch (Exception $e) { // Error handling echo 'An error occurred: ' . $e->getMessage();}?>In the try block, potentially dangerous code is placed. If an exception occurs inside it (via throw or due to an internal PHP error), execution immediately transfers to the catch block. The variable $e contains the exception object, on which you can call methods:
getMessage()— the error text;getCode()— the numeric error code;getFile()— the file where the error occurred;getLine()— the line that caused the error.
2. The finally Block: Code That Always Executes
Sometimes you need to perform certain actions regardless of whether an error occurred or not — for example, closing a database connection or freeing resources. The finally block is used for this:
<?phpfunction processData($data) { try { // Dangerous operation if (empty($data)) { throw new InvalidArgumentException('Data cannot be empty'); } echo "Processing data..."; return true; } catch (InvalidArgumentException $e) { echo "Error: " . $e->getMessage() . ""; return false; } finally { echo "The finally block always executes."; }}
processData([]); // Outputs: Error: Data cannot be empty, then the finally blockprocessData('test'); // Outputs: Processing data..., then the finally block?>The finally block executes even if there is a return statement in try or catch. This guarantees resource cleanup.
3. Custom Exceptions: Extending the Exception Class
In real projects, the standard Exception class is rarely sufficient. It is much more convenient to create your own exception classes for different types of errors. This makes the code more readable and simplifies catching specific situations.
Example: let's create an exception for validation errors and one for database errors.
<?phpclass ValidationException extends Exception {}class DatabaseException extends Exception {}
function validateUser($age) { if ($age < 0 || $age > 150) { throw new ValidationException('Invalid age'); } return true;}
function connectToDatabase($dsn) { if (empty($dsn)) { throw new DatabaseException('DSN cannot be empty'); } // Simulating connection...}
try { validateUser(200);} catch (ValidationException $e) { echo 'Validation error: ' . $e->getMessage();}
try { connectToDatabase('');} catch (DatabaseException $e) {