How to implement Enum like functionality in PHP?
如何在PHP中使用EnUM类功能(如Java和其他高级语言提供的)?我知道PHP目前不允许创建枚举,但是最接近的是什么?
可能使用
1 2 3 4 | class SomeClass { const FIRSTVAL = 1; const SECONDVAL = 2; }; |
我使用类常量,还有一些反射技巧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | <?php /** * @package Red.Core * @author [email protected] * * Implements the abstract base for all enum types * * example of a typical enum: * * class DayOfWeek extends Enum * { * const Sunday = 0; * const Monday = 1; * const Tuesday = 2; * const Wednesday = 3; * const Thursday = 4; * const Friday = 5; * const Saturday = 6; * } * * usage examples: * * $monday = Enum::FromString( 'DayOfWeek::Monday' ); // (int) 1 * $monday = DayOfWeek::Monday // (int) 1 * $monday = Enum::ToString( 'DayOfWeek', DayOfWeek::Monday ); // (string)"DayOfWeek::Monday" * $monday = Enum::Label( 'DayOfWeek', DayOfWeek::Monday ); // (string)"Monday" * **/ abstract class Enum { // make sure there are never any instances created final private function __construct() { throw new Exception( 'Enum and Subclasses cannot be instantiated.' ); } /** * Give the integer associated with the const of the given string in the format of"class:const" * * @param string $string * @return integer */ final public static function FromString( $string ) { if ( strpos( $string, '::' ) < 1 ) { throw new Exception( 'Enum::FromString( $string ) Input string is not in the expected format.' ); } list( $class, $const ) = explode( '::', $string ); if ( class_exists( $class, false ) ) { $reflector = new ReflectionClass( $class ); if ( $reflector->IsSubClassOf( 'Enum' ) ) { if ( $reflector->hasConstant( $const ) ) { return eval( sprintf( 'return %s;', $string ) ); } } } throw new Excption( sprintf( '%s does not map to an Enum field', $string ) ); } final public static function IsValidValue( $enumType, $enumValue ) { if ( class_exists( $enumType ) ) { $reflector = new ReflectionClass( $enumType ); if ( $reflector->IsSubClassOf( 'Enum' ) ) { foreach( $reflector->getConstants() as $label => $value ) { if ( $value == $enumValue ) { return true; } } } } return false; } final public static function IsValidLabel( $enumType, $enumValue ) { if ( class_exists( $enumType ) ) { $reflector = new ReflectionClass( $enumType ); if ( $reflector->IsSubClassOf( 'Enum' ) ) { foreach( $reflector->getConstants() as $label => $value ) { if ( $label == $enumValue ) { return true; } } } } return false; } /** * For a given $enumType, give the complete string representation for the given $enumValue (class::const) * * @param string $enumType * @param integer $enumValue * @return string */ final public static function ToString( $enumType, $enumValue ) { $result = 'NotAnEnum::IllegalValue'; if ( class_exists( $enumType, false ) ) { $reflector = new ReflectionClass( $enumType ); $result = $reflector->getName() . '::IllegalValue'; foreach( $reflector->getConstants() as $key => $val ) { if ( $val == $enumValue ) { $result = str_replace( 'IllegalValue', $key, $result ); break; } } } return $result; } /** * For a given $enumType, give the label associated with the given $enumValue (const name in class definition) * * @param string $enumType * @param integer $enumValue * @return string */ final public static function Label( $enumType, $enumValue ) { $result = 'IllegalValue'; if ( class_exists( $enumType, false ) ) { $reflector = new ReflectionClass( $enumType ); foreach( $reflector->getConstants() as $key => $val ) { if ( $val == $enumValue ) { $result = $key; break; } } } return $result; } } ?> |
这是@kris代码的一个更新版本,可以更好地与较新版本的PHP配合使用。它是根据@lassombra评论制作的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | /** * Implements the abstract base for all enum types * @see http://stackoverflow.com/a/2324746/1003020 * @see http://stackoverflow.com/a/254543/1003020 * * Example of a typical enum: * * class DayOfWeek extends Enum * { * const Sunday = 0; * const Monday = 1; * const Tuesday = 2; * const Wednesday = 3; * const Thursday = 4; * const Friday = 5; * const Saturday = 6; * } * * Usage examples: * * $monday = DayOfWeek::Monday // (int) 1 * DayOfWeek::isValidName('Monday') // (bool) true * DayOfWeek::isValidName('monday', $strict = true) // (bool) false * DayOfWeek::isValidValue(0) // (bool) true * DayOfWeek::fromString('Monday') // (int) 1 * DayOfWeek::toString(DayOfWeek::Tuesday) // (string)"Tuesday" * DayOfWeek::toString(5) // (string)"Friday" **/ abstract class Enum { private static $constCacheArray = NULL; private static function getConstants() { if (self::$constCacheArray == NULL) { self::$constCacheArray = []; } $calledClass = get_called_class(); if (!array_key_exists($calledClass, self::$constCacheArray)) { $reflect = new eflectionClass($calledClass); self::$constCacheArray[$calledClass] = $reflect->getConstants(); } return self::$constCacheArray[$calledClass]; } public static function isValidName($name, $strict = false) { $constants = self::getConstants(); if ($strict) { return array_key_exists($name, $constants); } $keys = array_map('strtolower', array_keys($constants)); return in_array(strtolower($name), $keys); } public static function isValidValue($value, $strict = true) { $values = array_values(self::getConstants()); return in_array($value, $values, $strict); } public static function fromString($name) { if (self::isValidName($name, $strict = true)) { $constants = self::getConstants(); return $constants[$name]; } return false; } public static function toString($value) { if (self::isValidValue($value, $strict = true)) { return array_search($value, self::getConstants()); } return false; } } |
您也可以使用这个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class Enum{ private $m_valueName = NULL; private function __construct($valueName){ $this->m_valueName = $valueName; } public static function __callStatic($methodName, $arguments){ $className = get_called_class(); return new $className($methodName); } function __toString(){ return $this->m_valueName; } } class NotificationType extends Enum{ const Notification = NULL; const Warning = NULL; const Error = NULL; } function Test(NotificationType $type){ echo"Test function, type: $type"; } Test(NotificationType::Warning()); |
提供了一个
文档中的示例用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?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; try { new Month(13); } catch (UnexpectedValueException $uve) { echo $uve->getMessage() . PHP_EOL; } |
上面的示例将输出
1 2 | 6 Value not a const in enum Month |
另一种可能是使用myclabs/php枚举包。
可以使用常量
1 2 3 4 | class myClass { const aValue = 123; const aString ="ABC"; }; |
但它不会提供一种很好的迭代方法,因此我可能会选择关联数组,因为这样更容易管理:
1 2 3 4 5 |
在我的例子中,我需要存储整个应用程序中使用的权限名称。我最后得到了一个基本枚举抽象类,它为枚举定义了几个实用函数,然后对其进行了扩展。下面是基本枚举类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <?php namespace App\Enums; use ReflectionClass; abstract class BasicEnum { private static $constCacheArray = NULL; public static function getConstants() { if (self::$constCacheArray == NULL) { self::$constCacheArray = []; } $calledClass = get_called_class(); if (!array_key_exists($calledClass, self::$constCacheArray)) { $reflect = new ReflectionClass($calledClass); self::$constCacheArray[$calledClass] = $reflect->getConstants(); } return self::$constCacheArray[$calledClass]; } public static function isValidName($name, $strict = false) { $constants = self::getConstants(); if ($strict) { return array_key_exists($name, $constants); } $keys = array_map('strtolower', array_keys($constants)); return in_array(strtolower($name), $keys); } public static function isValidValue($value, $strict = true) { $values = array_values(self::getConstants()); return in_array($value, $values, $strict); } } |
下面是通过扩展抽象类创建的枚举示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <?php namespace App\Enums; class Permissions extends BasicEnum { const COMMENTS_CREATE = 'create comments'; const COMMENTS_VIEW = 'view comments'; const COMMENTS_EDIT = 'edit comments'; const COMMENTS_DELETE = 'delete comments'; const COMMENTS_RESTORE = 'restore comments'; const REACTIONS_CREATE = 'create reactions'; const REACTIONS_VIEW = 'view reactions'; const REACTIONS_EDIT = 'edit reactions'; const REACTIONS_DELETE = 'delete reactions'; const REACTIONS_RESTORE = 'restore reactions'; const QUESTIONS_CREATE = 'create questions'; const QUESTIONS_VIEW = 'view questions'; const QUESTIONS_EDIT = 'edit questions'; const QUESTIONS_DELETE = 'delete questions'; const QUESTIONS_RESTORE = 'restore questions'; const PERMISSIONS_CREATE = 'create permissions'; const PERMISSIONS_VIEW = 'view permissions'; const PERMISSIONS_EDIT = 'edit permissions'; const PERMISSIONS_DELETE = 'delete permissions'; const PERMISSIONS_RESTORE = 'restore permissions'; } |
1 2 3 4 5 6 7 8 9 10 11 | <?php namespace App\Enums; class PostTypes extends BasicEnum { const POST = 'post'; const NEWS = 'news'; const SERVICE = 'service'; } |
下面是权限枚举的一个示例用法:
1 2 3 4 5 6 7 8 9 10 | /** * Determine whether the user can create reactions. * * @param User $user * @return mixed */ public function create(User $user) { return $user->can(Permissions::REACTIONS_CREATE); } |
希望这有帮助。
一个便宜的技巧是用可能的值创建一个数组。但是,与上面的答案不同,我选择一个键/值对相等的数组,即:
1 2 3 4 5 6 7 |
这样,如果
当然,如果您希望键/值对不同,可以使用常规数组。
作为数组。
1 2 3 4 5 |