Lỗi Gặp Phải
Fatal error: Uncaught DivisionByZeroError: Division by zero in /path/to/script.php on line 12
PHP 8 đã thay đổi cách xử lý phép chia cho số không. Trong PHP 7, chia cho số không chỉ tạo ra một Warning và chương trình vẫn tiếp tục — trả về false rồi chạy tiếp. PHP 8 biến nó thành một DivisionByZeroError được ném ra, và script dừng lại ngay lập tức. Nếu bạn vừa nâng cấp PHP gần đây, đó rất có thể là lý do tại sao thứ gì đó "chạy được" trước đây giờ lại đang crash trên môi trường production.
Nguyên Nhân Gốc Rễ
- Chia cho một biến đang chứa giá trị
0,null, hoặc chuỗi rỗng (chuỗi rỗng sẽ được ép kiểu thành0) - Toán tử modulo
%với số chia bằng không — cùng lỗi, khác toán tử - Số chia động đến từ input người dùng, truy vấn cơ sở dữ liệu, hoặc file config mà chưa được kiểm tra trước khi đưa vào phép tính
Cách Sửa Từng Bước
1. Kiểm tra số chia trước khi thực hiện phép chia
Chín trong mười trường hợp, một kiểm tra đơn giản trước khi chia là đủ. Hãy xác nhận số chia không phải là số không trước khi thực hiện phép tính.
<?php
// Bị crash khi $count là 0 — ví dụ: chưa có đơn hàng nào trong báo cáo doanh số
$average = $total / $count;
// Phiên bản an toàn
if ($count === 0) {
$average = 0; // hoặc null, hoặc ném một exception riêng cho nghiệp vụ
} else {
$average = $total / $count;
}
2. Dùng toán tử ternary cho cú pháp gọn hơn
Khi khối if/else cảm thấy rườm rà, một toán tử ternary hoạt động tốt không kém:
<?php
$average = $count !== 0 ? $total / $count : 0;
$remainder = $count !== 0 ? $total % $count : 0;
3. Dùng fdiv() cho phép chia số thực (PHP 8+)
fdiv() tuân theo chuẩn IEEE 754: chia cho số không và bạn nhận được INF, -INF, hoặc NAN — không bao giờ ném exception. Hàm này rất tiện trong các pipeline tính toán số khi bạn muốn xử lý kết quả bất thường sau đó thay vì phải kiểm tra từng số chia một.
<?php
$result = fdiv($total, $count); // trả về INF nếu $count === 0, không bao giờ ném exception
if (!is_finite($result)) {
$result = 0;
}
4. Bắt DivisionByZeroError trong các pipeline tính toán
Đôi khi bạn không thể dễ dàng kiểm tra số chia trước — chẳng hạn trong một chuỗi các giá trị được tính toán liên tiếp. Dùng try/catch giúp phần còn lại của ứng dụng vẫn chạy được dù một phép tính nào đó thất bại.
<?php
try {
$rate = $processed / $total;
} catch (\DivisionByZeroError $e) {
error_log('Division by zero: ' . $e->getMessage());
$rate = 0;
}
5. Kiểm tra dữ liệu đầu vào động ngay tại điểm nhận
Giá trị do người dùng gửi lên, dữ liệu POST từ form, và các cột trong cơ sở dữ liệu là nguồn phổ biến nhất tạo ra những số không bất ngờ. Hãy loại bỏ chúng trước khi chúng đến được phần logic tính toán của bạn.
<?php
$count = (int) $_POST['count'];
if ($count 'count must be greater than zero']);
exit;
}
$average = $total / $count; // an toàn — $count là số dương
6. Modulo cho số không — cùng lỗi, cùng cách sửa
Toán tử % cũng ném DivisionByZeroError. Cùng cách kiểm tra, khác toán tử:
<?php
// Bị crash
$remainder = $value % $divisor;
// Modulo số nguyên an toàn
$remainder = $divisor !== 0 ? $value % $divisor : 0;
// Với số thực, dùng fmod() — trả về NAN khi số chia là 0, không bao giờ ném exception
$remainder = fmod($value, $divisor);
Kiểm Tra Kết Quả
Lưu đoạn code này thành file test_division.php và chạy để xác nhận bản sửa hoạt động đúng:
<?php
$total = 100;
$count = 0;
$average = $count !== 0 ? $total / $count : 0;
echo "Average: $average\n"; // Average: 0
$result = fdiv($total, $count);
echo "fdiv result: $result\n"; // fdiv result: INF
echo "is_finite: " . (is_finite($result) ? 'yes' : 'no') . "\n"; // no
Chạy lệnh: php test_division.php. Không có lỗi fatal nghĩa là bạn đã xong.
Sự Khác Biệt Giữa PHP 7 và PHP 8
Đang nâng cấp từ PHP 7? Hành vi đã thay đổi theo hai cách:
- PHP 7:
$x / 0phát sinhWarning: Division by zerovà trả vềfalse;$x % 0đã némDivisionByZeroErrorrồi - PHP 8+: cả
/lẫn%đều némDivisionByZeroError
Code PHP 7 chỉ kiểm tra giá trị trả về là false sẽ bị lỗi trên PHP 8. Cách kiểm tra if ($divisor !== 0) hoạt động trên cả hai phiên bản — hãy dùng cách đó thay thế.
Mẹo Nhanh
- Dùng so sánh chặt
!== 0, không dùng so sánh lỏng!= 0— so sánh lỏng có thể hoạt động không như mong đợi vớinullvà chuỗi rỗng - Ép kiểu rõ ràng các giá trị lấy từ cơ sở dữ liệu trước khi chia:
(int) $row['count']hoặc(float) $row['amount'] intdiv()cũng némDivisionByZeroErrorkhi số chia bằng không — cùng cách kiểm tra áp dụng cho hàm này- Đặt
display_errors = Offtrên môi trường production và ghi log ra file; stack trace không bao giờ nên lộ ra cho người dùng cuối

