Lập trình · 26/09/2026

PHP isset và array_key_exists: Phân biệt thiếu khóa với giá trị null

Một API cập nhật hồ sơ nhận nickname bằng null, nhưng dữ liệu cũ vẫn không bị xóa. Lỗi có thể nằm ở điều kiện isset: chương trình đã xem giá trị null giống như trường không được gửi. Khi thiết kế cập nhật từng phần, hai trạng thái này thường cần hành vi khác nhau.

PHP isset và array_key_exists: Phân biệt thiếu khóa với giá trị null

Một API cập nhật hồ sơ nhận nickname bằng null, nhưng dữ liệu cũ vẫn không bị xóa. Lỗi có thể nằm ở điều kiện isset: chương trình đã xem giá trị null giống như trường không được gửi. Khi thiết kế cập nhật từng phần, hai trạng thái này thường cần hành vi khác nhau.

1. Hai câu hỏi khác nhau

isset trong tài liệu PHP kiểm tra biến đã được khai báo và khác null. Với phần tử mảng, isset trả false khi thiếu khóa hoặc giá trị là null. Trong khi đó, array_key_exists kiểm tra khóa tồn tại, kể cả khi giá trị tương ứng là null.

$input = ['nickname' => null];

var_dump(isset($input['nickname']));
var_dump(array_key_exists('nickname', $input));
// bool(false)
// bool(true)

Không có hàm nào luôn tốt hơn. Hãy chọn theo câu hỏi nghiệp vụ: cần biết người dùng đã gửi trường đó, hay cần biết nó có giá trị khác null?

2. Viết rõ hợp đồng cập nhật

Giả sử ứng dụng quy định: không gửi nickname thì giữ nguyên; gửi null thì xóa biệt danh; gửi chuỗi hợp lệ thì thay thế. Đây là quy ước của ví dụ, không phải hành vi mặc định của mọi API.

Dữ liệu đầu vàoÝ nghĩa trong ví dụ
[]Không thay đổi nickname
['nickname' => null]Xóa nickname
['nickname' => 'Lan']Đặt nickname thành Lan

Nếu frontend tự loại mọi trường null trước khi gửi, backend không còn nhận được ý định xóa. Cần thống nhất quy ước từ biểu mẫu, bước tạo payload đến lớp validation và lưu dữ liệu.

3. Tách kiểm tra tồn tại khỏi kiểm tra giá trị

function applyNickname(array $profile, array $input): array
{
    if (!array_key_exists('nickname', $input)) {
        return $profile;
    }

    $value = $input['nickname'];
    if ($value !== null && !is_string($value)) {
        throw new InvalidArgumentException('nickname must be string or null');
    }
    if (is_string($value) && trim($value) === '') {
        throw new InvalidArgumentException('nickname must not be blank');
    }

    $profile['nickname'] = $value;
    return $profile;
}

Hàm minh họa chỉ trả về mảng mới, không ghi database. Nó chấp nhận null hoặc chuỗi không rỗng sau phép kiểm tra trim; không tự cắt khoảng trắng khỏi giá trị được lưu. Sản phẩm thật cần bổ sung giới hạn độ dài, quy tắc ký tự, xác thực và quyền sửa hồ sơ.

Việc nhận tham số array cũng làm rõ giả định đầu vào. Nếu giải mã JSON, phải xác nhận kết quả có cấu trúc phù hợp trước khi gọi hàm, thay vì coi mọi JSON hợp lệ đều là một hồ sơ cập nhật hợp lệ.

4. Có khóa chưa có nghĩa là hợp lệ

array_key_exists không kiểm tra kiểu, quyền truy cập hoặc tính an toàn của giá trị. Không truyền nguyên payload vào lớp lưu trữ chỉ vì các khóa đều tồn tại. Trong ví dụ, chỉ nickname được xử lý; một khóa role gửi thêm không được tự động cập nhật.

Với cấu trúc lồng nhau, kiểm tra từng tầng và kiểu của tầng cha. Tên khóa 'profile.nickname' không tự trở thành đường dẫn vào mảng con khi dùng array_key_exists.

5. Các trường hợp nên đưa vào test

  • Thiếu nickname: giữ nguyên giá trị cũ.
  • nickname là null: kết quả có khóa nickname với giá trị null.
  • Chuỗi 'Lan': thay thế giá trị cũ.
  • Chuỗi rỗng hoặc chỉ có dấu cách thông thường: bị từ chối.
  • Số 0, false hoặc mảng: bị từ chối theo hợp đồng của ví dụ.
  • Khóa không được phép như role: không thay đổi quyền người dùng.

Đây là các kỳ vọng cần kiểm thử trong ứng dụng. Phân biệt rõ “không gửi” và “gửi null” giúp API cập nhật từng phần thể hiện đúng ý định, thay vì âm thầm bỏ qua yêu cầu của người dùng.

Thảo luận

Bình luận 0

Đăng nhập để bình luận

Bạn cần có tài khoản để tham gia thảo luận và trả lời độc giả khác.

Đăng nhậpĐăng ký

Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.