Session 4.6 – Error Handling and Exceptions
Module 4: Server-Side Scripting – PHP | Duration: 1 hr
Learning Objectives
By the end of this session, students will be able to:
- Understand different types of errors in PHP
- Implement proper error handling mechanisms
- Use try-catch blocks for exception handling
- Create custom exception classes
- Configure error reporting and logging
- Apply error handling best practices for production applications
Introduction
Error handling is crucial for building robust and maintainable PHP applications. Proper error management helps identify issues during development and provides graceful degradation in production.
Why Error Handling Matters
- User Experience: Show friendly messages instead of technical errors
- Security: Prevent exposure of sensitive system information
- Debugging: Quickly identify and fix issues
- Maintenance: Track and monitor application health
- Recovery: Implement fallback mechanisms
PHP Error Types
E_ERROR
Fatal run-time errors. Script execution stops.
Example: Calling undefined function
fatal_function();
E_WARNING
Run-time warnings. Script continues.
Example: Include missing file
include('nonexistent.php');
E_NOTICE
Run-time notices. Minor issues.
Example: Undefined variable
echo $undefined_var;
E_PARSE
Compile-time parse errors.
Example: Syntax error
echo "Hello World" // Missing semicolon
E_STRICT
Code suggestions for forward compatibility.
Example: Deprecated features
Using old-style constructors
E_DEPRECATED
Warnings about deprecated features.
Example: Using deprecated functions
mysql_connect() // Use mysqli instead
Error Handling Methods
Error Reporting Configuration
<?php
// Development environment - show all errors
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
// Production environment - log errors, don't display
error_reporting(E_ALL);
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/path/to/error.log');
// Report all errors except notices
error_reporting(E_ALL & ~E_NOTICE);
// Report only errors and warnings
error_reporting(E_ERROR | E_WARNING);
?>Custom Error Handler
<?php
// Define custom error handler
function customErrorHandler($errno, $errstr, $errfile, $errline) {
// Error type mapping
$errorTypes = array(
E_ERROR => 'Error',
E_WARNING => 'Warning',
E_NOTICE => 'Notice',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice'
);
$errorType = $errorTypes[$errno] ?? 'Unknown Error';
// Log error
$message = "[$errorType] $errstr in $errfile on line $errline";
error_log($message);
// Display user-friendly message (production)
if (ini_get('display_errors') == 0) {
echo "<div class='alert alert-danger'>An error occurred. Please try again later.</div>";
} else {
// Development - show detailed error
echo "<div class='alert alert-danger'>";
echo "<strong>$errorType:</strong> $errstr<br>";
echo "<strong>File:</strong> $errfile<br>";
echo "<strong>Line:</strong> $errline";
echo "</div>";
}
// Don't execute PHP internal error handler
return true;
}
// Set custom error handler
set_error_handler('customErrorHandler');
// Trigger errors for testing
echo $undefined; // E_NOTICE
include('nonexistent.php'); // E_WARNING
?>Trigger Custom Errors
<?php
function divide($a, $b) {
if ($b == 0) {
trigger_error("Division by zero attempted", E_USER_WARNING);
return false;
}
return $a / $b;
}
$result = divide(10, 0); // Triggers warning
// Different error levels
trigger_error("This is a notice", E_USER_NOTICE);
trigger_error("This is a warning", E_USER_WARNING);
trigger_error("This is an error", E_USER_ERROR); // Fatal - stops execution
?>Exception Handling
Try-Catch Blocks
<?php
// Basic try-catch
try {
$result = 10 / 0;
echo "This won't execute";
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
// Multiple catch blocks
try {
$pdo = new PDO("mysql:host=localhost;dbname=test", "user", "pass");
} catch (PDOException $e) {
echo "Database Error: " . $e->getMessage();
} catch (Exception $e) {
echo "General Error: " . $e->getMessage();
}
// Try-catch-finally
try {
$file = fopen("data.txt", "r");
// Process file
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
} finally {
// Always executed
if (isset($file)) {
fclose($file);
}
echo "Cleanup completed";
}
?>Throwing Exceptions
<?php
function divide($a, $b) {
if ($b == 0) {
throw new Exception("Division by zero is not allowed");
}
return $a / $b;
}
try {
echo divide(10, 2); // 5
echo divide(10, 0); // Throws exception
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
// Throw with error code
function validateAge($age) {
if ($age < 0) {
throw new Exception("Age cannot be negative", 1001);
}
if ($age < 18) {
throw new Exception("Must be 18 or older", 1002);
}
return true;
}
try {
validateAge(15);
} catch (Exception $e) {
echo "Error Code " . $e->getCode() . ": " . $e->getMessage();
}
?>Exception Object Methods
<?php
try {
throw new Exception("Something went wrong");
} catch (Exception $e) {
echo "Message: " . $e->getMessage() . "\n";
echo "Code: " . $e->getCode() . "\n";
echo "File: " . $e->getFile() . "\n";
echo "Line: " . $e->getLine() . "\n";
echo "Trace: " . $e->getTraceAsString() . "\n";
echo "String: " . $e->__toString() . "\n";
}
// Get trace as array
$trace = $e->getTrace();
foreach ($trace as $frame) {
echo "Function: " . $frame['function'];
echo "File: " . $frame['file'];
echo "Line: " . $frame['line'];
}
?>Re-throwing Exceptions
<?php
function processData($data) {
try {
// Some operation
if (empty($data)) {
throw new Exception("Data is empty");
}
} catch (Exception $e) {
// Log the error
error_log($e->getMessage());
// Re-throw to calling function
throw $e;
}
}
try {
processData([]);
} catch (Exception $e) {
echo "Caught re-thrown exception: " . $e->getMessage();
}
?>Custom Exception Classes
Creating Custom Exceptions
<?php
// Basic custom exception
class DatabaseException extends Exception {
public function errorMessage() {
return "Database Error [{$this->code}]: {$this->message} in {$this->file} on line {$this->line}";
}
}
// Custom exception with additional properties
class ValidationException extends Exception {
private $field;
private $value;
public function __construct($message, $field, $value, $code = 0, Exception $previous = null) {
parent::__construct($message, $code, $previous);
$this->field = $field;
$this->value = $value;
}
public function getField() {
return $this->field;
}
public function getValue() {
return $this->value;
}
public function getFullError() {
return "Validation failed for field '{$this->field}' with value '{$this->value}': {$this->message}";
}
}
// Use custom exceptions
try {
throw new ValidationException("Invalid email format", "email", "notanemail", 1001);
} catch (ValidationException $e) {
echo $e->getFullError();
echo "\nField: " . $e->getField();
echo "\nValue: " . $e->getValue();
}
?>Exception Hierarchy
<?php
// Base exception class
class ApplicationException extends Exception {}
// Specific exception types
class DatabaseException extends ApplicationException {}
class ValidationException extends ApplicationException {}
class AuthenticationException extends ApplicationException {}
class AuthorizationException extends ApplicationException {}
// Even more specific
class DatabaseConnectionException extends DatabaseException {}
class DatabaseQueryException extends DatabaseException {}
// Usage with multiple catch blocks
try {
// throw new DatabaseQueryException("Query failed");
// throw new ValidationException("Invalid input");
throw new AuthenticationException("User not authenticated");
} catch (DatabaseException $e) {
echo "Database issue: " . $e->getMessage();
// Handle database errors
} catch (ValidationException $e) {
echo "Validation issue: " . $e->getMessage();
// Handle validation errors
} catch (AuthenticationException $e) {
echo "Auth issue: " . $e->getMessage();
// Redirect to login
} catch (ApplicationException $e) {
echo "Application issue: " . $e->getMessage();
// General application error
} catch (Exception $e) {
echo "Unexpected error: " . $e->getMessage();
// Catch-all for unexpected errors
}
?>Practical Example: User Registration
<?php
class UserService {
public function register($username, $email, $password) {
try {
// Validate username
if (strlen($username) < 3) {
throw new ValidationException("Username too short", "username", $username);
}
// Validate email
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new ValidationException("Invalid email format", "email", $email);
}
// Check if user exists
if ($this->userExists($username)) {
throw new ValidationException("Username already taken", "username", $username);
}
// Connect to database
$db = $this->getConnection();
if (!$db) {
throw new DatabaseConnectionException("Cannot connect to database");
}
// Insert user
$stmt = $db->prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)");
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
if (!$stmt->execute([$username, $email, $hashedPassword])) {
throw new DatabaseQueryException("Failed to create user");
}
return true;
} catch (ValidationException $e) {
// Log validation error
error_log($e->getFullError());
throw $e; // Re-throw to controller
} catch (DatabaseException $e) {
// Log database error with details
error_log("Database Error: " . $e->getMessage());
// Don't expose database details to user
throw new ApplicationException("Registration failed. Please try again later.");
} catch (Exception $e) {
// Catch unexpected errors
error_log("Unexpected error: " . $e->getMessage());
throw new ApplicationException("An unexpected error occurred");
}
}
}
// Controller handling
try {
$userService = new UserService();
$userService->register("john", "john@example.com", "password123");
echo "Registration successful!";
} catch (ValidationException $e) {
echo "<div class='alert alert-warning'>{$e->getFullError()}</div>";
} catch (ApplicationException $e) {
echo "<div class='alert alert-danger'>{$e->getMessage()}</div>";
} catch (Exception $e) {
echo "<div class='alert alert-danger'>System error. Please contact support.</div>";
error_log("Critical error: " . $e->getMessage());
}
?>Error Handling Best Practices
Do's
- Use exceptions for exceptional conditions
- Create specific exception classes
- Log all errors with context
- Show user-friendly messages in production
- Use finally blocks for cleanup
- Catch specific exceptions first
- Document what exceptions functions throw
- Use meaningful error messages
- Implement error monitoring tools
- Test error handling paths
Don'ts
- Don't display errors in production
- Don't ignore exceptions (empty catch)
- Don't use exceptions for flow control
- Don't expose system details to users
- Don't catch Exception without good reason
- Don't suppress errors with @
- Don't have unclear error messages
- Don't forget to log errors
- Don't throw generic exceptions
- Don't mix errors and exceptions poorly
Production Configuration
<?php
// config/production.php
// Error reporting
error_reporting(E_ALL);
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/var/log/php/error.log');
// Global exception handler
set_exception_handler(function($exception) {
// Log error with full details
error_log(sprintf(
"Uncaught Exception: %s in %s:%d\nStack trace:\n%s",
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
$exception->getTraceAsString()
));
// Show generic message to user
http_response_code(500);
include 'templates/error500.php';
});
// Global error handler
set_error_handler(function($errno, $errstr, $errfile, $errline) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
?>Session Summary
Key Points
- PHP has various error types: E_ERROR, E_WARNING, E_NOTICE, etc.
- Configure error_reporting() and ini_set() based on environment
- Use custom error handlers with set_error_handler()
- Exceptions provide structured error handling with try-catch-finally
- Create custom exception classes for specific error types
- Build exception hierarchies for better error categorization
- Always log errors; never expose sensitive information to users
- Use finally blocks for cleanup operations
- Implement appropriate error handling strategies for production
Next Session Preview
In the next session, we will explore PHP File Handling, learning how to read, write, and manipulate files and directories in PHP applications.