我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
当前回答
踩在布莱恩·克莱恩的回答上,我想我可能会给出我的5美分
<?php
/**
* A class that simulates Enums behaviour
* <code>
* class Season extends Enum{
* const Spring = 0;
* const Summer = 1;
* const Autumn = 2;
* const Winter = 3;
* }
*
* $currentSeason = new Season(Season::Spring);
* $nextYearSeason = new Season(Season::Spring);
* $winter = new Season(Season::Winter);
* $whatever = new Season(-1); // Throws InvalidArgumentException
* echo $currentSeason.is(Season::Spring); // True
* echo $currentSeason.getName(); // 'Spring'
* echo $currentSeason.is($nextYearSeason); // True
* echo $currentSeason.is(Season::Winter); // False
* echo $currentSeason.is(Season::Spring); // True
* echo $currentSeason.is($winter); // False
* </code>
*
* Class Enum
*
* PHP Version 5.5
*/
abstract class Enum
{
/**
* Will contain all the constants of every enum that gets created to
* avoid expensive ReflectionClass usage
* @var array
*/
private static $_constCacheArray = [];
/**
* The value that separates this instance from the rest of the same class
* @var mixed
*/
private $_value;
/**
* The label of the Enum instance. Will take the string name of the
* constant provided, used for logging and human readable messages
* @var string
*/
private $_name;
/**
* Creates an enum instance, while makes sure that the value given to the
* enum is a valid one
*
* @param mixed $value The value of the current
*
* @throws \InvalidArgumentException
*/
public final function __construct($value)
{
$constants = self::_getConstants();
if (count($constants) !== count(array_unique($constants))) {
throw new \InvalidArgumentException('Enums cannot contain duplicate constant values');
}
if ($name = array_search($value, $constants)) {
$this->_value = $value;
$this->_name = $name;
} else {
throw new \InvalidArgumentException('Invalid enum value provided');
}
}
/**
* Returns the constant name of the current enum instance
*
* @return string
*/
public function getName()
{
return $this->_name;
}
/**
* Returns the value of the current enum instance
*
* @return mixed
*/
public function getValue()
{
return $this->_value;
}
/**
* Checks whether this enum instance matches with the provided one.
* This function should be used to compare Enums at all times instead
* of an identity comparison
* <code>
* // Assuming EnumObject and EnumObject2 both extend the Enum class
* // and constants with such values are defined
* $var = new EnumObject('test');
* $var2 = new EnumObject('test');
* $var3 = new EnumObject2('test');
* $var4 = new EnumObject2('test2');
* echo $var->is($var2); // true
* echo $var->is('test'); // true
* echo $var->is($var3); // false
* echo $var3->is($var4); // false
* </code>
*
* @param mixed|Enum $enum The value we are comparing this enum object against
* If the value is instance of the Enum class makes
* sure they are instances of the same class as well,
* otherwise just ensures they have the same value
*
* @return bool
*/
public final function is($enum)
{
// If we are comparing enums, just make
// sure they have the same toString value
if (is_subclass_of($enum, __CLASS__)) {
return get_class($this) === get_class($enum)
&& $this->getValue() === $enum->getValue();
} else {
// Otherwise assume $enum is the value we are comparing against
// and do an exact comparison
return $this->getValue() === $enum;
}
}
/**
* Returns the constants that are set for the current Enum instance
*
* @return array
*/
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];
}
}
其他回答
我使用带有常量的类:
class Enum {
const NAME = 'aaaa';
const SOME_VALUE = 'bbbb';
}
print Enum::NAME;
我所见过的PHP中枚举最常见的解决方案是创建一个通用枚举类,然后扩展它。你可以看看这个。
更新:或者,我从phpclasses.org找到了这个。
我已经开始使用下面的方法,因为它使我能够对函数参数具有类型安全,在NetBeans中自动完成和良好的性能。我不太喜欢的一件事是你必须调用[扩展类名]::enumerate();在定义类之后。
abstract class Enum {
private $_value;
protected function __construct($value) {
$this->_value = $value;
}
public function __toString() {
return (string) $this->_value;
}
public static function enumerate() {
$class = get_called_class();
$ref = new ReflectionClass($class);
$statics = $ref->getStaticProperties();
foreach ($statics as $name => $value) {
$ref->setStaticPropertyValue($name, new $class($value));
}
}
}
class DaysOfWeek extends Enum {
public static $MONDAY = 0;
public static $SUNDAY = 1;
// etc.
}
DaysOfWeek::enumerate();
function isMonday(DaysOfWeek $d) {
if ($d == DaysOfWeek::$MONDAY) {
return true;
} else {
return false;
}
}
$day = DaysOfWeek::$MONDAY;
echo (isMonday($day) ? "bummer it's monday" : "Yay! it's not monday");
这里有一些很好的解决方案!
这是我的版本。
它是强类型的 它与IDE自动补全一起工作 枚举由代码和描述定义,其中代码可以是整数、二进制值、短字符串或基本上任何您想要的内容。可以很容易地扩展该模式以支持其他属性。 它支持值(==)和引用(===)比较,并在switch语句中工作。
我认为主要的缺点是枚举成员必须分别声明和实例化,这是由于描述和PHP不能在静态成员声明时构造对象。我想绕过这个问题的一种方法可能是使用带有解析过的文档注释的反射。
抽象枚举看起来像这样:
<?php
abstract class AbstractEnum
{
/** @var array cache of all enum instances by class name and integer value */
private static $allEnumMembers = array();
/** @var mixed */
private $code;
/** @var string */
private $description;
/**
* Return an enum instance of the concrete type on which this static method is called, assuming an instance
* exists for the passed in value. Otherwise an exception is thrown.
*
* @param $code
* @return AbstractEnum
* @throws Exception
*/
public static function getByCode($code)
{
$concreteMembers = &self::getConcreteMembers();
if (array_key_exists($code, $concreteMembers)) {
return $concreteMembers[$code];
}
throw new Exception("Value '$code' does not exist for enum '".get_called_class()."'");
}
public static function getAllMembers()
{
return self::getConcreteMembers();
}
/**
* Create, cache and return an instance of the concrete enum type for the supplied primitive value.
*
* @param mixed $code code to uniquely identify this enum
* @param string $description
* @throws Exception
* @return AbstractEnum
*/
protected static function enum($code, $description)
{
$concreteMembers = &self::getConcreteMembers();
if (array_key_exists($code, $concreteMembers)) {
throw new Exception("Value '$code' has already been added to enum '".get_called_class()."'");
}
$concreteMembers[$code] = $concreteEnumInstance = new static($code, $description);
return $concreteEnumInstance;
}
/**
* @return AbstractEnum[]
*/
private static function &getConcreteMembers() {
$thisClassName = get_called_class();
if (!array_key_exists($thisClassName, self::$allEnumMembers)) {
$concreteMembers = array();
self::$allEnumMembers[$thisClassName] = $concreteMembers;
}
return self::$allEnumMembers[$thisClassName];
}
private function __construct($code, $description)
{
$this->code = $code;
$this->description = $description;
}
public function getCode()
{
return $this->code;
}
public function getDescription()
{
return $this->description;
}
}
下面是一个具体枚举示例:
<?php
require('AbstractEnum.php');
class EMyEnum extends AbstractEnum
{
/** @var EMyEnum */
public static $MY_FIRST_VALUE;
/** @var EMyEnum */
public static $MY_SECOND_VALUE;
/** @var EMyEnum */
public static $MY_THIRD_VALUE;
public static function _init()
{
self::$MY_FIRST_VALUE = self::enum(1, 'My first value');
self::$MY_SECOND_VALUE = self::enum(2, 'My second value');
self::$MY_THIRD_VALUE = self::enum(3, 'My third value');
}
}
EMyEnum::_init();
可以这样使用:
<?php
require('EMyEnum.php');
echo EMyEnum::$MY_FIRST_VALUE->getCode().' : '.EMyEnum::$MY_FIRST_VALUE->getDescription().PHP_EOL.PHP_EOL;
var_dump(EMyEnum::getAllMembers());
echo PHP_EOL.EMyEnum::getByCode(2)->getDescription().PHP_EOL;
并产生如下输出:
1 : My first value array(3) { [1]=> object(EMyEnum)#1 (2) { ["code":"AbstractEnum":private]=> int(1) ["description":"AbstractEnum":private]=> string(14) "My first value" } [2]=> object(EMyEnum)#2 (2) { ["code":"AbstractEnum":private]=> int(2) ["description":"AbstractEnum":private]=> string(15) "My second value" } [3]=> object(EMyEnum)#3 (2) { ["code":"AbstractEnum":private]=> int(3) ["description":"AbstractEnum":private]=> string(14) "My third value" } } My second value
我试图用PHP创建一个枚举…这是非常有限的,因为它不支持对象作为枚举值,但仍然有点有用…
class ProtocolsEnum {
const HTTP = '1';
const HTTPS = '2';
const FTP = '3';
/**
* Retrieve an enum value
* @param string $name
* @return string
*/
public static function getValueByName($name) {
return constant('self::'. $name);
}
/**
* Retrieve an enum key name
* @param string $code
* @return string
*/
public static function getNameByValue($code) {
foreach(get_class_constants() as $key => $val) {
if($val == $code) {
return $key;
}
}
}
/**
* Retrieve associate array of all constants (used for creating droplist options)
* @return multitype:
*/
public static function toArray() {
return array_flip(self::get_class_constants());
}
private static function get_class_constants()
{
$reflect = new ReflectionClass(__CLASS__);
return $reflect->getConstants();
}
}