PHP 核心語言未內建 Enum 類型(直到 PHP 8.1 才引入原生 Enum),歷史上主要通過 SPL 擴展 SplEnum 或自定義類庫模擬枚舉行為。本文詳細對比兩種方案的用法與優劣。
類結構:
SplEnum extends SplType {
const NULL __default = null;
public array getConstList([bool $include_default = false]);
/* 繼承 SplType::__construct([mixed $initial_value[, bool $strict]]) */
}
<?php
class Month extends SplEnum {
const __default = self::January;
const January = 1; const February = 2; const March = 3;
const April = 4; const May = 5; const June = 6;
const July = 7; const August = 8; const September = 9;
const October = 10; const November = 11; const December = 12;
}
echo new Month(Month::June) . PHP_EOL; // 輸出 6
try {
new Month(13); // 非法值
} catch (UnexpectedValueException $uve) {
echo $uve->getMessage() . PHP_EOL; // 輸出 "Value not a const in enum Month"
}
?>
優點:原生級效能、嚴格類型檢查、支援預設值。
缺點:需額外安裝擴展,部署環境受限,非標準分發。
<?php
abstract class Enum {
const __default = null;
private $value;
private $strict;
private static $constants = [];
public function getConstList($includeDefault = false) {
$class = get_class($this);
if (!array_key_exists($class, self::$constants)) self::populateConstants();
return $includeDefault ? array_merge(self::$constants[$class], ["__default" => self::__default]) : self::$constants[$class];
}
public function __construct($initialValue = null, $strict = true) {
$class = get_class($this);
if (!array_key_exists($class, self::$constants)) self::populateConstants();
if ($initialValue === null) $initialValue = self::$constants[$class]["__default"];
$temp = self::$constants[$class];
if (!in_array($initialValue, $temp, $strict)) throw new UnexpectedValueException("Value is not in enum $class");
$this->value = $initialValue; $this->strict = $strict;
}
private function populateConstants() {
$class = get_class($this);
$r = new ReflectionClass($class);
self::$constants[$class] = $r->getConstants();
}
public function __toString() { return (string) $this->value; }
public function equals($object) {
if (!($object instanceof Enum)) return false;
return $this->strict ? ($this->value === $object->value) : ($this->value == $object->value);
}
}
?>
<?php
class MyEnum extends Enum {
const HI = "Hi";
const BY = "By";
const NUMBER = 1;
const __default = self::BY;
}
var_dump(new MyEnum(MyEnum::HI));
var_dump(new MyEnum(MyEnum::BY));
var_dump(new MyEnum()); // 使用預設值 BY
try {
new MyEnum("I don't exist");
} catch (UnexpectedValueException $e) {
var_dump($e->getMessage()); // "Value is not in enum MyEnum"
}
?>
輸出:正確實例化並拋出異常阻擋非法值。
__toString() 直接輸出值,方便模板引擎使用。equals() 支援嚴格/非嚴格比較。| 維度 | SplEnum | 自定義基類 |
|---|---|---|
| 依賴 | 需安裝 PECL spl_types | 零依賴,純 PHP |
| 效能 | C 層實現,極快 | 反射緩存後接近原生 |
| 部署 | 需服務器權限安裝擴展 | 代碼即用,適合共享主機 |
| 功能 | 基礎枚舉行為 | 可擴展(如添加標籤、描述) |
PHP 8.1 引入原生 enum 關鍵字,支援純枚舉和有背景值枚舉,且具備方法、介面實現等豐富特性:
<?php
enum Status: string {
case Draft = 'draft';
case Published = 'published';
case Archived = 'archived';
public function label(): string {
return match($this) {
self::Draft => '草稿',
self::Published => '已發布',
self::Archived => '已歸檔',
};
}
}
$status = Status::Published;
echo $status->value; // 'published'
echo $status->label(); // '已發布'
?>
建議:新項目若環境允許,優先使用 PHP 8.1+ 原生 Enum;舊項目或受限環境,自定義基類是最佳替代方案。