- Tác giả

- Name
- Nguyễn Đức Xinh
- Ngày xuất bản
- Ngày xuất bản
PHP Throwable, Exception và Error là gì? So sánh chi tiết từ cơ bản đến nâng cao
Trong quá trình phát triển ứng dụng PHP, xử lý lỗi (error handling) là một trong những kỹ năng quan trọng nhất để xây dựng hệ thống ổn định, dễ bảo trì và production-ready.
Từ PHP 7 trở đi, cơ chế xử lý lỗi đã thay đổi đáng kể với sự xuất hiện của Throwable. Rất nhiều developer vẫn nhầm lẫn giữa: Throwable, Exception và Error.
Vậy chúng khác nhau như thế nào? Khi nào nên dùng Exception, khi nào nên catch Throwable? Error có nên xử lý hay không?
Bài viết này sẽ phân tích chi tiết từ cơ bản đến nâng cao, kèm ví dụ thực tế và best practices trong môi trường production.
1. Tổng quan về cơ chế xử lý lỗi trong PHP
Trước PHP 7
Ở PHP 5:
- Chỉ có
Exceptionlà có thểtry...catch - Fatal error không catch được
- Script sẽ dừng ngay lập tức
Ví dụ:
try {
undefinedFunction();
} catch (Exception $e) {
echo "Caught";
}
Trong PHP 5 → script sẽ crash. Không catch được.
Sau PHP 7
PHP 7 giới thiệu:
Throwable(interface)Error(class)- Nhiều lỗi nghiêm trọng trở thành catchable
Từ đó, hệ thống được chuẩn hóa lại.
2. Throwable là gì?
Throwable là một interface gốc, đại diện cho tất cả những gì có thể được throw.
Cấu trúc kế thừa:
Throwable (interface)
│
├── Exception
│ └── RuntimeException
│ └── LogicException
│
└── Error
└── TypeError
└── ParseError
└── ArithmeticError
👉 Cả Exception và Error đều implement Throwable.
Điều đó có nghĩa:
catch (Throwable $e)
sẽ bắt được cả Exception và Error.
3. Exception là gì?
Exception(ngoại lệ) là một đối tượng đại diện cho lỗi hoặc điều kiện bất thường xảy ra trong quá trình thực thi chương trình, nhưng có thể được xử lý (catch) để tránh làm gián đoạn toàn bộ ứng dụng. Exception thường được sử dụng để báo hiệu các lỗi thuộc về business logic hoặc các tình huống mà ứng dụng có thể dự đoán và xử lý hợp lý.
Nói cách khác, Exception là cơ chế giúp lập trình viên kiểm soát luồng xử lý khi gặp lỗi, thay vì để chương trình bị dừng đột ngột. Exception có thể được tạo ra (throw) và bắt (catch) bằng các khối try-catch, giúp ứng dụng hoạt động ổn định và dễ debug.
Exception thường thuộc về:
- Lỗi thuộc business logic
- Lỗi có thể dự đoán được
- Lỗi có thể xử lý hợp lý
Ví dụ:
function divide(int $a, int $b): float {
if ($b === 0) {
throw new Exception("Cannot divide by zero");
}
return $a / $b;
}
try {
divide(10, 0);
} catch (Exception $e) {
echo $e->getMessage();
}
Output:
Cannot divide by zero
Khi nào dùng Exception?
- Validate input
- Kiểm tra quyền
- Kiểm tra rule nghiệp vụ
- Kiểm tra tài nguyên tồn tại
Exception thường thuộc tầng Application / Domain.
4. Error là gì?
Error là một đối tượng đại diện cho các lỗi nghiêm trọng hoặc sai sót trong quá trình thực thi chương trình, thường xuất phát từ lỗi lập trình hoặc lỗi hệ thống mà ứng dụng không thể tự xử lý một cách an toàn. Error thường liên quan đến các vấn đề ở tầng engine, runtime hoặc môi trường thực thi.
Khác với Exception, Error thường không thể dự đoán trước và không nên xử lý ở tầng business logic. Khi gặp Error, ứng dụng có thể bị dừng đột ngột hoặc cần có cơ chế xử lý ở entry point/global handler.
Các loại Error phổ biến trong PHP bao gồm:
- Parse Error (Syntax Error): Lỗi cú pháp khi code không đúng chuẩn PHP.
- Fatal Error: Lỗi nghiêm trọng khiến chương trình dừng ngay lập tức (ví dụ gọi hàm không tồn tại, lỗi memory).
- Warning: Cảnh báo về lỗi không nghiêm trọng, chương trình vẫn tiếp tục chạy nhưng có thể gây ra hành vi không mong muốn.
- Notice: Thông báo về lỗi nhỏ, thường là lỗi không ảnh hưởng lớn đến chương trình.
- TypeError: Lỗi kiểu dữ liệu.
- DivisionByZeroError: Lỗi chia cho 0.
- ArithmeticError: Lỗi toán học.
- AssertionError: Lỗi khi assert thất bại.
Ví dụ TypeError:
function sum(int $a, int $b): int {
return $a + $b;
}
try {
sum("abc", 5);
} catch (Error $e) {
echo $e->getMessage();
}
Output:
Argument 1 must be of type int, string given
Đây là TypeError, một subclass của Error.
5. Các phương thức (method) chung của Throwable, Exception, Error
Tất cả các đối tượng Throwable (bao gồm Exception và Error) đều có các method sau:
| Method | Ý nghĩa |
|---|---|
getMessage() |
Lấy thông báo lỗi (message) |
getCode() |
Lấy mã lỗi (nếu có) |
getFile() |
Lấy tên file nơi lỗi phát sinh |
getLine() |
Lấy số dòng nơi lỗi phát sinh |
getTrace() |
Lấy mảng trace (stack trace dạng array) |
getTraceAsString() |
Lấy stack trace dạng chuỗi |
getPrevious() |
Lấy exception trước đó (nếu có, dùng khi re-throw) |
__toString() |
Xuất thông tin lỗi dạng chuỗi đầy đủ |
Ví dụ sử dụng:
try {
throw new Exception("Lỗi demo", 123);
} catch (Throwable $e) {
echo $e->getMessage(); // Lỗi demo
echo $e->getCode(); // 123
echo $e->getFile(); // Tên file
echo $e->getLine(); // Số dòng
print_r($e->getTrace()); // Mảng trace
echo $e->getTraceAsString(); // Chuỗi trace
}
5. Bảng so sánh Throwable vs Exception vs Error
| Tiêu chí | Throwable | Exception | Error |
|---|---|---|---|
| Kiểu | Interface | Class | Class |
| Giới thiệu từ | PHP 7 | PHP 5 | PHP 7 |
| Có thể throw | Có | Có | Có |
| Có thể catch | Có | Có | Có |
| Thuộc business logic | Không | Có | Không |
| Thuộc lỗi engine | Có | Không | Có |
| Có nên custom? | Không | Có | Rất hiếm |
7. Catch Exception vs Catch Throwable
Chỉ catch Exception
try {
riskyFunction();
} catch (Exception $e) {
echo "Handled business error";
}
Không bắt được Error.
Catch Throwable
try {
riskyFunction();
} catch (Throwable $e) {
echo "Caught everything";
}
Bắt được:
- Exception
- Error
- TypeError
- ParseError
8. Best Practices
1️⃣ Tầng Business → Catch Exception cụ thể
try {
$service->process();
} catch (ValidationException $e) {
return response($e->getMessage(), 422);
}
Không nên catch Throwable ở tầng business.
2️⃣ Tầng Entry Point → Catch Throwable
Trong index.php:
set_exception_handler(function (Throwable $e) {
error_log($e);
http_response_code(500);
echo "Internal Server Error";
});
Đây là best practice production.
3️⃣ Không bao giờ swallow error
Sai:
catch (Throwable $e) {
// ignore
}
Phải log.
9. Một số Error phổ biến
TypeError
function test(int $a) {}
test("abc");
ParseError
try {
eval("echo 'Hello");
} catch (ParseError $e) {
echo "Parse error";
}
DivisionByZeroError
intdiv(10, 0);
10. Custom Exception (Best Practice)
Tạo Exception riêng cho domain:
class InsufficientBalanceException extends Exception {}
function withdraw($balance, $amount) {
if ($amount > $balance) {
throw new InsufficientBalanceException("Not enough money");
}
}
Giúp:
- Code rõ ràng
- Dễ maintain
- Dễ phân loại lỗi
11. Thiết kế Exception Hierarchy trong hệ thống lớn
Ví dụ:
AppException
│
├── ValidationException
├── AuthenticationException
└── BusinessRuleException
Lợi ích:
- Bắt lỗi theo tầng
- Phân loại rõ ràng
- Kiến trúc sạch (Clean Architecture)
12. Khi nào nên catch Throwable?
✔ Global error handler
✔ CLI tool
✔ Queue worker
✔ Framework core
❌ Không nên dùng khắp nơi trong service layer.
14. Ví dụ Production REST API
try {
$order = $service->create($data);
return json_encode($order);
} catch (ValidationException $e) {
return response($e->getMessage(), 422);
} catch (Throwable $e) {
error_log($e);
return response("Server Error", 500);
}
Tách biệt rõ:
- 4xx → Business
- 5xx → System
15. Re-throw Exception (Advanced)
try {
risky();
} catch (Exception $e) {
throw new RuntimeException("Wrapped", 0, $e);
}
Giữ được stack trace gốc.
16. Log trace lỗi (Logging Stack Trace)
Khi xử lý lỗi, việc log đầy đủ stack trace giúp dễ dàng debug và truy vết nguồn gốc lỗi. Bạn nên sử dụng getTraceAsString() hoặc log mảng trace với context.
Ví dụ log trace đơn giản:
catch (Throwable $e) {
error_log($e->getMessage());
error_log($e->getTraceAsString());
}
Ví dụ log trace với context (dùng Monolog hoặc framework):
catch (Throwable $e) {
$logger->critical($e->getMessage(), [
'trace' => $e->getTraceAsString(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'code' => $e->getCode(),
]);
}
16. Logging Strategy trong hệ thống lớn
Trong production:
- Catch cụ thể ở service layer
- Catch Throwable ở entry layer
- Log centralized (ELK, Sentry, CloudWatch…)
Ví dụ:
catch (Throwable $e) {
$logger->critical($e->getMessage(), [
'trace' => $e->getTraceAsString()
]);
}
17. Tổng kết quan trọng
✔ Throwable là interface gốc
✔ Exception dành cho business logic
✔ Error dành cho lỗi lập trình
✔ Catch Exception ở application layer
✔ Catch Throwable ở system boundary
✔ Không bao giờ nuốt lỗi
18. Kết luận
Hiểu rõ Throwable, Exception, và Error giúp bạn:
- Viết code an toàn hơn
- Thiết kế kiến trúc tốt hơn
- Debug dễ hơn
- Tối ưu production system
Ở level senior, việc phân biệt rõ business error và system error là cực kỳ quan trọng.
Nếu bạn nắm vững phần này, bạn đã nâng trình từ intermediate PHP developer lên professional level.
