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

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

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


当前回答

这可能很简单

enum DaysOfWeek {
    Sunday,
    Monday,
    // ...
}

在未来。

枚举类型

其他回答

我在github上找到了这个库,我认为它提供了一个非常不错的答案。

PHP枚举实现灵感来自脾

你可以输入:function setAction(Action $ Action) { 你可以用方法丰富枚举(例如format, parse,…) 您可以扩展枚举以添加新值(将枚举设置为final以防止出现这种情况) 您可以得到所有可能值的列表(见下面)

宣言

<?php
use MyCLabs\Enum\Enum;

/**
 * Action enum
 */
class Action extends Enum
{
    const VIEW = 'view';
    const EDIT = 'edit';
}

使用

<?php
$action = new Action(Action::VIEW);

// or
$action = Action::VIEW();

类型提示枚举值:

<?php
function setAction(Action $action) {
    // ...
}

下面的Enum类定义是强类型的,使用和定义都很自然。

定义:

class Fruit extends Enum {
    static public $APPLE = 1;
    static public $ORANGE = 2;
}
Fruit::initialize(); //Can also be called in autoloader

切换Enum

$myFruit = Fruit::$APPLE;

switch ($myFruit) {
    case Fruit::$APPLE  : echo "I like apples\n";  break;
    case Fruit::$ORANGE : echo "I hate oranges\n"; break;
}

>> I like apples

传递Enum作为参数(强类型)

/** Function only accepts Fruit enums as input**/
function echoFruit(Fruit $fruit) {
    echo $fruit->getName().": ".$fruit->getValue()."\n";
}

/** Call function with each Enum value that Fruit has */
foreach (Fruit::getList() as $fruit) {
    echoFruit($fruit);
}

//Call function with Apple enum
echoFruit(Fruit::$APPLE)

//Will produce an error. This solution is strongly typed
echoFruit(2);

>> APPLE: 1
>> ORANGE: 2
>> APPLE: 1
>> Argument 1 passed to echoFruit() must be an instance of Fruit, integer given

以字符串形式返回Enum

echo "I have an $myFruit\n";

>> I have an APPLE

通过整数获取Enum

$myFruit = Fruit::getByValue(2);

echo "Now I have an $myFruit\n";

>> Now I have an ORANGE

按名称获取Enum

$myFruit = Fruit::getByName("APPLE");

echo "But I definitely prefer an $myFruit\n\n";

>> But I definitely prefer an APPLE

枚举类:

/**
 * @author Torge Kummerow
 */
class Enum {

    /**
     * Holds the values for each type of Enum
     */
    static private $list = array();

    /**
     * Initializes the enum values by replacing the number with an instance of itself
     * using reflection
     */
    static public function initialize() {
        $className = get_called_class();
        $class = new ReflectionClass($className);
        $staticProperties = $class->getStaticProperties();

        self::$list[$className] = array();

        foreach ($staticProperties as $propertyName => &$value) {
            if ($propertyName == 'list')
                continue;

            $enum = new $className($propertyName, $value);
            $class->setStaticPropertyValue($propertyName, $enum);
            self::$list[$className][$propertyName] = $enum;
        } unset($value);
    }


    /**
     * Gets the enum for the given value
     *
     * @param integer $value
     * @throws Exception
     *
     * @return Enum
     */
    static public function getByValue($value) {
        $className = get_called_class();
        foreach (self::$list[$className] as $propertyName=>&$enum) {
            /* @var $enum Enum */
            if ($enum->value == $value)
                return $enum;
        } unset($enum);

        throw new Exception("No such enum with value=$value of type ".get_called_class());
    }

    /**
     * Gets the enum for the given name
     *
     * @param string $name
     * @throws Exception
     *
     * @return Enum
     */
    static public function getByName($name) {
        $className = get_called_class();
        if (array_key_exists($name, static::$list[$className]))
            return self::$list[$className][$name];

        throw new Exception("No such enum ".get_called_class()."::\$$name");
    }


    /**
     * Returns the list of all enum variants
     * @return Array of Enum
     */
    static public function getList() {
        $className = get_called_class();
        return self::$list[$className];
    }


    private $name;
    private $value;

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

    public function __toString() {
        return $this->name;
    }

    public function getValue() {
        return $this->value;
    }

    public function getName() {
        return $this->name;
    }

}

除了

当然,您也可以为ide添加注释

class Fruit extends Enum {

    /**
     * This comment is for autocomplete support in common IDEs
     * @var Fruit A yummy apple
     */
    static public $APPLE = 1;

    /**
     * This comment is for autocomplete support in common IDEs
     * @var Fruit A sour orange
     */
    static public $ORANGE = 2;
}

//This can also go to the autoloader if available.
Fruit::initialize();

如果您需要使用全局唯一的enum(即即使在不同enum之间比较元素时)并且易于使用,请随意使用以下代码。我还添加了一些我认为有用的方法。您可以在代码顶部的注释中找到示例。

<?php

/**
 * Class Enum
 * 
 * @author Christopher Fox <christopher.fox@gmx.de>
 *
 * @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".
 * 
 * <code>
 * Enum::Create('UserState', 'inactive', 'active', 'banned', 'deleted');
 * </code>
 *
 * Using Enums:
 * ============
 *
 * The following example demonstrates how to compare two Enum elements
 *
 * <code>
 * var_dump(UserState::inactive == UserState::banned); // result: false
 * var_dump(UserState::active == UserState::active); // result: true
 * </code>
 *
 * Special Enum methods:
 * =====================
 *
 * Get the number of elements in an Enum:
 *
 * <code>
 * echo UserState::CountEntries(); // result: 4
 * </code>
 *
 * Get a list with all elements of the Enum:
 *
 * <code>
 * $allUserStates = UserState::GetEntries();
 * </code>
 *
 * Get a name of an element:
 *
 * <code>
 * echo UserState::GetName(UserState::deleted); // result: deleted
 * </code>
 *
 * 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.
 *
 * <code>
 * echo UserState::GetDatabaseID(UserState::active); // result: 2
 * </code>
 */
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";
    }

}


?>

上面的答案太棒了。但是,如果以两种不同的方式进行扩展,那么无论先进行哪种扩展,都会导致对函数的调用,从而创建缓存。这个缓存将被所有后续调用使用,无论调用是由哪个扩展发起的…

要解决这个问题,将变量和第一个函数替换为:

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];
}
// 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");
}