1. Định nghĩa
Error là lỗi nghiêm trọng của engine, do cú pháp, memory, fatal error và thường không catch được, nhằm báo lỗi system.
Exception là sự cố có thể dự đoán và xử lý được, thường là do business logic, input sai, DB lỗi, có thể sử dụng từ khoá try catch, nhằm điều khiển flow ứng dụng.
Ví dụ:
<?php
echo $undefinedVar; // Notice: undefined variable
throw new Exception("User not found");
2. Try … Catch
Cú pháp:
<?php
try {
// Code có khả năng gây lỗi
} catch (Exception $e) {
// Xử lý lỗi
} finally {
// Luôn luôn chạy dù có lỗi hay không
}
// chẳng hạn
try {
$result = 10 / 0;
} catch (DivisionByZeroError $e) {
echo "Không thể chia cho 0";
} finally {
echo "Kết thúc quá trình";
}
3. Custom Exception
Theo như định nghĩa ở mục 1, Exception dùng để điều khiển flow ứng dụng và thường mô tả lỗi do business logic hoặc input, nên những lỗi như vậy chúng ta thường tạo custom Exception cho app.
Ví dụ:
Mình đang xây dựng một ứng dụng mô phỏng máy POS trong siêu thị. Khi khách hàng thanh toán, hệ thống chỉ chấp nhận tiền Việt Nam Đồng (VND), và số tiền này phải đúng mệnh giá theo quy định (bội số của 1.000). Nhiệm vụ là viết đoạn code xử lý việc thanh toán và kiểm tra tính hợp lệ của số tiền khách đưa vào.
<?php
class InvalidCurrencyUnitException extends Exception
{
public function __construct()
{
parent::__construct('Số tiền không hợp lệ. VND phải chia hết cho 1,000.');
}
}
class InsufficientFundsException extends Exception
{
public function __construct()
{
parent::__construct('Không đủ tiền để thanh toán.');
}
}
class PosMachine
{
public function pay(int $userMoney): void
{
// VND chỉ có mệnh giá bội số 1.000
if ($userMoney % 1000 !== 0) {
throw new InvalidCurrencyUnitException();
}
// Giá trị món hàng cố định 100.000
if ($userMoney < 100000) {
throw new InsufficientFundsException();
}
}
}
4. Thực chiến
Trong lập trình, khi phát hiện dữ liệu hoặc trạng thái hệ thống không hợp lệ, ta nên dừng xử lý và ném exception ngay tại thời điểm phát hiện lỗi, thay vì để chương trình tiếp tục chạy rồi mới xử lý sau.
- Ngăn lỗi lan rộng
- Nếu tiếp tục xử lý với dữ liệu sai, các bước sau có thể tạo ra nhiều lỗi khó truy ngược.
- Throw sớm giúp “chặn” luồng xử lý trước khi hậu quả xảy ra.
- Dễ debug, dễ bảo trì
- Lỗi xuất hiện ngay đúng vị trí nguyên nhân.
- Developer không phải lần mò stack trace dài ngoằng.
- Giảm chi phí xử lý
- Không tốn CPU, IO để thực hiện những thao tác vô nghĩa phía sau.
- Bảo toàn tính toàn vẹn của dữ liệu
- Dữ liệu sai mà không được phát hiện sớm sẽ gây lỗi nghiêm trọng ở tầng DB, logic hoặc external API.
Ví dụ gây hao hiệu năng vô ích:
public function pay(int $userMoney)
{
// xử lý rất nhiều thứ gây tốn CPU, IO...
$this->logUserAction();
$this->calculateDiscount();
$this->printInvoice();
// VND chỉ có mệnh giá bội số 1.000
if ($userMoney % 1000 !== 0) {
throw new InvalidCurrencyUnitException();
}
// Giá trị món hàng cố định 100.000
if ($userMoney < 100000) {
throw new InsufficientFundsException();
}
}
Quy tắc vàng:
EXCEPTION NÉM CÀNG SỚM CÀNG TỐT