我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
当前回答
最后,PHP 7.1+给出了一个不能被重写的常量。
/**
* An interface that groups HTTP Accept: header Media Types in one place.
*/
interface MediaTypes
{
/**
* Now, if you have to use these same constants with another class, you can
* without creating funky inheritance / is-a relationships.
* Also, this gets around the single inheritance limitation.
*/
public const HTML = 'text/html';
public const JSON = 'application/json';
public const XML = 'application/xml';
public const TEXT = 'text/plain';
}
/**
* An generic request class.
*/
abstract class Request
{
// Why not put the constants here?
// 1) The logical reuse issue.
// 2) Single Inheritance.
// 3) Overriding is possible.
// Why put class constants here?
// 1) The constant value will not be necessary in other class families.
}
/**
* An incoming / server-side HTTP request class.
*/
class HttpRequest extends Request implements MediaTypes
{
// This class can implement groups of constants as necessary.
}
如果您使用的是名称空间,那么代码补全应该可以工作。
但是,这样做将失去在类族(protected)或单独在类(private)中隐藏常量的能力。根据定义,接口中的所有内容都是公共的。
PHP手册:接口
更新:
PHP 8.1现在有了枚举。
其他回答
下面是一个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
class DayOfWeek {
static $values = array(
self::MONDAY,
self::TUESDAY,
// ...
);
const MONDAY = 0;
const TUESDAY = 1;
// ...
}
$today = DayOfWeek::MONDAY;
// If you want to check if a value is valid
assert( in_array( $today, DayOfWeek::$values ) );
不要使用反射。这使得你很难推断你的代码并追踪某些东西被使用的位置,而且往往会破坏静态分析工具(例如你的IDE中内置的工具)。
一个不使用反射的更简单、更轻的版本:
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'是否是一个有效值)
我使用带有常量的类:
class Enum {
const NAME = 'aaaa';
const SOME_VALUE = 'bbbb';
}
print Enum::NAME;
我所见过的PHP中枚举最常见的解决方案是创建一个通用枚举类,然后扩展它。你可以看看这个。
更新:或者,我从phpclasses.org找到了这个。