我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
当前回答
基于此要点,所有枚举的基类:
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";
在网上试试:沙盒
其他回答
好吧,对于一个简单的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初学者,语法不好,所以这可能不是最好的方法。我尝试了一些类常量,使用反射从它的值中获得常量名,可能更整洁。
我在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) {
// ...
}
我已经在这里评论了一些其他的答案,所以我想我也会发表意见。 最后,由于PHP不支持类型化枚举,您可以选择以下两种方式之一:删除类型化枚举,或者接受它们极难有效删除的事实。
我更倾向于接受事实,而不是使用其他答案以某种方式使用的const方法:
abstract class Enum
{
const NONE = null;
final private function __construct()
{
throw new NotSupportedException(); //
}
final private function __clone()
{
throw new NotSupportedException();
}
final public static function toArray()
{
return (new ReflectionClass(static::class))->getConstants();
}
final public static function isValid($value)
{
return in_array($value, static::toArray());
}
}
枚举示例:
final class ResponseStatusCode extends Enum
{
const OK = 200;
const CREATED = 201;
const ACCEPTED = 202;
// ...
const SERVICE_UNAVAILABLE = 503;
const GATEWAY_TIME_OUT = 504;
const HTTP_VERSION_NOT_SUPPORTED = 505;
}
使用Enum作为所有其他枚举扩展的基类,允许使用诸如toArray、isValid等辅助方法。对我来说,类型化的枚举(以及管理它们的实例)最终太混乱了。
假设
如果存在一个__getStatic魔法方法(最好还有一个__equals魔法方法),那么大部分问题都可以通过一种多吨模式来缓解。
(以下是假设;它不会起作用,尽管也许有一天它会)
final class TestEnum
{
private static $_values = [
'FOO' => 1,
'BAR' => 2,
'QUX' => 3,
];
private static $_instances = [];
public static function __getStatic($name)
{
if (isset(static::$_values[$name]))
{
if (empty(static::$_instances[$name]))
{
static::$_instances[$name] = new static($name);
}
return static::$_instances[$name];
}
throw new Exception(sprintf('Invalid enumeration value, "%s"', $name));
}
private $_value;
public function __construct($name)
{
$this->_value = static::$_values[$name];
}
public function __equals($object)
{
if ($object instanceof static)
{
return $object->_value === $this->_value;
}
return $object === $this->_value;
}
}
$foo = TestEnum::$FOO; // object(TestEnum)#1 (1) {
// ["_value":"TestEnum":private]=>
// int(1)
// }
$zap = TestEnum::$ZAP; // Uncaught exception 'Exception' with message
// 'Invalid enumeration member, "ZAP"'
$qux = TestEnum::$QUX;
TestEnum::$QUX == $qux; // true
'hello world!' == $qux; // false
基于此要点,所有枚举的基类:
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";
在网上试试:沙盒
下面是一个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