我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
当前回答
我只是创建了一个库,希望它能完成这项工作。它可以在任何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'
];
}
}
其他回答
我在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) {
// ...
}
四年后,我又遇到了这个。我目前的方法是这样的,因为它允许在IDE中完成代码以及类型安全:
基类:
abstract class TypedEnum
{
private static $_instancedValues;
private $_value;
private $_name;
private function __construct($value, $name)
{
$this->_value = $value;
$this->_name = $name;
}
private static function _fromGetter($getter, $value)
{
$reflectionClass = new ReflectionClass(get_called_class());
$methods = $reflectionClass->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC);
$className = get_called_class();
foreach($methods as $method)
{
if ($method->class === $className)
{
$enumItem = $method->invoke(null);
if ($enumItem instanceof $className && $enumItem->$getter() === $value)
{
return $enumItem;
}
}
}
throw new OutOfRangeException();
}
protected static function _create($value)
{
if (self::$_instancedValues === null)
{
self::$_instancedValues = array();
}
$className = get_called_class();
if (!isset(self::$_instancedValues[$className]))
{
self::$_instancedValues[$className] = array();
}
if (!isset(self::$_instancedValues[$className][$value]))
{
$debugTrace = debug_backtrace();
$lastCaller = array_shift($debugTrace);
while ($lastCaller['class'] !== $className && count($debugTrace) > 0)
{
$lastCaller = array_shift($debugTrace);
}
self::$_instancedValues[$className][$value] = new static($value, $lastCaller['function']);
}
return self::$_instancedValues[$className][$value];
}
public static function fromValue($value)
{
return self::_fromGetter('getValue', $value);
}
public static function fromName($value)
{
return self::_fromGetter('getName', $value);
}
public function getValue()
{
return $this->_value;
}
public function getName()
{
return $this->_name;
}
}
枚举例子:
final class DaysOfWeek extends TypedEnum
{
public static function Sunday() { return self::_create(0); }
public static function Monday() { return self::_create(1); }
public static function Tuesday() { return self::_create(2); }
public static function Wednesday() { return self::_create(3); }
public static function Thursday() { return self::_create(4); }
public static function Friday() { return self::_create(5); }
public static function Saturday() { return self::_create(6); }
}
使用示例:
function saveEvent(DaysOfWeek $weekDay, $comment)
{
// store week day numeric value and comment:
$myDatabase->save('myeventtable',
array('weekday_id' => $weekDay->getValue()),
array('comment' => $comment));
}
// call the function, note: DaysOfWeek::Monday() returns an object of type DaysOfWeek
saveEvent(DaysOfWeek::Monday(), 'some comment');
注意,同一个枚举条目的所有实例都是相同的:
$monday1 = DaysOfWeek::Monday();
$monday2 = DaysOfWeek::Monday();
$monday1 === $monday2; // true
你也可以在switch语句中使用它:
function getGermanWeekDayName(DaysOfWeek $weekDay)
{
switch ($weekDay)
{
case DaysOfWeek::Monday(): return 'Montag';
case DaysOfWeek::Tuesday(): return 'Dienstag';
// ...
}
你也可以通过名称或值创建枚举项:
$monday = DaysOfWeek::fromValue(2);
$tuesday = DaysOfWeek::fromName('Tuesday');
或者你可以从一个现有的枚举条目中获取名称(即函数名):
$wednesday = DaysOfWeek::Wednesday()
echo $wednesDay->getName(); // Wednesday
对于简单的枚举,我使用如下结构。通常,您可以将它们用于switch语句。
<?php
define("OPTION_1", "1");
define("OPTION_2", OPTION_1 + 1);
define("OPTION_3", OPTION_2 + 1);
// Some function...
switch($Val){
case OPTION_1:{ Perform_1();}break;
case OPTION_2:{ Perform_2();}break;
...
}
?>
它不像c++中的本地枚举那样方便,但如果你以后想在两者之间添加一个选项,它似乎可以工作,并且需要更少的维护。
基于此要点,所有枚举的基类:
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";
在网上试试:沙盒
下面的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();