In older PHP code, errors often resulted in the script halting and displaying ugly error messages to the user. Exceptions provide an object-oriented, structured way to handle these runtime errors gracefully, allowing you to “catch” the error and execute cleanup or recovery code.
1. Exceptions vs. Traditional Errors
| Feature | Traditional PHP Error (E_WARNING, E_NOTICE) | PHP Exception (Throwable) |
| Handling | Handled by PHP’s default error handler (often leading to warnings/crashes). | Must be explicitly caught using try...catch blocks. |
| Control Flow | Script may continue or stop abruptly. | Program flow jumps directly to the catch block, bypassing all intermediate code. |
| Nature | Usually non-recoverable issues (e.g., syntax error). | Represents a recoverable condition (e.g., file not found, invalid database query). |
2. The try...catch...finally Block
This is the fundamental structure for handling exceptions:
| Block | Purpose | Execution |
try | Contains the code that might throw an exception (the risky code). | Executes first. |
catch | Contains the code that executes if an exception is thrown in the try block. | Executes only if an exception of the specified type occurs. |
finally | Contains code that always executes, regardless of whether an exception was thrown or caught. | Executes last, after try or after catch. |
Example: Handling a Division-by-Zero Error
<?php
function safe_divide($numerator, $denominator) {
// 1. Begin the risky operation inside the 'try' block
try {
if ($denominator === 0) {
// If condition fails, THROW an Exception
throw new Exception("Cannot divide by zero.");
}
$result = $numerator / $denominator;
echo "Result: $result<br>";
// 2. Catch the specific Exception type (the Exception object is assigned to $e)
} catch (Exception $e) {
// Log the error and show a clean message to the user
error_log("Division Error: " . $e->getMessage());
echo "Error: The calculation could not be performed. Please check input.<br>";
// 3. The 'finally' block runs no matter what
} finally {
echo "Operation finished.<br>";
}
}
safe_divide(10, 2); // Successful division (runs try, then finally)
safe_divide(10, 0); // Division by zero (runs try, jumps to catch, then finally)
?>
3. Throwing Exceptions
To signal that a critical problem has occurred, you use the throw keyword followed by a new instance of an Exception class.
| Function | Purpose |
new Exception(message) | Creates a standard exception object with a custom error message. |
throw | Immediately stops the current function/block execution and passes the exception up the call stack until a matching catch block is found. |
4. Custom Exceptions (OOP in Error Management)
For better error management, it’s best practice to define your own Custom Exception Classes that inherit from the base Exception class. This allows you to catch specific types of errors.
Example: Custom File Exception
<?php
// Define a specialized exception class
class FileAccessException extends Exception {}
function read_config($filename) {
if (!file_exists($filename)) {
// Throw our custom exception
throw new FileAccessException("Configuration file not found: $filename");
}
// ... file reading logic ...
return "Config data loaded.";
}
try {
echo read_config('non_existent_file.ini') . "<br>";
} catch (FileAccessException $e) {
// We can catch ONLY the specific file error
echo "CAUGHT FILE ERROR: " . $e->getMessage() . "<br>";
} catch (Exception $e) {
// Catch all other unexpected errors (database, etc.)
echo "CAUGHT GENERIC ERROR: " . $e->getMessage() . "<br>";
}
// Output: CAUGHT FILE ERROR: Configuration file not found: non_existent_file.ini
?>
5. Multiple Catch Blocks (Specific to Generic)
You can chain multiple catch blocks. The system always tries to match the exception to the most specific class first (e.g., FileAccessException) before falling back to the generic base class (Exception).
