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

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

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


当前回答

好吧,对于一个简单的java,比如php中的enum,我使用:

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初学者,语法不好,所以这可能不是最好的方法。我尝试了一些类常量,使用反射从它的值中获得常量名,可能更整洁。

其他回答

编辑:从PHP 8.1开始,支持枚举:https://www.php.net/manual/en/language.types.enumerations.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类上至少使用一次反射,其中数据不会改变(例如在enum中),我都会缓存那些反射调用的结果,因为每次使用新的反射对象最终都会有明显的性能影响(存储在多个enum的关联数组中)。

现在大多数人已经升级到至少5.3版本,而且也有了脾脏,这当然也是一个可行的选择——只要你不介意在你的代码库中使用实际枚举实例化这种传统的不直观的概念。在上面的例子中,BasicEnum和DaysOfWeek根本不能被实例化,也不应该被实例化。

我知道这是一个旧线程,然而没有一个我所见过的解决方法真的看起来像枚举,因为几乎所有的解决方法都需要你手动分配值给枚举项,或者它需要你传递一个枚举键数组到一个函数。所以我创造了自己的解决方案。

要使用我的解决方案创建枚举类,可以简单地扩展下面的enum类,创建一堆静态变量(不需要初始化它们),并在枚举类定义的下面调用yourEnumClass::init()。

edit: This only works in php >= 5.3, but it can probably be modified to work in older versions as well /** * A base class for enums. * * This class can be used as a base class for enums. * It can be used to create regular enums (incremental indices), but it can also be used to create binary flag values. * To create an enum class you can simply extend this class, and make a call to <yourEnumClass>::init() before you use the enum. * Preferably this call is made directly after the class declaration. * Example usages: * DaysOfTheWeek.class.php * abstract class DaysOfTheWeek extends Enum{ * static $MONDAY = 1; * static $TUESDAY; * static $WEDNESDAY; * static $THURSDAY; * static $FRIDAY; * static $SATURDAY; * static $SUNDAY; * } * DaysOfTheWeek::init(); * * example.php * require_once("DaysOfTheWeek.class.php"); * $today = date('N'); * if ($today == DaysOfTheWeek::$SUNDAY || $today == DaysOfTheWeek::$SATURDAY) * echo "It's weekend!"; * * Flags.class.php * abstract class Flags extends Enum{ * static $FLAG_1; * static $FLAG_2; * static $FLAG_3; * } * Flags::init(Enum::$BINARY_FLAG); * * example2.php * require_once("Flags.class.php"); * $flags = Flags::$FLAG_1 | Flags::$FLAG_2; * if ($flags & Flags::$FLAG_1) * echo "Flag_1 is set"; * * @author Tiddo Langerak */ abstract class Enum{ static $BINARY_FLAG = 1; /** * This function must be called to initialize the enumeration! * * @param bool $flags If the USE_BINARY flag is provided, the enum values will be binary flag values. Default: no flags set. */ public static function init($flags = 0){ //First, we want to get a list of all static properties of the enum class. We'll use the ReflectionClass for this. $enum = get_called_class(); $ref = new ReflectionClass($enum); $items = $ref->getStaticProperties(); //Now we can start assigning values to the items. if ($flags & self::$BINARY_FLAG){ //If we want binary flag values, our first value should be 1. $value = 1; //Now we can set the values for all items. foreach ($items as $key=>$item){ if (!isset($item)){ //If no value is set manually, we should set it. $enum::$$key = $value; //And we need to calculate the new value $value *= 2; } else { //If there was already a value set, we will continue starting from that value, but only if that was a valid binary flag value. //Otherwise, we will just skip this item. if ($key != 0 && ($key & ($key - 1) == 0)) $value = 2 * $item; } } } else { //If we want to use regular indices, we'll start with index 0. $value = 0; //Now we can set the values for all items. foreach ($items as $key=>$item){ if (!isset($item)){ //If no value is set manually, we should set it, and increment the value for the next item. $enum::$$key = $value; $value++; } else { //If a value was already set, we'll continue from that value. $value = $item+1; } } } } }

类常量呢?

<?php

class YourClass
{
    const SOME_CONSTANT = 1;

    public function echoConstant()
    {
        echo self::SOME_CONSTANT;
    }
}

echo YourClass::SOME_CONSTANT;

$c = new YourClass;
$c->echoConstant();

一个不使用反射的更简单、更轻的版本:

abstract class enum {
    private function __construct() {}
    static function has($const) {
        $name = get_called_class();
        return defined("$name::$const");
    }
    static function value($const) {
        $name = get_called_class();
        return defined("$name::$const")? constant("$name::$const") : false;
    }
}

用法:

class requestFormat  extends enum { const HTML = 1; const JSON = 2; const XML  = 3; const FORM = 4; }

echo requestFormat::value('JSON'); // 2
echo requestFormat::has('JSON');   // true

这提供了常量的优势,也允许检查它们的有效性,但它缺乏更复杂的解决方案所提供的其他花哨功能,更明显的是无法检查值的反向(在上面的例子中,你不能检查'2'是否是一个有效值)

现在您可以使用The脾脏类来原生构建它。根据官方文件。

脾提供了模拟和创建枚举对象的能力 原生的PHP。

<?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;
}
?>

请注意,这是一个必须安装的扩展,但默认情况下不可用。在PHP网站上描述的特殊类型下。上面的示例取自PHP站点。