如何使用PHP來實現枚舉?
本篇文章帶給大家的內容是關於如何使用PHP來實現列舉?有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
枚舉
在數學和計算機科學理論中,一個集的枚舉是列出某些有窮序列集的所有成員的程序,或者是一種特定類型物件的計數。這兩種類型經常(但不總是)重疊。枚舉是一個被命名的整數常數的集合,枚舉在日常生活中很常見,例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一個枚舉。 —— 維基百科
業務場景
在實際開發過程中我們非常容易接觸到枚舉類型,但又因為PHP 原生對枚舉的支援不是太好,所以很多時候開發人員並沒有重視枚舉的使用,而是使用全域常數或類別常數代替,而這兩個資料原則上還是字串
並不能用來做類型判斷。
業務
- 訂單狀態待支付/待發貨/待收貨/待評估
- 會員狀態啟動/未啟動
- . ...
等等,很多時候我們都會用簡單的1/2/3/4 或0/1 這樣的方式去代表,然後在文件或註解中規定這些東西。
更高級一點兒的就是定義成常數,然後方便統一訪問,但是常數的值還是是字串,無法進行型別判斷。
這裡就要看一下PHP 對枚舉的支持,雖然PHP 對枚舉沒有完美的支持,但是在SPL 中還是有一個基礎的枚舉類
SPL 枚舉
SplEnum extends SplType {/ Constants / const NULL __default = NULL ; / 方法 / public getConstList ([ bool $include_default = FALSE ] ) : array / 继承的方法 / SplType::__construct ( [mixed $initial_value [, bool $strict ]] ) }
但是!這個需要額外的安裝 PECL 用 PECL 安裝 Spl_Types
,無意間增加了使用成本,那有沒有其他解?答案是肯定的。
直接手寫一個。
開始準備
先定一個枚舉
class Enum { // 默认值 const __default = self::WAIT_PAYMENT; // 待付款 const WAIT_PAYMENT = 0; // 待发货 const WAIT_SHIP = 1; // 待收货 const WAIT_RECEIPT = 2; // 待评价 const WAIT_COMMENT = 3; }
這樣似乎就完成了,我們直接使用Enum::WAIT_PAYMENT
就可以拿到裡面的值了,但是傳參的地方我們並沒法校驗他。
function setStatus(Enum $status){ // TODO } setStatus(Enum::WAIT_PAYMENT); // Error 显然这是不行的 因为上面常量的值时一个 int 并不是 Enum 类型。
這裡我們就需要用到PHP 物件導向中的一個魔術方法__toString()
public __toString ( void ) : string
#__toString() 方法用於一個類別被當成字串時應怎樣回應。例如 echo $obj; 應該顯示些什麼。此方法必須傳回字串,否則將發出 E_RECOVERABLE_ERROR 等級的致命錯誤。
現在我們來完善一下這個方法。
class OrderStatus extends Enum { // 默认值 const __default = self::WAIT_PAYMENT; // 待付款 const WAIT_PAYMENT = 0; // 待发货 const WAIT_SHIP = 1; // 待收货 const WAIT_RECEIPT = 2; // 待评价 const WAIT_COMMENT = 3; public function __toString() { return '233'; } } // object echo gettype($orderStatus) . PHP_EOL; // boolean true var_dump($orderStatus instanceof Enum); // 233 echo $orderStatus;
初具模型
這裡似乎實現了一部分,那我們應該怎麼樣讓他做的更好?再來改造一下。
class OrderStatus extends Enum { // 默认值 const __default = self::WAIT_PAYMENT; // 待付款 const WAIT_PAYMENT = 0; // 待发货 const WAIT_SHIP = 1; // 待收货 const WAIT_RECEIPT = 2; // 待评价 const WAIT_COMMENT = 3; /** * @var string */ protected $value; public function __construct($value = null) { $this->value = is_null($value) ? self::__default : $value; } public function __toString() { return (string)$this->value; } } // 1️⃣ $orderStatus = new OrderStatus(OrderStatus::WAIT_SHIP); // object echo gettype($orderStatus) . PHP_EOL; // boolean true var_dump($orderStatus instanceof Enum); // 1 echo $orderStatus . PHP_EOL; // 2️⃣ $orderStatus = new OrderStatus(); // object echo gettype($orderStatus) . PHP_EOL; // boolean true var_dump($orderStatus instanceof Enum); // 0 echo $orderStatus; // 3️⃣ $orderStatus = new OrderStatus('意外的参数'); // object echo gettype($orderStatus) . PHP_EOL; // boolean true var_dump($orderStatus instanceof Enum); // 意外的参数 echo $orderStatus;
在這次,我們加入了建構子
並且允許他傳入一個可選的值,然後來作為__toString
方法的輸出值,這次看起來不錯,功能都已經實現了,如果傳入的參數否和我們的預期的話。但是 萬一不符合呢?看看,第 3️⃣ 個那裡,就已經變成了意外了,哪還有沒有辦法補救?答案當然是 有的
,在這裡我們會用到 PHP 另一個好東西 反射類別 ,當然這個不是 PHP 特有的,其他語言也有。
當然,除了反射,我們還會用到另一個東西 方法重載
裡面的 __callStatic
方法。
更進一步
public static __callStatic ( string $name , array $arguments ) : mixed
#在靜態上下文中呼叫一個不可存取方法時,__callStatic() 會被呼叫。$name 參數是要呼叫的方法名稱。 $arguments 參數是一個枚舉數組,包含著要傳遞給方法 $name 的參數。
繼續改造。
class Enum { const __default = null; /** * @var string */ protected static $value; // 注意这里 将构造函数的 修饰符改成了 受保护的 即 外部无法直接 new protected function __construct($value = null) { // 很常规 self::$value = is_null($value) ? static::__default : $value; } /** * @param $name * @param $arguments * @return mixed * @throws ReflectionException */ public static function __callStatic($name, $arguments) { // 实例化一个反射类 static::class 表示调用者 $reflectionClass = new ReflectionClass(static::class); // 这里我们要有一个约定, 就是类常量成员的名字必须的大写。 // 这里就是取出来调用的静态方法名对应的常量值 虽然这里有个 getValue 方法 // 但是因为其返回值不可靠 我们就依赖于他原本的隐式的 __toString 方法来帮我们输出字符串即可。 $constant = $reflectionClass->getConstant(strtoupper($name)); // 获取调用者的 构造方法 $construct = $reflectionClass->getConstructor(); // 设置成可访问 因为我们把修饰符设置成了受保护的 这里需要访问到,所以就需要设置成可访问的。 $construct->setAccessible(true); // 因为现在类已经是可以访问的了所以我们直接实例化即可,实例化之后 PHP 会自动调用 __toString 方法 使得返回预期的值。 $static = new static($constant); return $static; } public function __toString() { return (string)self::$value; } } class OrderStatus extends Enum { // 默认值 const __default = self::WAIT_PAYMENT; // 待付款 const WAIT_PAYMENT = 0; // 待发货 const WAIT_SHIP = 1; // 待收货 const WAIT_RECEIPT = 2; // 待评价 const WAIT_COMMENT = 3; } $WAIT_SHIP = OrderStatus::WAIT_SHIP(); var_dump($WAIT_SHIP . ''); var_dump($WAIT_SHIP instanceof Enum);
到這裡 一個簡單的枚舉類別就完成了。
結束
那如果我們還有其他需求、例如 判斷一個值是不是在列舉範圍內?取得所有的枚舉值?取得所有的枚舉鍵,判斷枚舉鍵是否有效?自動格式化「因為__toString 方法只允許回傳字串,但有的時候我們強制需要整形、bool 等型別」
class Enum { const __default = null; /** * @var string */ protected static $value; /** * @var ReflectionClass */ protected static $reflectionClass; // 注意这里 将构造函数的 修饰符改成了 受保护的 即 外部无法直接 new protected function __construct($value = null) { // 很常规 self::$value = is_null($value) ? static::__default : $value; } /** * @param $name * @param $arguments * @return mixed */ public static function __callStatic($name, $arguments) { // 实例化一个反射类 static::class 表示调用者 $reflectionClass = self::getReflectionClass(); // 这里我们要有一个约定, 就是类常量成员的名字必须的大写。 // 这里就是取出来调用的静态方法名对应的常量值 虽然这里有个 getValue 方法 // 但是因为其返回值不可靠 我们就依赖于他原本的隐式的 __toString 方法来帮我们输出字符串即可。 $constant = $reflectionClass->getConstant(strtoupper($name)); // 获取调用者的 构造方法 $construct = $reflectionClass->getConstructor(); // 设置成可访问 因为我们把修饰符设置成了受保护的 这里需要访问到,所以就需要设置成可访问的。 $construct->setAccessible(true); // 因为现在类已经是可以访问的了所以我们直接实例化即可,实例化之后 PHP 会自动调用 __toString 方法 使得返回预期的值。 $static = new static($constant); return $static; } /** * 实例化一个反射类 * @return ReflectionClass * @throws ReflectionException */ protected static function getReflectionClass() { if (!self::$reflectionClass instanceof ReflectionClass) { self::$reflectionClass = new ReflectionClass(static::class); } return self::$reflectionClass; } /** * @return string */ public function __toString() { return (string)self::$value; } /** * 判断一个值是否有效 即是否为枚举成员的值 * @param $val * @return bool * @throws ReflectionException */ public static function isValid($val) { return in_array($val, self::toArray()); } /** * 转换枚举成员为键值对输出 * @return array * @throws ReflectionException */ public static function toArray() { return self::getEnumMembers(); } /** * 获取枚举的常量成员数组 * @return array * @throws ReflectionException */ public static function getEnumMembers() { return self::getReflectionClass() ->getConstants(); } /** * 获取枚举成员值数组 * @return array * @throws ReflectionException */ public static function values() { return array_values(self::toArray()); } /** * 获取枚举成员键数组 * @return array * @throws ReflectionException */ public static function keys() { return array_keys(self::getEnumMembers()); } /** * 判断 Key 是否有效 即存在 * @param $key * @return bool * @throws ReflectionException */ public static function isKey($key) { return in_array($key, array_keys(self::getEnumMembers())); } /** * 根据 Key 去获取枚举成员值 * @param $key * @return static */ public static function getKey($key) { return self::$key(); } /** * 格式枚举结果类型 * @param null|bool|int $type 当此处的值时什么类时 格式化输出的即为此类型 * @return bool|int|string|null */ public function format($type = null) { switch (true) { // 当为纯数字 或者类型处传入的为 int 值时 转为 int case ctype_digit(self::$value) || is_int($type): return (int)self::$value; break; // 当 type 传入 true 时 返回 bool 类型 case $type === true: return (bool)filter_var(self::$value, FILTER_VALIDATE_BOOLEAN); break; default: return self::$value; break; } } } class OrderStatus extends Enum { // 默认值 const __default = self::WAIT_PAYMENT; // 待付款 const WAIT_PAYMENT = 0; // 待发货 const WAIT_SHIP = 1; // 待收货 const WAIT_RECEIPT = 2; // 待评价 const WAIT_COMMENT = 3; } $WAIT_SHIP = OrderStatus::WAIT_SHIP(); // 直接输出是字符串 echo $WAIT_SHIP; // 判断类型是否存在 var_dump($WAIT_SHIP instanceof OrderStatus); // 格式化输出一下 是要 字符串 、还是 bool 还是整形 // 自动 var_dump($WAIT_SHIP->format()); // 整形 var_dump($WAIT_SHIP->format(1)); // bool var_dump($WAIT_SHIP->format(true)); // 判断这个值是否有效的枚举值 var_dump(OrderStatus::isValid(2)); // 判断这个值是否有效的枚举值 var_dump(OrderStatus::isValid(8)); // 获取所有枚举成员的 Key var_dump(OrderStatus::keys()); // 获取所有枚举成员的值 var_dump(OrderStatus::values()); // 获取枚举成员的键值对 var_dump(OrderStatus::toArray()); // 判断枚举 Key 是否有效 var_dump(OrderStatus::isKey('WAIT_PAYMENT')); // 判断枚举 Key 是否有效 var_dump(OrderStatus::isKey('WAIT_PAYMENT_TMP')); // 根据 Key 取去 值 注意 这里取出来的已经不带有类型了 // 更加建议直接使用 取类常量的方式去取 或者在高版本的 直接使用类常量修饰符 // 将类常量不可见最佳,但是需要额外处理了 var_dump(OrderStatus::getKey('WAIT_PAYMENT') ->format(1));
截至目前一個完整的列舉就完成了~
以上是如何使用PHP來實現枚舉?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。

PHP在電子商務、內容管理系統和API開發中廣泛應用。 1)電子商務:用於購物車功能和支付處理。 2)內容管理系統:用於動態內容生成和用戶管理。 3)API開發:用於RESTfulAPI開發和API安全性。通過性能優化和最佳實踐,PHP應用的效率和可維護性得以提升。

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP仍然具有活力,其在現代編程領域中依然佔據重要地位。 1)PHP的簡單易學和強大社區支持使其在Web開發中廣泛應用;2)其靈活性和穩定性使其在處理Web表單、數據庫操作和文件處理等方面表現出色;3)PHP不斷進化和優化,適用於初學者和經驗豐富的開發者。

在PHP8 中,match表達式是一種新的控制結構,用於根據表達式的值返回不同的結果。 1)它類似於switch語句,但返回值而非執行語句塊。 2)match表達式使用嚴格比較(===),提升了安全性。 3)它避免了switch語句中可能的break遺漏問題,增強了代碼的簡潔性和可讀性。

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP和Python各有優劣,選擇取決於項目需求和個人偏好。 1.PHP適合快速開發和維護大型Web應用。 2.Python在數據科學和機器學習領域佔據主導地位。
