PHP Exception
PHP Exception Handling
Section titled “PHP Exception Handling”Exceptions provide an object-oriented way to handle errors and exceptional situations, allowing for cleaner and more robust error management than traditional methods.
What is an Exception?
Section titled “What is an Exception?”PHP has a robust exception model. An exception is an object that describes an error or unexpected event that occurred during script execution.
When an exceptional situation occurs, the script can “throw” an exception. This interrupts the normal flow of execution.
What happens when an exception is thrown:
- The current code state is saved.
- Execution jumps to the nearest enclosing
catchblock that can handle the type of exception thrown. - If no matching
catchblock is found in the current function, it looks up the call stack until a handler is found. - If the exception reaches the global scope without being caught, the script terminates with a fatal error (unless a global exception handler is set).
Key concepts we will cover:
- Using
try,throw, andcatchblocks - The
finallyblock (ensuring code execution) - Creating custom exception classes
- Handling multiple exception types
- Re-throwing exceptions
- Setting a global exception handler
Note: Exceptions are intended for exceptional circumstances (errors, invalid state), not for normal program control flow like if/else or loops.
Basic Exception Handling: try, throw, catch
Section titled “Basic Exception Handling: try, throw, catch”When an exception is thrown using the throw keyword, the code immediately following it within the try block is skipped. PHP searches for the first matching catch block.
If an exception isn’t caught, it results in a fatal error:
Example: Throwing an uncaught exception:
<?phpdeclare(strict_types=1);
function checkNumber(int $number): bool { if ($number > 1) { // Throwing an instance of the base Exception class throw new Exception("Value must be 1 or below"); } return true;}
// Trigger exception - no try/catch block here!try { checkNumber(2);} catch (Exception $e) { // This catch block will handle the exception echo "Caught exception: " . $e->getMessage();}
// If we called checkNumber(2) outside a try block:// It would result in a Fatal error: Uncaught Exception: Value must be 1 or below...?>To handle exceptions gracefully, use the try...catch structure:
try- Encloses the code that might throw an exception.throw- The keyword used to signal an exception. You throw an object that is an instance ofExceptionor its subclasses (includingErrorsubclasses in PHP 7+).catch- Declares an exception handler. It specifies the type of exception it can catch and provides a variable (e.g.,$e) to access the exception object.
Example: Catching an exception:
<?phpdeclare(strict_types=1);
function checkNumber(int $number): bool { if ($number > 1) { throw new Exception("Value must be 1 or below"); } return true;}
try { // Code that might throw an exception checkNumber(5);
// This line will not be executed if checkNumber(5) throws echo 'If you see this, the number is 1 or below.';} catch (Exception $e) { // Code to handle the exception // $e is an object containing details about the exception echo 'Caught Exception: ' . $e->getMessage() . PHP_EOL; echo 'File: ' . $e->getFile() . PHP_EOL; echo 'Line: ' . $e->getLine() . PHP_EOL;}
echo 'Script continues after the try/catch block.';?>Output of the above example:
Caught Exception: Value must be 1 or belowFile: /path/to/your/script.phpLine: 7Script continues after the try/catch block.The finally Block
Section titled “The finally Block”A finally block can be added after the catch blocks. The code inside finally will always execute after the try (and any catch) blocks finish, regardless of whether an exception was thrown or caught.
This is useful for cleanup operations, like closing file handles or database connections, that must happen whether the operation succeeded or failed.
<?phpdeclare(strict_types=1);
function processResource(bool $shouldFail) { echo "Opening resource...\n"; try { echo "Processing resource...\n"; if ($shouldFail) { throw new Exception("Processing failed!"); } echo "Resource processed successfully.\n"; } catch (Exception $e) { echo "Caught exception: " . $e->getMessage() . "\n"; } finally { // This code runs whether an exception occurred or not echo "Closing resource...\n"; }}
echo "--- Running without failure ---\n";processResource(false);
echo "\n--- Running with failure ---\n";processResource(true);?>Output:
--- Running without failure ---Opening resource...Processing resource...Resource processed successfully.Closing resource...
--- Running with failure ---Opening resource...Processing resource...Caught exception: Processing failed!Closing resource...Creating a Custom Exception Class
Section titled “Creating a Custom Exception Class”You can create your own exception types by extending the base Exception class (or other exception classes). This allows you to create more specific error handlers and add custom properties or methods to your exceptions.
Custom exception classes inherit properties and methods from Exception (like getMessage(), getFile(), getLine(), getCode(), getTraceAsString()).
Example: Creating and using a ValidationException:
<?phpdeclare(strict_types=1);
// Custom exception classclass ValidationException extends Exception { // You can add custom properties or methods public function getDetailedMessage(): string { return "Validation Error: {$this->getMessage()} in {$this->getFile()} on line {$this->getLine()}"; }}
function validateEmail(string $email): bool { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { // Throw our custom exception throw new ValidationException("'{$email}' is not a valid email address"); } // Example: Check if domain is blocked (conceptual) if (str_ends_with($email, '@blocked.com')) { // Throwing a standard exception for a different issue throw new Exception("Email domain is blocked"); } return true;}
$email = "invalid-email";
try { validateEmail($email); echo "Email is valid.";} catch (ValidationException $e) { // Catch specifically our custom validation exception echo $e->getDetailedMessage();} catch (Exception $e) { // Catch any other standard exceptions echo "General Error: " . $e->getMessage();}?>Output:
Validation Error: 'invalid-email' is not a valid email address in /path/to/your/script.php on line 13Handling Multiple Exception Types
Section titled “Handling Multiple Exception Types”You can use multiple catch blocks to handle different types of exceptions specifically.
PHP checks the catch blocks in order. The first block with a type hint matching the thrown exception (or one of its parent classes) will be executed.
Example: Using multiple catch blocks:
<?phpdeclare(strict_types=1);
class NetworkException extends Exception {}class DatabaseException extends Exception {}
function performAction(int $type) { if ($type === 1) { throw new NetworkException("Could not connect to server"); } elseif ($type === 2) { throw new DatabaseException("Error querying database"); } elseif ($type === 3) { throw new InvalidArgumentException("Invalid action type"); // Built-in SPL exception } else { throw new Exception("An unknown error occurred"); }}
for ($i = 1; $i <= 4; $i++) { try { echo "Attempting action type $i: "; performAction($i); echo "Success!\n"; } catch (NetworkException $e) { echo "Network Error: " . $e->getMessage() . "\n"; } catch (DatabaseException $e) { echo "Database Error: " . $e->getMessage() . "\n"; } catch (InvalidArgumentException $e) { // Catching a specific built-in type echo "Input Error: " . $e->getMessage() . "\n"; } catch (Exception $e) { // Catch-all for other Exceptions echo "General Error: " . $e->getMessage() . "\n"; }}?>Output:
Attempting action type 1: Network Error: Could not connect to serverAttempting action type 2: Database Error: Error querying databaseAttempting action type 3: Input Error: Invalid action typeAttempting action type 4: General Error: An unknown error occurredPHP 7.1+ also allows catching multiple types in a single block using the pipe (|) character:
<?php// ... (Exception classes defined as above)try { // ... code that might throw NetworkException or DatabaseException ...} catch (NetworkException | DatabaseException $e) { echo "Connectivity Error: " . $e->getMessage();} catch (Exception $e) { echo "Other Error: " . $e->getMessage();}?>Re-throwing Exceptions
Section titled “Re-throwing Exceptions”Sometimes, a catch block might perform partial handling (like logging) and then decide to throw the same or a different exception up the call stack for a higher-level handler to deal with.
This is useful for wrapping low-level exceptions with more context or user-friendly messages.
<?phpdeclare(strict_types=1);
class ConfigurationException extends Exception {}
function loadConfigFile(string $path) { if (!file_exists($path)) { throw new Exception("File not found at: {$path}"); // Generic low-level exception } // ... process file ... if (false /* some config error */) { throw new Exception("Invalid config format"); }}
function initializeApp(string $configPath) { try { loadConfigFile($configPath); } catch (Exception $e) { // Log the original error error_log("Config Error: " . $e->getMessage());
// Re-throw a more specific, higher-level exception // Pass the original exception as the 'previous' exception (PHP 5.3+) throw new ConfigurationException("Failed to initialize application due to configuration issue.", 0, $e); }}
try { initializeApp("nonexistent_config.ini");} catch (ConfigurationException $e) { echo "Application Error: " . $e->getMessage() . "\n"; // Optionally inspect the original cause: if ($previous = $e->getPrevious()) { echo "Original Cause: " . $previous->getMessage() . "\n"; }}?>Output (will also log the original error):
Application Error: Failed to initialize application due to configuration issue.Original Cause: File not found at: nonexistent_config.iniSetting a Global Exception Handler
Section titled “Setting a Global Exception Handler”The set_exception_handler() function registers a callback function that will be called for any exception that is not caught by a try...catch block.
This is useful for providing a fallback mechanism to log uncaught exceptions or display a generic error page in production environments, preventing sensitive error details from being shown to users.
<?phpdeclare(strict_types=1);
// Define the global exception handler function// It receives the Throwable object (Exception or Error)function globalExceptionHandler(Throwable $exception) { // Log the error (recommended for production) error_log("Uncaught Exception: " . $exception->getMessage() . " in " . $exception->getFile() . ":" . $exception->getLine());
// Display a user-friendly message (avoid showing $exception details directly in production) http_response_code(500); // Internal Server Error echo "<h1>Oops! Something went wrong.</h1>"; echo "<p>We've been notified and are looking into it. Please try again later.</p>";}
// Register the handlerset_exception_handler('globalExceptionHandler');
// Example of code throwing an exception that won't be caught elsewherefunction riskyOperation() { throw new Exception('Something bad happened unexpectedly!');}
riskyOperation();
echo "This line will not be reached.";
?>Output (to browser):
<h1>Oops! Something went wrong.</h1><p>We've been notified and are looking into it. Please try again later.</p>An error message would also appear in the PHP error log.
Error vs Exception in Modern PHP
Section titled “Error vs Exception in Modern PHP”In PHP 7 and later, many traditional fatal errors (like calling a non-existent function or type errors when strict types are enabled) now throw Error exceptions. Both Error and Exception implement the Throwable interface.
This means you can often catch these critical errors using catch (Error $e) or catch (Throwable $t).
Generally:
Exception: Used for errors in the application logic that can potentially be handled (e.g., invalid input, file not found, failed database query).Error: Used for internal PHP errors (e.g., type errors, parsing errors, undefined functions). Catching these is less common, usually indicating a programming mistake that should be fixed, but can be useful for graceful shutdown or logging.
Your catch blocks can catch Throwable to handle both Error and Exception types if needed, especially in global handlers or finally blocks.
<?phpdeclare(strict_types=1);
try { // This will throw a TypeError in PHP 7+ strlen(123);} catch (TypeError $e) { echo "Caught TypeError: " . $e->getMessage();} catch (Throwable $t) { // Fallback for any other Throwable (Error or Exception) echo "Caught Throwable: " . $t->getMessage();}?>