Nếu bạn vừa viết những dòng PHP đầu tiên, rất có thể bạn đã gặp ít nhất một trong bảy sai lầm thường gặp khi viết PHP dưới đây. Chúng tôi làm nghề này đủ lâu để thấy chúng lặp đi lặp lại trong hầu hết dự án, từ website bán hàng nhỏ đến hệ thống quản trị của doanh nghiệp. Bài này chỉ ra từng lỗi, vì sao nó nguy hiểm và cách sửa bằng kiến thức cơ bản.
Sai lầm thường gặp về PHP là gì và vì sao cần quan tâm?
Đây là bảy lỗi viết code PHP xuất hiện nhiều nhất trong thực tế. Chúng thường dẫn đến ba hệ quả chính: lỗ hổng bảo mật, hệ thống chạy chậm và mã nguồn khó sửa về sau.
Chúng tôi chọn bảy mục này theo ba tiêu chí. Thứ nhất là tần suất gặp trong các dự án thực tế. Thứ hai là mức độ ảnh hưởng nếu để lâu. Thứ ba là khả năng sửa bằng kiến thức nền tảng.
Bài này phù hợp với lập trình viên PHP từ mới đến trung cấp, và cả chủ website đang tự quản lý mã nguồn. Nếu bạn thuê người khác làm, nội dung vẫn giúp bạn biết cách đặt câu hỏi khi nhận bàn giao.
Ghép chuỗi trực tiếp vào câu SQL: cánh cửa cho tấn công SQL Injection
Đây là lỗi thuộc nhóm nguy hiểm nhất trong danh sách. Nhiều người nối thẳng dữ liệu người dùng gửi lên vào câu truy vấn, khiến máy chủ coi mọi nội dung gõ vào ô tìm kiếm là lệnh hợp lệ.
Dấu hiệu nhận biết khá đơn giản: nếu bạn thấy câu lệnh kiểu "SELECT * FROM users WHERE id = " . $_GET['id'] thì đó là tín hiệu cảnh báo. Kẻ xấu chỉ cần gõ thêm vài ký tự là có thể có khả năng đọc dữ liệu trong bảng.
Cách khắc phục là dùng prepared statement với PDO hoặc MySQLi. Khi đó dữ liệu được gửi tách riêng khỏi câu lệnh. Bạn vẫn nên kiểm tra dữ liệu ở tầng ứng dụng, ví dụ kiểm tra số nguyên, độ dài chuỗi và định dạng email.
Biến toàn cục và hàm global: khi mọi thứ trộn lẫn vào nhau
Biến toàn cục giống như để chìa khóa nhà ở giữa phòng khách, ai đi qua cũng cầm được. Khi một hàm đọc và ghi biến global, bạn sẽ khó biết giá trị đến từ đâu.
Tác động thường gặp là sửa code ở chỗ này lại làm hỏng chức năng ở chỗ khác. Lỗi kiểu này đặc biệt khó tìm vì không báo ngay.
Cách làm tốt hơn là truyền tham số rõ ràng vào hàm. Khi dự án lớn hơn, bạn có thể gom logic vào class và dùng dependency injection. Với script vài chục dòng, dùng biến toàn cục đôi khi vẫn chấp nhận được, nên đừng áp kiến trúc phức tạp cho bài toán đơn giản.
Để lộ thông báo lỗi ra ngoài: kẻ xấu đọc được bản đồ hệ thống
Khi có lỗi, PHP thường in đường dẫn file, tên hàm và đôi khi cả thông tin kết nối database lên trình duyệt. Trên máy dev điều này hữu ích. Trên website đang chạy thật, nó giống như đưa bản vẽ nhà cho người lạ.
Chúng tôi khuyên tắt display_errors trên môi trường production, ghi lỗi vào file qua error_log, và chỉ dùng try-catch ở những chỗ thật sự cần xử lý. Bạn cần tách cấu hình dev và production để làm được điều này.
- Môi trường dev: hiển thị lỗi đầy đủ để sửa nhanh.
- Môi trường production: chỉ ghi log, người dùng thấy trang thông báo chung.
Hàm mysql_* đã cũ: code chạy được hôm nay nhưng có thể lỗi ngày mai
Đây là lỗi thường gặp ở những dự án viết từ lâu rồi đem nâng cấp. Các hàm mysql_* đã bị loại bỏ từ PHP 7, nên khi chuyển lên phiên bản mới, trang web sẽ báo lỗi ngay.
Hướng xử lý là chuyển sang MySQLi hoặc PDO, đồng thời kiểm tra phiên bản PHP trên hosting của bạn. Với code cũ nhiều file, việc này có thể tốn thời gian. Bạn cần kiểm thử kỹ từng chức năng sau khi sửa.
Lẫn lộn xử lý nghiệp vụ với giao diện trong code PHP
Chúng tôi từng gặp một dự án mà trong cùng một file vừa có câu SQL, vừa có vòng lặp tính tiền, lại vừa có thẻ HTML. Mỗi lần đổi màu nút bấm, nhân viên đều lo làm hỏng phần tính tiền.
Cách tốt hơn là áp dụng mô hình MVC, hoặc tối thiểu tách hàm xử lý ra khỏi file template. Kiến trúc tách lớp làm số file tăng lên, nên phù hợp hơn với dự án vừa và lớn, còn với trang giới thiệu vài trang thì chưa cần thiết.
Copy thư viện bằng tay và quên cập nhật bản vá
Nhiều người tải thư viện về rồi chép thẳng vào thư mục dự án. Cách này đơn giản, nhưng khi thư viện có lỗ hổng bảo mật, bạn không biết mình đang dùng bản nào và phải cập nhật thủ công.
Composer giải quyết việc này. Bạn khai báo thư viện trong file composer.json, Composer tự khóa phiên bản và tải đúng bản cần thiết. Người mới có thể thấy hơi lạ lúc đầu vì phải làm quen với autoload, nhưng đầu tư vài giờ học là đáng.
Áp dụng bảy điểm trên vào dự án PHP thực tế
Không nên sửa tất cả cùng lúc. Chúng tôi thường ưu tiên theo thứ tự: lỗ hổng bảo mật trước, sau đó đến cấu trúc code, cuối cùng mới tới hiệu năng.
Mỗi phần đã sửa nên có vài bài kiểm thử tự động để hạn chế phát sinh lỗi mới. Hãy làm từng bước nhỏ, kiểm tra, rồi mới tiếp tục.
Bảng tổng hợp bảy sai lầm và mức ưu tiên sửa
Bảng dưới đây là cách chúng tôi phân loại khi nhận một dự án cũ. Mức ưu tiên chỉ mang tính tham khảo, bạn cần đánh giá lại theo hoàn cảnh cụ thể của hệ thống mình.
| Sai lầm | Rủi ro chính | Đối tượng dễ gặp | Mức ưu tiên |
|---|---|---|---|
| Ghép chuỗi vào SQL | Có thể lộ dữ liệu, bị thao túng truy vấn | Người mới, code cũ | Rất cao |
| Thông báo lỗi lộ ra ngoài | Lộ cấu trúc hệ thống | Website tự quản | Cao |
| Hàm mysql_* cũ | Trang lỗi khi nâng PHP | Dự án từ nhiều năm trước | Cao |
| Lạm dụng biến global | Lỗi khó lần theo | Script lớn dần theo thời gian | Trung bình |
| Trộn logic và giao diện | Sửa chỗ này dễ hỏng chỗ khác | Dự án không có người rành kiến trúc | Trung bình |
| Copy thư viện thủ công | Khó vá lỗ hổng kịp thời | Người mới tự cài đặt | Trung bình |
| Bỏ qua kiểm thử | Dễ phát sinh lỗi mới sau mỗi lần sửa | Đội nhỏ, deadline gấp | Thấp đến trung bình |
Câu hỏi thường gặp về các sai lầm PHP
PHP phiên bản nào nên dùng để tránh các lỗi cũ?
Bạn nên chọn phiên bản PHP còn được hỗ trợ bảo mật, thường là các bản 8.x mới nhất mà hosting của bạn hỗ trợ. Bản cũ hơn có thể vẫn chạy, nhưng sẽ không nhận bản vá mới.
Trước khi nâng cấp, hãy kiểm tra thư viện và plugin đang dùng có tương thích không. Đây là chỗ hay phát sinh lỗi nhất.
Làm sao biết code của mình có lỗ hổng SQL Injection?
Cách nhanh nhất là tìm trong code các câu lệnh có chứa $_GET, $_POST hoặc $_REQUEST nối trực tiếp vào chuỗi SQL. Nếu có, đó là chỗ cần sửa ngay.
Bạn cũng có thể nhờ người khác review code hoặc dùng công cụ quét bảo mật. Kết quả quét chỉ là gợi ý, bạn vẫn cần đọc lại từng đoạn.
Có cần học MVC ngay từ đầu khi mới học PHP không?
Không bắt buộc. Bạn nên nắm vững cú pháp, hàm, mảng và cách kết nối database trước. Khi đã viết được vài dự án nhỏ và thấy code khó quản lý, học MVC sẽ dễ hiểu hơn nhiều.
Dùng công cụ nào để kiểm tra lỗi và chất lượng code PHP?
Một số công cụ phổ biến là PHPStan hoặc Psalm để phân tích tĩnh, và PHP_CodeSniffer để kiểm tra chuẩn viết code. Cài đặt không khó, nhưng bạn cần thời gian làm quen với các cảnh báo. Tính năng có thể thay đổi theo phiên bản, nên hãy xem tài liệu chính thức trước khi dùng.
Nên sửa code cũ hay viết lại từ đầu khi phát hiện nhiều sai lầm?
Với đa số trường hợp, chúng tôi khuyên sửa từng phần thay vì viết lại. Viết lại nghe hấp dẫn nhưng thường tốn nhiều thời gian hơn dự kiến và dễ phát sinh lỗi mới.
Chỉ nên cân nhắc viết lại khi hệ thống quá cũ, không còn ai hiểu, và việc sửa từng phần tốn nhiều công sức hơn việc viết lại. Hãy đánh giá cụ thể trước khi quyết định.
Lời khuyên cuối cùng
Nếu chỉ làm một việc sau khi đọc bài này, hãy rà lại mọi câu SQL trong dự án và chuyển sang prepared statement. Đây thường là bước mang lại tác động lớn nhất với chi phí thấp nhất. Sau đó, bật ghi log lỗi trên môi trường thật và dần làm theo các điểm còn lại.
Bạn có thể tham khảo thêm kiến thức PHP và công cụ lập trình cho người mới để nắm nền tảng trước khi áp dụng thay đổi lớn. Nếu đang phân vân nên học framework nào tiếp theo, bạn có thể đọc so sánh framework PHP cho người mới. Và với chủ website đang vận hành mã nguồn tự phát triển, top 10 kinh nghiệm thực tế về mã nguồn mở cho developer PHP là gợi ý đáng đọc tiếp.
Nếu muốn đối chiếu thêm kiến thức lập trình nền tảng, bạn có thể tham khảo visualcpp.net.
