本篇文章給大家帶來的內容是關于如何使用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 ;/ 方法 /html' target='_blank'>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){ // TODOsetStatus(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 // objectecho gettype($orderStatus) . PHP_EOL;// boolean truevar_dump($orderStatus instanceof Enum);// 233echo $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);// objectecho gettype($orderStatus) . PHP_EOL;// boolean truevar_dump($orderStatus instanceof Enum);echo $orderStatus . PHP_EOL;// 2??$orderStatus = new OrderStatus();// objectecho gettype($orderStatus) . PHP_EOL;// boolean truevar_dump($orderStatus instanceof Enum);echo $orderStatus;// 3??$orderStatus = new OrderStatus( 意外的參數 // objectecho gettype($orderStatus) . PHP_EOL;// boolean truevar_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));// boolvar_dump($WAIT_SHIP- format(true));// 判斷這個值是否有效的枚舉值var_dump(OrderStatus::isValid(2));// 判斷這個值是否有效的枚舉值var_dump(OrderStatus::isValid(8));// 獲取所有枚舉成員的 Keyvar_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));
截至目前 一個完整的枚舉就完成了~
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。
新聞熱點
疑難解答