我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
当前回答
对于简单的枚举,我使用如下结构。通常,您可以将它们用于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++中的本地枚举那样方便,但如果你以后想在两者之间添加一个选项,它似乎可以工作,并且需要更少的维护。
其他回答
我在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) {
// ...
}
我知道这是一个旧线程,然而没有一个我所见过的解决方法真的看起来像枚举,因为几乎所有的解决方法都需要你手动分配值给枚举项,或者它需要你传递一个枚举键数组到一个函数。所以我创造了自己的解决方案。
要使用我的解决方案创建枚举类,可以简单地扩展下面的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; } } } } }
如果你想要类型安全和一堆与该类型匹配的常量,一种方法是为你的枚举创建一个抽象类,然后用一个锁定的构造函数扩展这个类,如下所示:
abstract class DaysOfWeekEnum{
public function __construct(string $value){
$this->value = $value;
}
public function __toString(){
return $this->value;
}
}
class Monday extends DaysOfWeekEnum{
public function __construct(){
parent::__construct("Monday");
}
}
class Tuesday extends DaysOfWeekEnum{
public function __construct(){
parent::__construct("Tuesday");
}
}
然后你可以让你的方法获取DaysOfWeek的一个实例,并传递给它一个Monday、Tuesday等实例……唯一的缺点是每次你想使用你的枚举时都必须“更新”一个实例,但我发现这是值得的。
function printWeekDay(DaysOfWeek $day){
echo "Today is $day.";
}
printWeekDay(new Monday());
公认的答案是要走的路,实际上这是我所做的简单。枚举提供了大多数优点(可读、快速等)。然而,这里缺少一个概念:类型安全。在大多数语言中,枚举也用于限制允许的值。下面是一个通过使用私有构造函数、静态实例化方法和类型检查来获得类型安全的例子:
class DaysOfWeek{
const Sunday = 0;
const Monday = 1;
// etc.
private $intVal;
private function __construct($intVal){
$this->intVal = $intVal;
}
//static instantiation methods
public static function MONDAY(){
return new self(self::Monday);
}
//etc.
}
//function using type checking
function printDayOfWeek(DaysOfWeek $d){ //compiler can now use type checking
// to something with $d...
}
//calling the function is safe!
printDayOfWeek(DaysOfWeek::MONDAY());
我们甚至可以更进一步:在DaysOfWeek类中使用常量可能会导致误用:例如,人们可能会这样错误地使用它:
printDayOfWeek(DaysOfWeek::Monday); //triggers a compiler error.
这是错误的(调用整数常量)。我们可以使用私有静态变量而不是常量来防止这种情况:
class DaysOfWeeks{
private static $monday = 1;
//etc.
private $intVal;
//private constructor
private function __construct($intVal){
$this->intVal = $intVal;
}
//public instantiation methods
public static function MONDAY(){
return new self(self::$monday);
}
//etc.
//convert an instance to its integer value
public function intVal(){
return $this->intVal;
}
}
当然,不可能访问整数常量(这实际上是目的)。intVal方法允许将DaysOfWeek对象转换为其整数表示形式。
请注意,我们甚至可以进一步在实例化方法中实现缓存机制,以在广泛使用枚举的情况下节省内存…
希望这能有所帮助
如果您需要使用全局唯一的enum(即即使在不同enum之间比较元素时)并且易于使用,请随意使用以下代码。我还添加了一些我认为有用的方法。您可以在代码顶部的注释中找到示例。
<?php
/**
* Class Enum
*
* @author Christopher Fox <christopher.fox@gmx.de>
*
* @version 1.0
*
* This class provides the function of an enumeration.
* The values of Enum elements are unique (even between different Enums)
* as you would expect them to be.
*
* Constructing a new Enum:
* ========================
*
* In the following example we construct an enum called "UserState"
* with the elements "inactive", "active", "banned" and "deleted".
*
* <code>
* Enum::Create('UserState', 'inactive', 'active', 'banned', 'deleted');
* </code>
*
* Using Enums:
* ============
*
* The following example demonstrates how to compare two Enum elements
*
* <code>
* var_dump(UserState::inactive == UserState::banned); // result: false
* var_dump(UserState::active == UserState::active); // result: true
* </code>
*
* Special Enum methods:
* =====================
*
* Get the number of elements in an Enum:
*
* <code>
* echo UserState::CountEntries(); // result: 4
* </code>
*
* Get a list with all elements of the Enum:
*
* <code>
* $allUserStates = UserState::GetEntries();
* </code>
*
* Get a name of an element:
*
* <code>
* echo UserState::GetName(UserState::deleted); // result: deleted
* </code>
*
* Get an integer ID for an element (e.g. to store as a value in a database table):
* This is simply the index of the element (beginning with 1).
* Note that this ID is only unique for this Enum but now between different Enums.
*
* <code>
* echo UserState::GetDatabaseID(UserState::active); // result: 2
* </code>
*/
class Enum
{
/**
* @var Enum $instance The only instance of Enum (Singleton)
*/
private static $instance;
/**
* @var array $enums An array of all enums with Enum names as keys
* and arrays of element names as values
*/
private $enums;
/**
* Constructs (the only) Enum instance
*/
private function __construct()
{
$this->enums = array();
}
/**
* Constructs a new enum
*
* @param string $name The class name for the enum
* @param mixed $_ A list of strings to use as names for enum entries
*/
public static function Create($name, $_)
{
// Create (the only) Enum instance if this hasn't happened yet
if (self::$instance===null)
{
self::$instance = new Enum();
}
// Fetch the arguments of the function
$args = func_get_args();
// Exclude the "name" argument from the array of function arguments,
// so only the enum element names remain in the array
array_shift($args);
self::$instance->add($name, $args);
}
/**
* Creates an enumeration if this hasn't happened yet
*
* @param string $name The class name for the enum
* @param array $fields The names of the enum elements
*/
private function add($name, $fields)
{
if (!array_key_exists($name, $this->enums))
{
$this->enums[$name] = array();
// Generate the code of the class for this enumeration
$classDeclaration = "class " . $name . " {\n"
. "private static \$name = '" . $name . "';\n"
. $this->getClassConstants($name, $fields)
. $this->getFunctionGetEntries($name)
. $this->getFunctionCountEntries($name)
. $this->getFunctionGetDatabaseID()
. $this->getFunctionGetName()
. "}";
// Create the class for this enumeration
eval($classDeclaration);
}
}
/**
* Returns the code of the class constants
* for an enumeration. These are the representations
* of the elements.
*
* @param string $name The class name for the enum
* @param array $fields The names of the enum elements
*
* @return string The code of the class constants
*/
private function getClassConstants($name, $fields)
{
$constants = '';
foreach ($fields as $field)
{
// Create a unique ID for the Enum element
// This ID is unique because class and variables
// names can't contain a semicolon. Therefore we
// can use the semicolon as a separator here.
$uniqueID = $name . ";" . $field;
$constants .= "const " . $field . " = '". $uniqueID . "';\n";
// Store the unique ID
array_push($this->enums[$name], $uniqueID);
}
return $constants;
}
/**
* Returns the code of the function "GetEntries()"
* for an enumeration
*
* @param string $name The class name for the enum
*
* @return string The code of the function "GetEntries()"
*/
private function getFunctionGetEntries($name)
{
$entryList = '';
// Put the unique element IDs in single quotes and
// separate them with commas
foreach ($this->enums[$name] as $key => $entry)
{
if ($key > 0) $entryList .= ',';
$entryList .= "'" . $entry . "'";
}
return "public static function GetEntries() { \n"
. " return array(" . $entryList . ");\n"
. "}\n";
}
/**
* Returns the code of the function "CountEntries()"
* for an enumeration
*
* @param string $name The class name for the enum
*
* @return string The code of the function "CountEntries()"
*/
private function getFunctionCountEntries($name)
{
// This function will simply return a constant number (e.g. return 5;)
return "public static function CountEntries() { \n"
. " return " . count($this->enums[$name]) . ";\n"
. "}\n";
}
/**
* Returns the code of the function "GetDatabaseID()"
* for an enumeration
*
* @return string The code of the function "GetDatabaseID()"
*/
private function getFunctionGetDatabaseID()
{
// Check for the index of this element inside of the array
// of elements and add +1
return "public static function GetDatabaseID(\$entry) { \n"
. "\$key = array_search(\$entry, self::GetEntries());\n"
. " return \$key + 1;\n"
. "}\n";
}
/**
* Returns the code of the function "GetName()"
* for an enumeration
*
* @return string The code of the function "GetName()"
*/
private function getFunctionGetName()
{
// Remove the class name from the unique ID
// and return this value (which is the element name)
return "public static function GetName(\$entry) { \n"
. "return substr(\$entry, strlen(self::\$name) + 1 , strlen(\$entry));\n"
. "}\n";
}
}
?>