我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。

常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。

你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?


当前回答

// My Enumeration Class
class Enum
{
    protected $m_actions = array();

    public function __construct($actions)
    {
        $this->init($actions);
    }

    public function init($actions)
    {
        $this->m_actions = array();
        for($i = 0; $i < count($actions); ++$i)
        {
            $this->m_actions[$actions[$i]] = ($i + 1); 
            define($actions[$i], ($i + 1));
        }
    }

    public function toString($index)
    {
        $keys = array_keys($this->m_actions);
        for($i = 0; $i < count($keys); ++$i)
        {
            if($this->m_actions[$keys[$i]] == $index)
            {
                return $keys[$i];
            }
        }

        return "undefined";
    }

    public function fromString($str)
    {
        return $this->m_actions[$str];
    }
}

// Enumeration creation
$actions = new Enum(array("CREATE", "READ", "UPDATE", "DELETE"));

// Examples
print($action_objects->toString(DELETE));
print($action_objects->fromString("DELETE"));

if($action_objects->fromString($_POST["myAction"]) == CREATE)
{
    print("CREATE");
}

其他回答

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<br>";
}

我也喜欢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

我使用带有常量的类:

class Enum {
    const NAME       = 'aaaa';
    const SOME_VALUE = 'bbbb';
}

print Enum::NAME;

提出的解决方案效果很好。干净光滑。

然而,如果你想要强类型的枚举,你可以使用这个:

class TestEnum extends Enum
{
    public static $TEST1;
    public static $TEST2;
}
TestEnum::init(); // Automatically initializes enum values

枚举类如下所示:

class Enum
{
    public static function parse($enum)
    {
        $class = get_called_class();
        $vars = get_class_vars($class);
        if (array_key_exists($enum, $vars)) {
            return $vars[$enum];
        }
        return null;
    }

    public static function init()
    {
        $className = get_called_class();
        $consts = get_class_vars($className);
        foreach ($consts as $constant => $value) {
            if (is_null($className::$$constant)) {
                $constantValue = $constant;
                $constantValueName = $className . '::' . $constant . '_VALUE';
                if (defined($constantValueName)) {
                    $constantValue = constant($constantValueName);
                }
                $className::$$constant = new $className($constantValue);
            }
        }
    }

    public function __construct($value)
    {
        $this->value = $value;
    }
}

这样,枚举值是强类型的和

$TEST1 == TEST1::parse('TEST1') // true语句

公认的答案是要走的路,实际上这是我所做的简单。枚举提供了大多数优点(可读、快速等)。然而,这里缺少一个概念:类型安全。在大多数语言中,枚举也用于限制允许的值。下面是一个通过使用私有构造函数、静态实例化方法和类型检查来获得类型安全的例子:

class DaysOfWeek{
 const Sunday = 0;
 const Monday = 1;
 // etc.

 private $intVal;
 private function __construct($intVal){
   $this->intVal = $intVal;
 }

 //static instantiation methods
 public static function MONDAY(){
   return new self(self::Monday);
 }
 //etc.
}

//function using type checking
function printDayOfWeek(DaysOfWeek $d){ //compiler can now use type checking
  // to something with $d...
}

//calling the function is safe!
printDayOfWeek(DaysOfWeek::MONDAY());

我们甚至可以更进一步:在DaysOfWeek类中使用常量可能会导致误用:例如,人们可能会这样错误地使用它:

printDayOfWeek(DaysOfWeek::Monday); //triggers a compiler error.

这是错误的(调用整数常量)。我们可以使用私有静态变量而不是常量来防止这种情况:

class DaysOfWeeks{

  private static $monday = 1;
  //etc.

  private $intVal;
  //private constructor
  private function __construct($intVal){
    $this->intVal = $intVal;
  }

  //public instantiation methods
  public static function MONDAY(){
    return new self(self::$monday);
  }
  //etc.


  //convert an instance to its integer value
  public function intVal(){
    return $this->intVal;
  }

}

当然,不可能访问整数常量(这实际上是目的)。intVal方法允许将DaysOfWeek对象转换为其整数表示形式。

请注意,我们甚至可以进一步在实例化方法中实现缓存机制,以在广泛使用枚举的情况下节省内存…

希望这能有所帮助