Back to Tutorials
PHPBeginner
Modern Exception Handling
Learn how to manage errors and exceptions gracefully in modern PHP applications.
Alex Rivers
December 28, 2025
25 min read
Modern Exception Handling in PHP
Learn how to handle errors and exceptions gracefully in modern PHP applications for better debugging and user experience.
Understanding Exceptions
Basic Try-Catch
php
1try {
2 $result = riskyOperation();
3} catch (Exception $e) {
4 echo "Error: " . $e->getMessage();
5}Multiple Catch Blocks
php
1try {
2 $user = findUser($id);
3 $user->sendEmail();
4} catch (UserNotFoundException $e) {
5 echo "User not found";
6} catch (EmailException $e) {
7 echo "Failed to send email";
8} catch (Exception $e) {
9 echo "An unexpected error occurred";
10}Creating Custom Exceptions
Basic Custom Exception
php
1class UserNotFoundException extends Exception
2{
3 public function __construct(int $userId)
4 {
5 parent::__construct("User with ID $userId not found");
6 }
7}Advanced Custom Exception
php
1class ValidationException extends Exception
2{
3 public function __construct(
4 private array $errors,
5 string $message = "Validation failed"
6 ) {
7 parent::__construct($message);
8 }
9
10 public function getErrors(): array
11 {
12 return $this->errors;
13 }
14}
15
16// Usage
17throw new ValidationException([
18 'email' => 'Invalid email format',
19 'password' => 'Password too short'
20]);Exception Hierarchy
php
1class AppException extends Exception {}
2
3class DatabaseException extends AppException {}
4class ValidationException extends AppException {}
5class AuthenticationException extends AppException {}
6
7// Specific exceptions
8class UserNotFoundException extends DatabaseException {}
9class InvalidCredentialsException extends AuthenticationException {}Practical Examples
User Service with Exceptions
php
1class UserService
2{
3 public function __construct(
4 private Database $db,
5 private Validator $validator
6 ) {}
7
8 public function register(array $data): User
9 {
10 // Validate input
11 $errors = $this->validator->validate($data, [
12 'email' => 'required|email|unique:users',
13 'password' => 'required|min:8'
14 ]);
15
16 if (!empty($errors)) {
17 throw new ValidationException($errors);
18 }
19
20 // Create user
21 try {
22 $user = new User(
23 email: $data['email'],
24 password: password_hash($data['password'], PASSWORD_ARGON2ID)
25 );
26
27 $this->db->execute(
28 'INSERT INTO users (email, password) VALUES (?, ?)',
29 [$user->email, $user->password]
30 );
31
32 $user->id = $this->db->lastInsertId();
33
34 return $user;
35 } catch (PDOException $e) {
36 throw new DatabaseException('Failed to create user', 0, $e);
37 }
38 }
39
40 public function login(string $email, string $password): User
41 {
42 $users = $this->db->query(
43 'SELECT * FROM users WHERE email = ?',
44 [$email]
45 );
46
47 if (empty($users)) {
48 throw new InvalidCredentialsException('Invalid email or password');
49 }
50
51 $user = User::fromArray($users[0]);
52
53 if (!password_verify($password, $user->password)) {
54 throw new InvalidCredentialsException('Invalid email or password');
55 }
56
57 return $user;
58 }
59}Controller with Exception Handling
php
1class UserController
2{
3 public function __construct(private UserService $userService) {}
4
5 public function register(): void
6 {
7 try {
8 $user = $this->userService->register($_POST);
9
10 http_response_code(201);
11 echo json_encode([
12 'success' => true,
13 'user' => [
14 'id' => $user->id,
15 'email' => $user->email
16 ]
17 ]);
18 } catch (ValidationException $e) {
19 http_response_code(422);
20 echo json_encode([
21 'success' => false,
22 'errors' => $e->getErrors()
23 ]);
24 } catch (DatabaseException $e) {
25 http_response_code(500);
26 echo json_encode([
27 'success' => false,
28 'message' => 'Server error. Please try again later.'
29 ]);
30
31 // Log the actual error
32 error_log($e->getMessage());
33 }
34 }
35}Global Exception Handler
php
1set_exception_handler(function (Throwable $e) {
2 // Log the exception
3 error_log($e->getMessage() . " in " . $e->getFile() . ":" . $e->getLine());
4
5 // Send appropriate response
6 http_response_code(500);
7
8 if ($e instanceof ValidationException) {
9 http_response_code(422);
10 echo json_encode([
11 'error' => 'Validation failed',
12 'details' => $e->getErrors()
13 ]);
14 } elseif ($e instanceof AuthenticationException) {
15 http_response_code(401);
16 echo json_encode(['error' => 'Authentication failed']);
17 } else {
18 echo json_encode(['error' => 'Internal server error']);
19 }
20});Finally Block
php
1$file = fopen('data.txt', 'r');
2
3try {
4 $content = fread($file, filesize('data.txt'));
5 processContent($content);
6} catch (Exception $e) {
7 echo "Error processing file: " . $e->getMessage();
8} finally {
9 // Always executes, even if exception is thrown
10 fclose($file);
11}Best Practices
1. Be Specific
php
1// BAD - Too generic
2throw new Exception('Error');
3
4// GOOD - Specific and informative
5throw new UserNotFoundException($userId);2. Don't Catch What You Can't Handle
php
1// BAD - Swallowing exceptions
2try {
3 criticalOperation();
4} catch (Exception $e) {
5 // Do nothing
6}
7
8// GOOD - Let it bubble up or handle properly
9try {
10 criticalOperation();
11} catch (Exception $e) {
12 log($e);
13 throw $e; // Re-throw if you can't handle it
14}3. Provide Context
php
1// BAD
2throw new Exception('Invalid data');
3
4// GOOD
5throw new ValidationException([
6 'field' => 'email',
7 'value' => $email,
8 'rule' => 'email format',
9 'message' => 'Email must be valid'
10]);Logging Exceptions
php
1class Logger
2{
3 public function logException(Throwable $e): void
4 {
5 $message = sprintf(
6 "[%s] %s: %s in %s:%d\nStack trace:\n%s",
7 date('Y-m-d H:i:s'),
8 get_class($e),
9 $e->getMessage(),
10 $e->getFile(),
11 $e->getLine(),
12 $e->getTraceAsString()
13 );
14
15 error_log($message);
16 }
17}Conclusion
Proper exception handling makes your code more robust, easier to debug, and provides better user experience. Always use specific exceptions, handle errors gracefully, and log appropriately!