簡單介紹 PHP 8.1 的列舉 (Enumerations)
PHP 8.1 在前陣子正式推出!加入不少新功能,其中包含最多人期待的列舉 (Enumerations)。列舉可以用來定義一系列的常數設定值,可以放在類型提示告知開發者哪些值是可以使用的,避免在開發時使用無效的設定值。
簡單介紹 Enum 的特性與各種功能
PHP 中宣告 Enum 的方式如下。可以看到一個 Enum 可以包含多個 Case,以此明確告知開發者有哪些 Case 是可以使用的。
// 有回退的 enum (也就是那個 return type hint),一定要設定初始值
enum UserName: string {
case ADMIN = 'admin';
case USER = 'user';
case GUEST = 'guest';
}
// 沒有回退 (non-backed) 的 enum 不能設定初始值
enum UserRole {
case ADMIN;
case USER;
case GUEST;
}
// 有回退的 enum 只能設定 int 與 string
enum UserNumber: int {
case ADMIN = 1;
case USER = 2;
case GUEST = 3;
}
Enum 可以看作是物件,有預設的屬性 name
與 value
。
// enum 物件有預設的屬性 name 與 value
var_dump(UserName::ADMIN->name); // ADMIN
var_dump(UserName::ADMIN->value); // admin
此外也有預設的靜態方法 from()
,此方法可以依據 value 回推 enum 中的 case,如果找不到就會拋出錯誤。如果不想讓錯誤停止程式執行,可以使用 tryFrom()
。
// enum 物件有預設的方法
var_dump(UserName::from('admin')); // enum(Enum\UserName::ADMIN)
var_dump(UserName::from('hello')); // throw error !!!
var_dump(UserName::tryFrom('hello')); // null
你可以使用 cases()
來遍歷 Enum 中所有的 Case。
enum UserRole {
case ADMIN;
case USER;
case GUEST;
}
foreach (UserRole::cases() as $role) {
echo $role->name . "\n";
}
// 執行結果為
// ADMIN
// USER
// GUEST
Enum 內部還可以設定方法。
enum UserRole {
case ADMIN;
case USER;
case GUEST;
// enum 內部可以設定 method
public function content(): string
{
return match($this) {
self::ADMIN => 'admin',
self::USER => 'user',
self::GUEST => 'guest',
};
}
}
var_dump(UserRole::ADMIN->content()); // admin
當一個類別的初始參數被設定為 Enum 時,就只能傳入 Enum 的 Case,就樣就能限制值的範圍,避免傳入預料外的內容。
注意這裡不是接受 Case 的對應值,也就是純數字或是純字串。而是傳入 Enum 的 Case。
class User
{
public function __construct(
public UserName $userName,
) {}
}
$user = new User('admin'); // throw error !!!
$user = new User(UserName::ADMIN);
var_dump($user->userName); // enum(Enum\UserName::ADMIN)
用 Enum 來重構部落格的文章排列選單
我的部落格文章列表有三種排列順序,分別是「最新文章」、「最近更新」與「最多留言」。這種有固定一系列值的情境就很適合使用 Enum。
首先宣告一個 PostOrder
的 Enum。
namespace App\Enums;
enum PostOrder: string
{
case LATEST = 'latest';
case RECENT = 'recent';
case COMMENT = 'comment';
public function label(): string
{
return match ($this) {
self::LATEST => '最新文章',
self::RECENT => '最近更新',
self::COMMENT => '最多留言',
};
}
public function iconComponentName(): string
{
return match ($this) {
self::LATEST => 'icon.stars',
self::RECENT => 'icon.wrench',
self::COMMENT => 'icon.chat-square-text',
};
}
}
接著在前端使用 PostOrder
來顯示選單。
<!-- laravel blade 範例程式碼 -->
<!-- 引入 PostOrder enums -->
@php
use App\Enums\PostOrder;
@endphp
<nav>
<!-- 遍歷 PostOrder enum 中所有的 case -->
@foreach (PostOrder::cases() as $postOrder)
<!-- 這裡使用 livewire 提供的語法來更新後端對文章的排列順序 -->
<!-- changeOrder 這個方法我設定只能接收 PostOrder 的 case -->
<!-- 傳入目前的 PostOrder case -->
<button
type="button"
wire:click="changeOrder('{{ $postOrder }}')"
wire:key="post-order-{{ $postOrder->value }}"
>
<!-- 根據目前的 PostOrder case,產生不一樣的 component -->
<x-dynamic-component
:component="$postOrder->iconComponentName()"
/>
<!-- 根據目前的 PostOrder case,產生不一樣的選單文字 -->
<span>{{ $postOrder->label() }}</span>
</button>
@endforeach
</nav>
在後端的 changeOrder
方法,我們可以使用類型提示限定參數只能接受 PostOrder
的 Case。
use App\Enums\PostOrder;
// ...
// 這裡設定參數只能接受 PostOrder 的 case
public function changeOrder(PostOrder $newOrder): void
{
$this->order = $newOrder->value;
$this->resetPage();
}
PHP 的 Enum 除了可以明確告知開發者應該使用哪些值之外,也可以根據不同的情況讓 Case 返回不一樣的內容,真的非常好用!