Bắt bug từ trong "trứng nước": PHPStan - Vệ sĩ thầm lặng nâng tầm mã nguồn PHP
Ở bài viết trước, chúng ta đã nhắc đến lệnh php -l như một công cụ kiểm tra cú pháp (Syntax Check) siêu tốc. Lệnh đó rất tuyệt để bắt những lỗi ngớ ngẩn như thiếu dấu chấm phẩy (;) hay quên đóng ngoặc.
Nhưng, thực tế phũ phàng là: Cú pháp đúng không có nghĩa là code sẽ chạy đúng!
Hãy thử nhìn vào đoạn code này:
function calculateDiscount(int $price) {
return $price * 0.1;
}
$total = "1000";
echo calculateDiscount($total);
Nếu bạn chạy php -l, nó sẽ báo Xanh (Ok) vì cú pháp hoàn hảo. Nhưng khi chạy thực tế trên PHP 8, nó sẽ văng thẳng một cái Fatal Error: TypeError: Argument 1 passed to calculateDiscount() must be of the type int, string given.
Làm sao để phát hiện ra những lỗi sai kiểu dữ liệu, gọi nhầm tên hàm, hay thao tác trên một biến null mà không cần phải chạy code? Chào mừng bạn đến với thế giới của Phân tích tĩnh (Static Analysis) và ông hoàng trong làng PHP: PHPStan.
1. PHPStan thực chất là gì?
PHPStan (PHP Static Analysis Tool) là một công cụ giúp bạn tìm ra lỗi trong code bằng cách "đọc" và phân tích mã nguồn một cách cực kỳ thông minh, mà hoàn toàn không cần thực thi (chạy) bất kỳ một dòng code nào.
Hãy tưởng tượng PHPStan giống như một ông Senior Tech Lead cực kỳ khó tính. Ông ấy sẽ soi xét từng dòng code của bạn, dò theo từng đường đi của biến, kiểm tra xem bạn truyền tham số có đúng kiểu không, hàm này trả về array mà bạn lại đi gọi method của object thì có chết không. Tất cả diễn ra trong vòng vài giây.
2. Sự khác biệt giữa PHPStan và Unit Test
Nhiều anh em thắc mắc: "Tôi viết Unit Test phủ 100% rồi, cần gì PHPStan nữa?"
- Unit Test: Kiểm tra xem logic nghiệp vụ của bạn có đúng không. (Ví dụ: Nhập 10.000đ, giảm 10% thì phải ra 9.000đ).
- PHPStan: Kiểm tra xem cấu trúc mã nguồn của bạn có hợp lệ không. (Ví dụ: Bạn không thể nhân một chuỗi (string) với một mảng (array) được).
Unit Test đòi hỏi bạn phải tốn hàng giờ để viết kịch bản. Còn PHPStan, bạn chỉ cần cài đặt, gõ một lệnh, và nó tự động lôi ra hàng tá lỗi tiềm ẩn (những trường hợp edge-cases mà bạn thậm chí chưa bao giờ nghĩ tới khi viết Test).
3. Những lỗi kinh điển mà PHPStan sẽ cứu rỗi bạn
Nếu đưa PHPStan vào một dự án cũ, chắc chắn bạn sẽ phải toát mồ hôi hột khi thấy nó in ra hàng trăm lỗi mà dự án vẫn đang "may mắn" chạy được bấy lâu nay.
- Gọi phương thức/thuộc tính không tồn tại: Bạn gõ nhầm
$user->getNname(), PHPStan sẽ báo lỗi ngay lập tức vì classUserkhông có hàm nào tên như vậy. - Ác mộng NullPointerException:
$user = UserRepository::find($id); // Hàm này có thể trả về User hoặc null
echo $user->getName();
PHPStan sẽ chặn bạn lại ngay: "Ê, $user có thể là null kìa. Bạn phải check if ($user !== null) trước khi gọi getName() chứ!".
- Sai kiểu dữ liệu (Type Hinting): Hàm yêu cầu mảng, bạn truyền vào chuỗi. PHPStan báo lỗi.
4. Thiết kế thiên tài: "Quy luật của các cấp độ" (Rule Levels)
Điểm ăn tiền nhất khiến PHPStan được cộng đồng đón nhận cuồng nhiệt chính là hệ thống Level (từ 0 đến 9). Nếu ném một công cụ kiểm tra quá khắt khe vào một dự án Legacy (code cũ nát bét), dev sẽ nản và bỏ cuộc ngay.
PHPStan cho phép bạn đưa vào dự án một cách từ từ:
- Level 0: Chỉ kiểm tra những lỗi sơ đẳng nhất (cú pháp cơ bản, gọi hàm không tồn tại, sai số lượng tham số).
- Level 5: Bắt đầu soi xét kỹ về kiểu dữ liệu (Types) truyền vào và trả về.
- Level 8: Khởi động tính năng phát hiện gọi hàm trên các biến có thể là
null. - Level 9: Chế độ "Đồ tể". Khắt khe nhất, yêu cầu mọi biến phải được định nghĩa kiểu dữ liệu rõ ràng, không chấp nhận kiểu
mixed. Đạt được Level 9 nghĩa là code PHP của bạn chặt chẽ không khác gì Java hay Golang.
5. Áp dụng vào thực tế cực kỳ đơn giản
Việc cài đặt chỉ mất đúng 1 dòng lệnh Composer:
composer require --dev phpstan/phpstan
Sau đó, chạy lệnh để quét thư mục code (ví dụ thư mục app, quét ở mức độ 5):
vendor/bin/phpstan analyse app --level 5
Thường thì anh em sẽ tạo một file cấu hình phpstan.neon ở thư mục gốc để lưu sẵn các thiết lập, sau đó đưa luôn lệnh chạy PHPStan vào hệ thống CI/CD (GitHub Actions / GitLab CI). Cứ mỗi lần có ai đó Push code hoặc tạo Pull Request, con bot sẽ tự động chạy PHPStan. Nếu báo lỗi, chặn không cho Merge code.
Lời kết
PHP vốn dĩ là một ngôn ngữ linh hoạt (dynamic typing), đó là điểm mạnh nhưng cũng là điểm yếu chí mạng gây ra những lỗi Runtime (lỗi khi đang chạy) vô cùng khó đỡ. Bằng cách sử dụng PHPStan, bố đời đang khoác lên dự án PHP của mình một bộ giáp của các ngôn ngữ Strict Typing.
Nó ép chúng ta phải viết code cẩn thận hơn, sử dụng Type Hinting rõ ràng hơn, và quan trọng nhất là mang lại một giấc ngủ ngon mỗi khi Deploy lên Production. Đừng chần chừ nữa, hãy cài ngay PHPStan ở Level 0 cho dự án của mình để xem có con "bug" nào đang trốn trong đó không nhé!
All rights reserved