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

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

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


当前回答

下面是一个github库,用于在php中处理类型安全的枚举:

这个库处理类生成、类缓存,并实现了Type Safe Enumeration设计模式,使用几个辅助方法来处理枚举,比如为枚举排序检索序号,或为枚举组合检索二进制值。

生成的代码使用一个普通的旧php模板文件,该文件也是可配置的,因此您可以提供自己的模板。

它是由phpunit覆盖的完整测试。

Php-enums在github (feel free to fork)

用法:(@参见Usage .php或单元测试了解更多细节)

<?php
//require the library
require_once __DIR__ . '/src/Enum.func.php';

//if you don't have a cache directory, create one
@mkdir(__DIR__ . '/cache');
EnumGenerator::setDefaultCachedClassesDir(__DIR__ . '/cache');

//Class definition is evaluated on the fly:
Enum('FruitsEnum', array('apple' , 'orange' , 'rasberry' , 'bannana'));

//Class definition is cached in the cache directory for later usage:
Enum('CachedFruitsEnum', array('apple' , 'orange' , 'rasberry' , 'bannana'), '\my\company\name\space', true);

echo 'FruitsEnum::APPLE() == FruitsEnum::APPLE(): ';
var_dump(FruitsEnum::APPLE() == FruitsEnum::APPLE()) . "\n";

echo 'FruitsEnum::APPLE() == FruitsEnum::ORANGE(): ';
var_dump(FruitsEnum::APPLE() == FruitsEnum::ORANGE()) . "\n";

echo 'FruitsEnum::APPLE() instanceof Enum: ';
var_dump(FruitsEnum::APPLE() instanceof Enum) . "\n";

echo 'FruitsEnum::APPLE() instanceof FruitsEnum: ';
var_dump(FruitsEnum::APPLE() instanceof FruitsEnum) . "\n";

echo "->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

其他回答

我所见过的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");

我只是创建了一个库,希望它能完成这项工作。它可以在任何PHP项目中独立使用,并且有一些Laravel的好东西可以让生活变得更简单。我在生产项目中使用它们。

https://github.com/Kwaadpepper/enum

如果你喜欢或不喜欢,不要犹豫提供反馈。 它可以被打印出来,并序列化为JSON。它的定义很简单。

用法非常简单:

$enum = BasicEnum::someValue();
echo $enum->equals(BasicEnum::someValue()) ? 'true' : 'false'; // true
echo $enum->value; // 'someValue' or the value you have defined
echo $enum->label; // 'someValue' or the label you have defined
echo $enum; // 'someValue' or the value you have defined
echo json_encode($enum); // {"label": "someValue", "value: "someValue" }

枚举定义非常简单(值和标签方法是可选的)

/**
 * @method static self post()
 * @method static self about()
 * @method static self contact()
 */
class PostType extends BaseEnum
{
    protected static function values(): array
    {
        return [
            'post' => 0,
            'about' => 1,
            'contact' => 2
        ];
    }

    protected static function labels(): array
    {
        return [
            'post' => 'Regular posts',
            'about' => 'The about page',
            'contact' => 'The contact page'
        ];
    }
}

类常量呢?

<?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 {
    protected $val;

    protected function __construct($arg) {
        $this->val = $arg;
    }

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

    public function __set($arg1, $arg2) {
        throw new Exception("enum does not have property");
    }

    public function __get($arg1) {
        throw new Exception("enum does not have property");
    }

    // not really needed
    public function __call($arg1, $arg2) {
        throw new Exception("enum does not have method");
    }

    // not really needed
    static public function __callStatic($arg1, $arg2) {
        throw new Exception("enum does not have static method");
    }
}

你的枚举:

final class MyEnum extends Enum {
    static public function val1() {
        return new self("val1");
    }

    static public function val2() {
        return new self("val2");
    }

    static public function val3() {
        return new self("val3");
    }
}

测试:

$a = MyEnum::val1();
echo "1.the enum value is '$a'\n";

function consumeMyEnum(MyEnum $arg) {
    return "2.the return value is '$arg'\n";
}

echo consumeMyEnum($a);
$version = explode(".", PHP_VERSION);
if ($version[0] >= 7) {
    try {
        echo consumeMyEnum("val1");
    } catch (TypeError $e) {
        echo "3.passing argument error happens (PHP 7.0 and above)\n";
    }
}

echo ($a == MyEnum::val1()) ? "4.same\n" : "4.different\n";
echo ($a == MyEnum::val2()) ? "5.same\n" : "5.different\n";

$b = MyEnum::val1();
echo ($a == $b)  ? "6.same\n" : "6.different\n";
echo ($a === $b) ? "7.same\n" : "7.different\n";

$c = MyEnum::val2();
echo ($a == $c)  ? "8.same\n" : "8.different\n";
echo ($a === $c) ? "9.same\n" : "9.different\n";

switch ($c) {
    case MyEnum::val1(): echo "10.case of 1st\n"; break;
    case MyEnum::val2(): echo "11.case of 2nd\n"; break;
    case MyEnum::val3(): echo "12.case of 3rd\n"; break;
}

try {
    $a->prop = 10;
} catch (Exception $e) {
    echo "13.set property error happens\n";
}

try {
    echo $a->prop;
} catch (Exception $e) {
    echo "14.get property error happens\n";
}

try {
    echo $a->meth();
} catch (Exception $e) {
    echo "15.method call error happens\n";
}

try {
    echo MyEnum::meth();
} catch (Exception $e) {
    echo "16.static method call error happens\n";
}

class Ordinary {}
echo $a instanceof MyEnum   ? "17.MyEnum instance\n"   : "17.not MyEnum instance\n";
echo $a instanceof Enum     ? "18.Enum instance\n"     : "18.not Enum instance\n";
echo $a instanceof Ordinary ? "19.Ordinary instance\n" : "19.not Ordinary instance\n";

在网上试试:沙盒