Menu

Enum trong PHP: backed enum, from(), tryFrom() và cases()

Enum trong PHP (8.1+) là một kiểu có danh sách giá trị cố định: enum Status: string { case Active = 'active'; }. Tìm hiểu pure enum và backed enum, ->value và ->name, chuyển đổi bằng from() và tryFrom(), liệt kê case bằng cases(), phương thức, hằng số, interface và enum trong match.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Enum trong PHP (PHP 8.1+) định nghĩa một kiểu với tập giá trị cố định, gọi là các case: enum Status { case Active; case Banned; }. Bạn dùng một case dưới dạng Status::Active, và tham số có kiểu Status sẽ không nhận gì khác. Backed enum, enum Status: string, còn gán cho mỗi case một giá trị mà bạn có thể lưu lại và chuyển ngược về bằng Status::from().

Trước khi có enum, việc này là một danh sách hằng số chuỗi, và không gì ngăn được một lỗi gõ như 'actve' lọt vào cơ sở dữ liệu. Khi tham số có kiểu Status, chỉ ba case kia mới truyền vào được.

Pure enum và backed enum

Pure enum có các case chỉ là tên. Backed enum khai báo một kiểu sau tên, int hoặc string, và khi đó mọi case đều phải có một giá trị duy nhất thuộc kiểu ấy. Mọi case đều có ->name; chỉ case của backed enum mới có ->value.

Hãy chọn backed enum mỗi khi giá trị đi ra khỏi chương trình: một cột cơ sở dữ liệu, một URL, một trường form, JSON. Chọn pure enum cho những giá trị chỉ tồn tại bên trong code, như trạng thái của một bộ phân tích cú pháp.

Chuyển đổi giá trị bằng from() và tryFrom()

Backed enum có hai phương thức tĩnh để biến một giá trị đã lưu trở lại thành case. from() ném ra ValueError với giá trị không xác định; tryFrom() trả về null, rất hợp khi đi cùng ?? để lấy giá trị mặc định:

Dùng tryFrom() cho mọi thứ người dùng gửi đến, và from() cho các giá trị do chính code của bạn ghi ra, nơi một giá trị lạ nghĩa là có bug mà bạn muốn biết. Đối số tuân theo quy tắc kiểu tham số thông thường: với enum kiểu int, ở chế độ mặc định Priority::from('5') trả về Priority::Normal vì '5' được chuyển thành 5 trước, Priority::from('5x') là TypeError, còn với declare(strict_types=1) thì mọi chuỗi đều là TypeError. Điều đó áp dụng cả cho tryFrom(): Priority::tryFrom('abc') ném ngoại lệ thay vì trả về null, nên hãy kiểm tra dữ liệu form cho enum kiểu int bằng filter_var($raw, FILTER_VALIDATE_INT) trước.

Liệt kê mọi case bằng cases()

Enum::cases() trả về một mảng gồm mọi case, theo thứ tự khai báo. Đây là cách bạn tạo dropdown, kiểm tra một danh sách, hoặc lấy mọi giá trị bằng array_column:

Hãy chạy nó, chọn một gói và nhấn Choose: cùng script đó chạy lại với $_POST['plan'] đã có dữ liệu, và tryFrom() biến chuỗi được gửi lên trở lại thành một case của Plan. Một giá trị giả mạo như plan=gold cho ra null, và không có lựa chọn nào được chọn.

Phương thức, hằng số và interface của enum

Enum có thể có phương thức, phương thức tĩnh và hằng số, và có thể triển khai interface. Bên trong một phương thức, $this là case hiện tại, nên match ($this) là cách tự nhiên để gắn dữ liệu cho từng case. Thứ duy nhất enum không thể có là thuộc tính: dữ liệu theo từng case đến từ các phương thức.

Enum trong match

match so sánh bằng ===, và một case enum chỉ đồng nhất với chính nó, nên match và enum rất hợp nhau. Nếu bạn quên một case và case đó đi đến match, PHP ném ra UnhandledMatchError thay vì lặng lẽ không trả về gì:

Xem match để biết match khác switch như thế nào.

Enum trong JSON và dưới dạng chuỗi

json_encode() ghi một case của backed enum thành giá trị của nó. Pure enum thì không encode được, và echo bất kỳ case nào cũng thất bại, vì enum là một đối tượng, không phải chuỗi. Hãy in ->value hoặc ->name:

echo Role::Admin; ném ra Error ("Object of class Role could not be converted to string"), và enum không thể triển khai __toString(). Khi đọc vào, JSON chỉ cho bạn một chuỗi thường, nên hãy chuyển nó bằng from() hoặc tryFrom() như trên. Xem thêm về encode ở json_encode.

Lỗi thường gặp với enum

Có ba lỗi lặp đi lặp lại. Một case không bao giờ bằng giá trị của nó, một case không thể làm khóa mảng, và new không dùng được với enum:

Nếu bạn cần một bảng ánh xạ dùng chính các case làm khóa, hãy dùng SplObjectStorage hoặc WeakMap, vốn nhận đối tượng làm khóa.

Câu hỏi thường gặp

Làm sao lấy giá trị của một enum trong PHP?

Với backed enum, đọc ->value: Status::Active->value là 'active'. Mọi case còn có ->name, tên của case dưới dạng chuỗi ('Active'). Pure enum (không có : string hay : int) chỉ có ->name.

from() và tryFrom() của enum trong PHP khác nhau thế nào?

Cả hai đều biến một giá trị thành case của backed enum. Status::from('active') trả về case đó, hoặc ném ra ValueError khi không có case nào mang giá trị ấy; còn Status::tryFrom('nope') trả về null. Dùng tryFrom() cho dữ liệu người dùng nhập và from() khi giá trị sai là một bug.

Làm sao lấy tất cả giá trị của một enum trong PHP?

Status::cases() trả về mọi case theo thứ tự khai báo. Để lấy các giá trị của backed enum, dùng array_column(Status::cases(), 'value'), còn để lấy tên thì dùng array_column(Status::cases(), 'name').

Pure enum và backed enum khác nhau thế nào?

Pure enum (enum Suit { case Hearts; }) có các case chỉ là tên. Backed enum (enum Suit: string { case Hearts = 'H'; }) gán cho mỗi case một giá trị int hoặc string duy nhất, thứ bạn cần để lưu vào cơ sở dữ liệu, đưa vào form hay JSON, rồi chuyển ngược lại bằng from() hoặc tryFrom().

Enum trong PHP có phương thức được không?

Có. Enum có thể có phương thức, phương thức tĩnh và hằng số, và có thể triển khai interface. Bên trong một phương thức, $this là case hiện tại, nên return match ($this) { self::Active => 'green', self::Banned => 'red' }; là một mẫu hay gặp. Enum không thể có thuộc tính.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu