PHP 8.1 เพิ่มการรองรับ Enum
PHP 8.1 กำลังอยู่ระหว่างการพัฒนาและคาดว่าจะเริ่มปล่อยให้ใช้ได้ช่วงปลายเดือน พฤศจิกายน 2021 ฟีเจอร์หนึ่งที่เพิ่มขึ้นมาคือการอองรับ Enum
เราจะมาเริ่มดูกันที่วิธีประกาศ enum ใน PHP8.1 กันเลย
enum Status
{
case DRAFT;
case PUBLISHED;
case ARCHIVED;
}
ข้อดีของการใช้ enum คือ ไว้ใช้ประกาศค่าคงที่หลาย ๆ ตัวที่เป็นกลุ่มเดียวกันโดยที่ค่าพวกนี้สามารถในไปใช้ type-hint ได้
class Post
{
public function __construct(
public Status $status,
) {}
}
จากตัวอย่างนี้ เราได้สร้าง enum แล้วส่งค่าให้ Post
ได้แบบนี้
$post = new Post(Status::DRAFT);
อันนี้เป็นเพียงวิธีการใช้งานแบบพื้นฐานไม่มีอะไรซับซ้อนไม่ต่างจากการประกาศค่าคงที่ในคลาสตามปกติ แต่ enum ยังมีความสามารถมากกว่านี้ ลองมาดูกันเลย
Enum สามารถมี method ได้
Enum สามารถมีเมธอดได้เหมือนกับคลาส อันนี้เป็นฟีเจอร์ที่มีความสามารถมาก เช่น หลาย ๆ ครั้งเราต้องการเอา status ไปแสดงเป็น Label แล้วต้องมีการ style สีของ label หรือ ทำ title ของ label ตาม status ปัจจุบัน เราสามารถนำไปใช้ร่วมกับ match
operator ซึ่งเป็น syntax ใหม่ที่ถูกเพิ่มเข้ามาตั้งแต่ PHP 8 ด้วย ตามตัวอย่างนี้
enum Status
{
case DRAFT;
case PUBLISHED;
case ARCHIVED;
case TRASHED;
public function color(): string
{
return match($this)
{
Status::DRAFT => 'grey',
Status::PUBLISHED => 'green',
Status::ARCHIVED,
Status::ARCHIVED => 'red',
};
}
public function title(): string
{
return match($this)
{
Status::DRAFT => 'ร่าง',
Status::PUBLISHED => 'เผยแพร่',
Status::ARCHIVED => 'ประวัติ',
Status::TRASHED => 'ถังขยะ',
};
}
}
เมธอดใน Enum สามารถเรียกใช้งานได้แบบนี้
$status = Status::ARCHIVED;
$status->color(); // 'red'
$status->color(); // 'ประวัติ'
ใน enum ก็สามารถใช้ static method ได้เหมือนกัน แล้วก็สังเกตว่าเราสามารถใช้ self
ใน enum ได้เหมือนกับในคลาส
enum Status
{
// …
public function color(): string
{
return match($this)
{
self::DRAFT => 'grey',
self::PUBLISHED => 'green',
self::ARCHIVED => 'red',
};
}
}
Enum interface
Enum สามารถ implement interface ได้ เหมือนคลาสปกติทั่วไป
interface HasColor
{
public function color(): string;
}
enum Status implements HasColor
{
case DRAFT;
case PUBLISHED;
case ARCHIVED;
public function color(): string { /* … */ }
}
Enum แบบมี value — หรือ "Backed enums"
ค่าของ enum ภายใน PHP จริง ๆ แล้วเก็บเป็น Object แต่เราสามารถกำหนดค่าให้ enum แต่ละตัวได้ ทำแบบนี้จะมีประโยชน์ เช่น กรณีที่เราต้องการเก็บค่าลง database
enum Status: string // สังเกคประกาศ type ของ enum
{
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
// แต่ละค่า มี value เป็น string
}
สังเกต การประกาศ type หลังชื่อ enum (enum Status: string
) ตามตัวอย่างด้านบน เป็นการบอกว่าค่า enum จะมี type ตามที่ระบุ เราสามารถระบุ type เป็น int ได้เช่นกัน
เฉพาะ type
int
และstring
เท่านั้นที่สามารถระเป็น enum value ได้
enum Status: int
{
case DRAFT = 1;
case PUBLISHED = 2;
case ARCHIVED = 3;
}
ตามเทคนิคแล้ว เราเรียก enum ที่มีการระบุ type แบบนี้ว่า Backed Enum เพราะเรามีการระบุค่าให้กับแต่ละ case ใน enum เราไม่สามารถปนกันระว่าง case ที่มี value มารับ กับ case ที่ไม่มี value มารับได้ ถ้า enum ที่มีแต่ case โดยไม่มี value มารองรับเหมือนกันตัวอย่างแรกสุดของเรา เราจะเรียก enum แบบนี้ว่า Pure Enum
Backed Enums ที่มี interfaces
ถ้าเราต้องการใช้ทั้ง backed enum และมีการ implement interface ด้วย เราต้องประกาศ enum type ต่อจากชื่อ enum และก่อนคีย์เวิร์ด implements
enum Status: string implements HasColor
{
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
// …
}
Serializing backed enums
ถ้าเรามีการกำหนดค่าให้กับ enum เราอาจจะต้องการ serialize หรือ deserialize enum ด้วยเหมือนกัน เช่น แปลงเป็น array หรือ json เพื่อ save ลง database หรือใช้ใน frontend โดยเราสามารถเรียกได้ผ่าน value
ซึ่งเป็น readonly public property ของ enum
$value = Status::PUBLISHED->value; // 2
การคืนค่า enum จากค่า สามารถทำได้ผ่านเมธอด Enum::form
$status = Status::from(2); // Status::PUBLISHED
นอกจากนี้ยังมีเมธอด tryFrom()
ที่จะ return null
ถ้าหาค่าที่ระบุไม่พบ แต่ถ้าเราใช้ from จะเป็น exception
$status = Status::from('unknown'); // ValueError
$status = Status::tryFrom('unknown'); // null
method ที่มีชื่อเป็น tryX
จะพบได้มากในภาษา C# และ Rust ใช้เพื่อบ่งชี้ว่าผลลัพธ์ที่ return มาอาจเป็น null หรือไม่มีค่าได้ เราอาจจะได้เห็น PHP เริ่มเอาสไตล์การตั้งชื่อ method แบบนี้มาใช้มากขึ้นได้
เพิ่มเติมว่าเรายังสามารถใช้ฟังก์ชัน serialize
และ unserialize
กับ enum ได้ แล้วใช้ json_encode
ร่วมกับ backed enum ได้ โดยค่าจะเป็น enum value เราสามารถ override พฤติกรรมนี้ได้โดย implment JsonSerializable
แสดงค่าทั้งหมดใน enum
เราสามารถใช้ static method Enum::cases()
เพื่อแสดง array ของค่าทั้งหมดที่มีใน enum ได้
Status::cases();
/* [
Status::DRAFT,
Status::PUBLISHED,
Status::ARCHIVED
] */
โดย array ที่ได้จะเก็บ object enum แต่ละตัว
array_map(
fn(Status $status) => $status->color(),
Status::cases()
);
ถ้าเราใช้ Enum::cases()
กับ backed enums, เราจะได้ enum value ใน array key
Status::cases();
/* [
'draft' => Status::DRAFT,
'published' => Status::PUBLISHED,
'archived' => Status::ARCHIVED,
] */
Enum เป็น Object
อย่างที่ได้กล่าวไปในตอนต้นแล้วว่า enum เก็บค่าเป็น Object ในความจริงแล้วต้องบอกว่าเป็น Singleton Object นั่นแปลว่าเราสามารถนำ enum มาเปรียบเทียบกันได้
$statusA = Status::PENDING;
$statusB = Status::PENDING;
$statusC = Status::ARCHIVED;
$statusA === $statusB; // true
$statusA === $statusC; // false
$statusC instanceof Status; // true
Enum สามารถใช้ร่วมกับ Trait ได้
Enum สามารถใช้ร่วมกับ trait ได้โดยจะมีผลเหมือนกับใช้ร่วมกับคลาส ข้อจำกัดอย่างหนึ่งคือ trait ที่นำมาใช้ใน enum ต้องไม่มี property จะมีได้เฉพาะ method หรือ static method เท่านั้น ถ้า Enum ที่เอา trait ที่มี property มาใช้จะทำให้เกิด fatal error
trait Label {
public function label() {
return "<span>{$this->value}</span>";
}
}
enum Status: string implements HasColor
{
use Label;
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
// …
}
สรุป
ในบทความนี้เราพามาทำความรู้จักกับ Enum ซึ่งเป็นความสามารถใหม่ที่มาใน PHP 8.1 เราได้อธิบายการใช้ Enum ไว้ดังนี้
- การประกาศ Enum (Pure Enum)
- การใช้ Enum แบบระบุค่า (Backed Enum)
- Backed Enum สามารถมีค่าได้เป็น int หรือ string เท่านั้น
- รู้จักกับ method ที่มีใน enum
- Enum::cases() แสดงค่าทั้งหมดใน enum
- Enum::from() return Object enum จากค่าที่ระบุ
- Enum::tryFrom() return Object enum จากค่าที่ระบุ return null ถ้าหาค่าไม่พบ
อ้างอิง
- ถ้าผู้ที่สนใจอยากรู้จักกับ Enum ใน PHP 8.1 ลองดูที่มาจาก PHP RFC: Enumeration ได้เพิ่มเติม
- stitcher.io