我知道PHP没有本机枚举.但我已经从Java世界习惯了它们.我希望使用枚举作为一种方式来提供IDE的自动完成功能可以理解的预定义值.
常量可以解决问题,但是存在名称空间冲突问题,并且(或实际上因为)它们是全局的.数组没有命名空间问题,但是它们太模糊了,它们可以在运行时覆盖,IDE很少(从不?)知道如何自动填充其键.
您是否经常使用任何解决方案/解决方法?有谁回忆一下PHP家伙是否对枚举有任何想法或决定?
根据用例,我通常会使用如下简单的东西:
abstract class DaysOfWeek { const Sunday = 0; const Monday = 1; // etc. } $today = DaysOfWeek::Sunday;
但是,其他用例可能需要更多的常量和值验证.根据以下关于反思的评论以及其他一些注释,这里有一个扩展的例子,可以更好地服务于更广泛的案例:
abstract class BasicEnum { 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 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); } }
通过创建一个扩展BasicEnum的简单枚举类,您现在可以使用方法进行简单的输入验证:
abstract class DaysOfWeek extends BasicEnum { const Sunday = 0; const Monday = 1; const Tuesday = 2; const Wednesday = 3; const Thursday = 4; const Friday = 5; const Saturday = 6; } DaysOfWeek::isValidName('Humpday'); // false DaysOfWeek::isValidName('Monday'); // true DaysOfWeek::isValidName('monday'); // true DaysOfWeek::isValidName('monday', $strict = true); // false DaysOfWeek::isValidName(0); // false DaysOfWeek::isValidValue(0); // true DaysOfWeek::isValidValue(5); // true DaysOfWeek::isValidValue(7); // false DaysOfWeek::isValidValue('Friday'); // false
作为旁注,任何时候我在静态/ const类上使用反射至少一次数据不会改变(例如在枚举中),我会缓存那些反射调用的结果,因为每次使用新的反射对象最终会产生明显的性能影响(存储在多个枚举的关联数组中).
现在大多数人已经最终升级到至少5.3,并且SplEnum
可用,这当然是一个可行的选择 - 只要你不介意在整个代码库中实际实现枚举实例的传统不直观的概念.在上述例子中,BasicEnum
并DaysOfWeek
不能在所有的实例化,也不应.
还有一个原生扩展.该SplEnum
SplEnum提供了在PHP中本地模拟和创建枚举对象的功能.
http://www.php.net/manual/en/class.splenum.php
类常量怎么样?
echoConstant();
上面的最佳答案是太棒了.但是,如果您extend
以两种不同的方式使用它,那么无论哪个扩展都先完成,导致对函数的调用将创建缓存.然后,所有后续呼叫都将使用此缓存,无论呼叫由哪个分机发起......
要解决此问题,请将变量和第一个函数替换为:
private static $constCacheArray = null; private static function getConstants() { if (self::$constCacheArray === null) self::$constCacheArray = array(); $calledClass = get_called_class(); if (!array_key_exists($calledClass, self::$constCacheArray)) { $reflect = new \ReflectionClass($calledClass); self::$constCacheArray[$calledClass] = $reflect->getConstants(); } return self::$constCacheArray[$calledClass]; }
我使用interface
而不是class
:
interface DaysOfWeek { const Sunday = 0; const Monday = 1; // etc. } var $today = DaysOfWeek::Sunday;
我使用了常量类:
class Enum { const NAME = 'aaaa'; const SOME_VALUE = 'bbbb'; } print Enum::NAME;
我在这里评论了其他一些答案,所以我想我也会权衡.在一天结束时,由于PHP不支持键入的枚举,您可以采用以下两种方法之一:破解类型枚举,或者生活在它们非常难以有效破解的事实中.
我更喜欢接受这个事实,而是使用const
此处其他答案以某种方式使用的方法:
abstract class Enum { const NONE = null; final private function __construct() { throw new NotSupportedException(); // } final private function __clone() { throw new NotSupportedException(); } final public static function toArray() { return (new ReflectionClass(static::class))->getConstants(); } final public static function isValid($value) { return in_array($value, static::toArray()); } }
枚举示例:
final class ResponseStatusCode extends Enum { const OK = 200; const CREATED = 201; const ACCEPTED = 202; // ... const SERVICE_UNAVAILABLE = 503; const GATEWAY_TIME_OUT = 504; const HTTP_VERSION_NOT_SUPPORTED = 505; }
使用Enum
作为基类,所有其他枚举延伸允许辅助方法,诸如toArray
,isValid
等.对我来说,键入的枚举(以及管理它们的实例)最终会变得太乱.
如果,存在一种__getStatic
神奇的方法(并且最好也是一种__equals
神奇的方法),可以用一种多重模式来缓解这种情况.
(以下是假设的,它不会工作,虽然也许有一天它会)
final class TestEnum { private static $_values = [ 'FOO' => 1, 'BAR' => 2, 'QUX' => 3, ]; private static $_instances = []; public static function __getStatic($name) { if (isset(static::$_values[$name])) { if (empty(static::$_instances[$name])) { static::$_instances[$name] = new static($name); } return static::$_instances[$name]; } throw new Exception(sprintf('Invalid enumeration value, "%s"', $name)); } private $_value; public function __construct($name) { $this->_value = static::$_values[$name]; } public function __equals($object) { if ($object instanceof static) { return $object->_value === $this->_value; } return $object === $this->_value; } } $foo = TestEnum::$FOO; // object(TestEnum)#1 (1) { // ["_value":"TestEnum":private]=> // int(1) // } $zap = TestEnum::$ZAP; // Uncaught exception 'Exception' with message // 'Invalid enumeration member, "ZAP"' $qux = TestEnum::$QUX; TestEnum::$QUX == $qux; // true 'hello world!' == $qux; // false
好吧,对于像php这样的简单java,我使用:
class SomeTypeName { private static $enum = array(1 => "Read", 2 => "Write"); public function toOrdinal($name) { return array_search($name, self::$enum); } public function toString($ordinal) { return self::$enum[$ordinal]; } }
并称之为:
SomeTypeName::toOrdinal("Read"); SomeTypeName::toString(1);
但我是一个PHP初学者,在语法方面苦苦挣扎,所以这可能不是最好的方法.我用类常量实验了一些,使用Reflection从它的值中获取常量名称,可能更整洁.
四年后,我又遇到了这个.我目前的方法是这样,因为它允许在IDE中完成代码以及类型安全:
基类:
abstract class TypedEnum { private static $_instancedValues; private $_value; private $_name; private function __construct($value, $name) { $this->_value = $value; $this->_name = $name; } private static function _fromGetter($getter, $value) { $reflectionClass = new ReflectionClass(get_called_class()); $methods = $reflectionClass->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC); $className = get_called_class(); foreach($methods as $method) { if ($method->class === $className) { $enumItem = $method->invoke(null); if ($enumItem instanceof $className && $enumItem->$getter() === $value) { return $enumItem; } } } throw new OutOfRangeException(); } protected static function _create($value) { if (self::$_instancedValues === null) { self::$_instancedValues = array(); } $className = get_called_class(); if (!isset(self::$_instancedValues[$className])) { self::$_instancedValues[$className] = array(); } if (!isset(self::$_instancedValues[$className][$value])) { $debugTrace = debug_backtrace(); $lastCaller = array_shift($debugTrace); while ($lastCaller['class'] !== $className && count($debugTrace) > 0) { $lastCaller = array_shift($debugTrace); } self::$_instancedValues[$className][$value] = new static($value, $lastCaller['function']); } return self::$_instancedValues[$className][$value]; } public static function fromValue($value) { return self::_fromGetter('getValue', $value); } public static function fromName($value) { return self::_fromGetter('getName', $value); } public function getValue() { return $this->_value; } public function getName() { return $this->_name; } }
示例枚举:
final class DaysOfWeek extends TypedEnum { public static function Sunday() { return self::_create(0); } public static function Monday() { return self::_create(1); } public static function Tuesday() { return self::_create(2); } public static function Wednesday() { return self::_create(3); } public static function Thursday() { return self::_create(4); } public static function Friday() { return self::_create(5); } public static function Saturday() { return self::_create(6); } }
用法示例:
function saveEvent(DaysOfWeek $weekDay, $comment) { // store week day numeric value and comment: $myDatabase->save('myeventtable', array('weekday_id' => $weekDay->getValue()), array('comment' => $comment)); } // call the function, note: DaysOfWeek::Monday() returns an object of type DaysOfWeek saveEvent(DaysOfWeek::Monday(), 'some comment');
请注意,相同枚举条目的所有实例都是相同的:
$monday1 = DaysOfWeek::Monday(); $monday2 = DaysOfWeek::Monday(); $monday1 === $monday2; // true
您也可以在switch语句中使用它:
function getGermanWeekDayName(DaysOfWeek $weekDay) { switch ($weekDay) { case DaysOfWeek::Monday(): return 'Montag'; case DaysOfWeek::Tuesday(): return 'Dienstag'; // ... }
您还可以按名称或值创建枚举条目:
$monday = DaysOfWeek::fromValue(2); $tuesday = DaysOfWeek::fromName('Tuesday');
或者您可以从现有的枚举条目中获取名称(即函数名称):
$wednesday = DaysOfWeek::Wednesday() echo $wednesDay->getName(); // Wednesday
如果您需要使用全局唯一的枚举(即使在比较不同枚举之间的元素时)并且易于使用,请随意使用以下代码.我还添加了一些我觉得有用的方法.您可以在代码最顶部的注释中找到示例.
* * @version 1.0 * * This class provides the function of an enumeration. * The values of Enum elements are unique (even between different Enums) * as you would expect them to be. * * Constructing a new Enum: * ======================== * * In the following example we construct an enum called "UserState" * with the elements "inactive", "active", "banned" and "deleted". * ** Enum::Create('UserState', 'inactive', 'active', 'banned', 'deleted'); *
* * Using Enums: * ============ * * The following example demonstrates how to compare two Enum elements * ** var_dump(UserState::inactive == UserState::banned); // result: false * var_dump(UserState::active == UserState::active); // result: true *
* * Special Enum methods: * ===================== * * Get the number of elements in an Enum: * ** echo UserState::CountEntries(); // result: 4 *
* * Get a list with all elements of the Enum: * ** $allUserStates = UserState::GetEntries(); *
* * Get a name of an element: * ** echo UserState::GetName(UserState::deleted); // result: deleted *
* * Get an integer ID for an element (e.g. to store as a value in a database table): * This is simply the index of the element (beginning with 1). * Note that this ID is only unique for this Enum but now between different Enums. * ** echo UserState::GetDatabaseID(UserState::active); // result: 2 *
*/ class Enum { /** * @var Enum $instance The only instance of Enum (Singleton) */ private static $instance; /** * @var array $enums An array of all enums with Enum names as keys * and arrays of element names as values */ private $enums; /** * Constructs (the only) Enum instance */ private function __construct() { $this->enums = array(); } /** * Constructs a new enum * * @param string $name The class name for the enum * @param mixed $_ A list of strings to use as names for enum entries */ public static function Create($name, $_) { // Create (the only) Enum instance if this hasn't happened yet if (self::$instance===null) { self::$instance = new Enum(); } // Fetch the arguments of the function $args = func_get_args(); // Exclude the "name" argument from the array of function arguments, // so only the enum element names remain in the array array_shift($args); self::$instance->add($name, $args); } /** * Creates an enumeration if this hasn't happened yet * * @param string $name The class name for the enum * @param array $fields The names of the enum elements */ private function add($name, $fields) { if (!array_key_exists($name, $this->enums)) { $this->enums[$name] = array(); // Generate the code of the class for this enumeration $classDeclaration = "class " . $name . " {\n" . "private static \$name = '" . $name . "';\n" . $this->getClassConstants($name, $fields) . $this->getFunctionGetEntries($name) . $this->getFunctionCountEntries($name) . $this->getFunctionGetDatabaseID() . $this->getFunctionGetName() . "}"; // Create the class for this enumeration eval($classDeclaration); } } /** * Returns the code of the class constants * for an enumeration. These are the representations * of the elements. * * @param string $name The class name for the enum * @param array $fields The names of the enum elements * * @return string The code of the class constants */ private function getClassConstants($name, $fields) { $constants = ''; foreach ($fields as $field) { // Create a unique ID for the Enum element // This ID is unique because class and variables // names can't contain a semicolon. Therefore we // can use the semicolon as a separator here. $uniqueID = $name . ";" . $field; $constants .= "const " . $field . " = '". $uniqueID . "';\n"; // Store the unique ID array_push($this->enums[$name], $uniqueID); } return $constants; } /** * Returns the code of the function "GetEntries()" * for an enumeration * * @param string $name The class name for the enum * * @return string The code of the function "GetEntries()" */ private function getFunctionGetEntries($name) { $entryList = ''; // Put the unique element IDs in single quotes and // separate them with commas foreach ($this->enums[$name] as $key => $entry) { if ($key > 0) $entryList .= ','; $entryList .= "'" . $entry . "'"; } return "public static function GetEntries() { \n" . " return array(" . $entryList . ");\n" . "}\n"; } /** * Returns the code of the function "CountEntries()" * for an enumeration * * @param string $name The class name for the enum * * @return string The code of the function "CountEntries()" */ private function getFunctionCountEntries($name) { // This function will simply return a constant number (e.g. return 5;) return "public static function CountEntries() { \n" . " return " . count($this->enums[$name]) . ";\n" . "}\n"; } /** * Returns the code of the function "GetDatabaseID()" * for an enumeration * * @return string The code of the function "GetDatabaseID()" */ private function getFunctionGetDatabaseID() { // Check for the index of this element inside of the array // of elements and add +1 return "public static function GetDatabaseID(\$entry) { \n" . "\$key = array_search(\$entry, self::GetEntries());\n" . " return \$key + 1;\n" . "}\n"; } /** * Returns the code of the function "GetName()" * for an enumeration * * @return string The code of the function "GetName()" */ private function getFunctionGetName() { // Remove the class name from the unique ID // and return this value (which is the element name) return "public static function GetName(\$entry) { \n" . "return substr(\$entry, strlen(self::\$name) + 1 , strlen(\$entry));\n" . "}\n"; } } ?>
我也喜欢java中的枚举,因此我以这种方式编写我的枚举,我认为这是Java枚举中最类似的行为,当然,如果有人想使用java中的更多方法应该在这里写,或者抽象类,但核心思想嵌入在下面的代码中
class FruitsEnum { static $APPLE = null; static $ORANGE = null; private $value = null; public static $map; public function __construct($value) { $this->value = $value; } public static function init () { self::$APPLE = new FruitsEnum("Apple"); self::$ORANGE = new FruitsEnum("Orange"); //static map to get object by name - example Enum::get("INIT") - returns Enum::$INIT object; self::$map = array ( "Apple" => self::$APPLE, "Orange" => self::$ORANGE ); } public static function get($element) { if($element == null) return null; return self::$map[$element]; } public function getValue() { return $this->value; } public function equals(FruitsEnum $element) { return $element->getValue() == $this->getValue(); } public function __toString () { return $this->value; } } FruitsEnum::init(); var_dump(FruitsEnum::$APPLE->equals(FruitsEnum::$APPLE)); //true var_dump(FruitsEnum::$APPLE->equals(FruitsEnum::$ORANGE)); //false var_dump(FruitsEnum::$APPLE instanceof FruitsEnum); //true var_dump(FruitsEnum::get("Apple")->equals(FruitsEnum::$APPLE)); //true - enum from string var_dump(FruitsEnum::get("Apple")->equals(FruitsEnum::get("Orange"))); //false
我在github上找到了这个库,我认为它提供了一个非常好的替代方案.
你可以输入提示: function setAction(Action $action) {
你可以用丰富方法枚举(例如format
,parse
,...)
您可以扩展枚举以添加新值(使您的枚举final
可以防止它)
您可以获得所有可能值的列表(参见下文)
用法
type-hint枚举值:
13> jglatre..:abstract class Enumeration { public static function enum() { $reflect = new ReflectionClass( get_called_class() ); return $reflect->getConstants(); } } class Test extends Enumeration { const A = 'a'; const B = 'b'; } foreach (Test::enum() as $key => $value) { echo "$key -> $value
"; }
14> Noah Goodric..:我在PHP中看到的最常见的解决方案是创建一个通用枚举类,然后对其进行扩展.你可以看看这个.
更新:或者,我从phpclasses.org 找到了这个.
15> 小智..:这是一个用于处理php中类型安全枚举的github库:
这个库处理类生成,类缓存,它实现了Type Safe Enumeration设计模式,有几个辅助方法来处理枚举,比如为枚举组合检索枚举排序或检索二进制值的序数.
生成的代码使用一个普通的旧php模板文件,该文件也是可配置的,因此您可以提供自己的模板.
它是用phpunit覆盖的完整测试.
github上的php-enums(随意分叉)
用法:(@ see usage.php,或单元测试了解更多详情)
getName()\n"; foreach (FruitsEnum::iterator() as $enum) { echo " " . $enum->getName() . "\n"; } echo "->getValue()\n"; foreach (FruitsEnum::iterator() as $enum) { echo " " . $enum->getValue() . "\n"; } echo "->getOrdinal()\n"; foreach (CachedFruitsEnum::iterator() as $enum) { echo " " . $enum->getOrdinal() . "\n"; } echo "->getBinary()\n"; foreach (CachedFruitsEnum::iterator() as $enum) { echo " " . $enum->getBinary() . "\n"; }输出:
FruitsEnum::APPLE() == FruitsEnum::APPLE(): bool(true) FruitsEnum::APPLE() == FruitsEnum::ORANGE(): bool(false) FruitsEnum::APPLE() instanceof Enum: bool(true) FruitsEnum::APPLE() instanceof FruitsEnum: bool(true) ->getName() APPLE ORANGE RASBERRY BANNANA ->getValue() apple orange rasberry bannana ->getValue() when values have been specified pig dog cat bird ->getOrdinal() 1 2 3 4 ->getBinary() 1 2 4 8
16> mykhal..:它可能很简单
enum DaysOfWeek { Sunday, Monday, // ... }在将来.
PHP RFC:枚举类型