+3

Đọc "ruột" code PHP: Giải mã strcasecmp và bí ẩn của hàm #[Pure]

Chào anh em Viblo, lại là mình đây!

Bình thường chúng ta hay bàn về framework, về những công nghệ to tát, nhưng hôm nay có một chủ đề rất thú vị. Bạn vừa đưa cho mình một đoạn "Core Stub" (phần khai báo cấu trúc) của một hàm PHP chuẩn mực: strcasecmp.

Nhìn lướt qua, nó chỉ là một hàm so sánh chuỗi cỏn con. Nhưng nếu chúng ta soi kỹ từng dòng comment (docblock) và các attribute (thuộc tính) đính kèm, anh em sẽ học được rất nhiều về tư duy thiết kế hệ thống và những khái niệm cực kỳ hay ho của giới lập trình.

Hãy cùng mình "mổ xẻ" đoạn code này nhé!

1. Nhiệm vụ cốt lõi: Case-insensitive (Không phân biệt hoa thường)

Tác dụng chính của hàm strcasecmp($string1, $string2) là so sánh hai chuỗi với nhau mà bỏ qua sự khác biệt giữa chữ hoa và chữ thường.

Bối cảnh thực tế: Giả sử bạn đang làm tính năng nhập mã giảm giá (Promo Code) cho một trang e-commerce. Khách hàng có người nhập SALE2024, có người nhập sale2024, người lại nhập SaLe2024. Bạn không thể bắt ép khách hàng phải gõ đúng từng chữ hoa chữ thường được.

Thay vì phải gọi hàm strtolower() biến cả 2 về chữ thường rồi mới so sánh (gây tốn bộ nhớ tạo ra chuỗi mới), bạn chỉ cần dùng:

if (strcasecmp($inputCode, "SALE2024") === 0) {
    echo "Mã hợp lệ!";
}

Hàm này sẽ đi vòng lặp so sánh từng ký tự một ở cấp độ mã ASCII mà không cần tạo thêm bộ nhớ thừa, cực kỳ tối ưu.

2. Cú lừa nguy hiểm: Giá trị trả về (Return int)

Hãy nhìn vào đoạn comment này:

@return int less than 0 if str1 is less than str2; > 0 if str1 is greater than str2, and 0 if they are equal.

Đây là một trong những cái bẫy kinh điển nhất của dân mới học PHP. Hàm này KHÔNG trả về true/false (boolean). Nó trả về một số nguyên (int).

  • Nếu chuỗi 1 giống hệt chuỗi 2: Trả về 0.
  • Nếu chuỗi 1 đứng trước chuỗi 2 trong từ điển: Trả về số âm.
  • Nếu chuỗi 1 đứng sau chuỗi 2 trong từ điển: Trả về số dương.

Hậu quả nếu lơ đễnh: Rất nhiều anh em viết code thế này:

// SAI LẦM CHẾT NGƯỜI
if (strcasecmp("Admin", "admin")) {
    // Logic cho admin
}

Khổ nỗi, "Admin" và "admin" giống nhau nên hàm trả về 0. Trong điều kiện if, số 0 bị hiểu là false. Thế là đoạn code bên trong không bao giờ được chạy! Bạn bắt buộc phải so sánh nó với 0 bằng toán tử === 0 như ví dụ ở phần 1.

3. "Binary safe" (An toàn nhị phân) là cái quái gì?

Trong comment có ghi: "Binary safe case-insensitive string comparison".

Để hiểu từ này, chúng ta phải quay về cội nguồn của PHP là ngôn ngữ C. Trong C, một chuỗi được xác định điểm kết thúc bằng một ký tự đặc biệt gọi là Null byte (kí hiệu là \0).

Ngày xưa, các hàm xử lý chuỗi "không an toàn nhị phân" cứ đọc chuỗi đến khi gặp ký tự \0 là nó tự động ngắt, vì nó tưởng chuỗi đã hết. Nếu bạn truyền vào một file ảnh (vốn chứa rất nhiều ký tự \0 lung tung bên trong), hàm sẽ đọc sai bét.

strcasecmp là hàm Binary safe. Nó không quan tâm bên trong chuỗi của bạn có cái quái gì, nó chỉ dựa vào chiều dài (length) thực tế của biến để so sánh từng byte một. Nhờ vậy, bạn có thể dùng hàm này để so sánh cả những dữ liệu mã hóa, file nhị phân, hay những chuỗi có chứa ký tự đặc biệt mà không sợ hệ thống bị ngắt giữa chừng.

4. Dấu ấn hiện đại: Attribute #[Pure]

Đây là chi tiết đắt giá nhất trong đoạn code bạn đưa ra. #[Pure] là một Attribute (thuộc tính) được giới thiệu từ PHP 8, xuất phát từ tư duy của Functional Programming (Lập trình hàm).

Một hàm được đánh dấu là Pure (Tinh khiết) khi nó thỏa mãn 2 điều kiện:

  1. Không có Side-effect (Hiệu ứng phụ): Hàm này không làm thay đổi bất kỳ thứ gì ở thế giới bên ngoài. Nó không ghi log, không gọi database, không thay đổi biến global.
  2. Deterministic (Tính tất định): Với cùng một cặp giá trị đầu vào ($string1, $string2), nó luôn luôn trả ra một kết quả duy nhất, không phụ thuộc vào thời gian hay trạng thái hệ thống.

Tại sao phải gắn mác #[Pure] vào đây? Bởi vì nó giúp các công cụ phân tích code tĩnh (như PHPStan, Psalm, hoặc IDE PhpStorm) trở nên siêu thông minh. Nếu bạn viết thế này:

$a = "hello";
$b = "HELLO";
strcasecmp($a, $b); // Gọi hàm nhưng không gán kết quả cho ai cả

IDE của bạn sẽ ngay lập tức gạch chân vàng cảnh báo: "Ê, hàm này là hàm Pure, nó chả làm thay đổi cái gì đâu. Bạn gọi nó mà không lấy kết quả trả về (return) thì gọi làm gì cho tốn CPU?". Nhờ cái mác #[Pure] này mà code của bạn sẽ sạch hơn và tránh được các lỗi logic thừa thãi.

Lời kết

Chỉ với 14 dòng khai báo của hàm strcasecmp, chúng ta thấy được cả một sự tinh tế của những người tạo ra PHP: Từ việc tối ưu hiệu năng so sánh, đảm bảo an toàn với dữ liệu nhị phân (Binary safe), cho đến việc áp dụng các chuẩn mực code hiện đại (#[Pure]) để bảo vệ lập trình viên khỏi những lỗi sai ngớ ngẩn.

Việc thi thoảng "đào bới" lại các thư viện Core của ngôn ngữ để đọc comment như thế này là một cách cực kỳ nhanh để nâng trình độ code lên tầm Senior đấy anh em ạ!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí