我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
当前回答
如果您需要使用全局唯一的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";
}
}
?>
其他回答
提出的解决方案效果很好。干净光滑。
然而,如果你想要强类型的枚举,你可以使用这个:
class TestEnum extends Enum
{
public static $TEST1;
public static $TEST2;
}
TestEnum::init(); // Automatically initializes enum values
枚举类如下所示:
class Enum
{
public static function parse($enum)
{
$class = get_called_class();
$vars = get_class_vars($class);
if (array_key_exists($enum, $vars)) {
return $vars[$enum];
}
return null;
}
public static function init()
{
$className = get_called_class();
$consts = get_class_vars($className);
foreach ($consts as $constant => $value) {
if (is_null($className::$$constant)) {
$constantValue = $constant;
$constantValueName = $className . '::' . $constant . '_VALUE';
if (defined($constantValueName)) {
$constantValue = constant($constantValueName);
}
$className::$$constant = new $className($constantValue);
}
}
}
public function __construct($value)
{
$this->value = $value;
}
}
这样,枚举值是强类型的和
$TEST1 == TEST1::parse('TEST1') // true语句
这可能很简单
enum DaysOfWeek {
Sunday,
Monday,
// ...
}
在未来。
枚举类型
我也喜欢java的枚举,因此我这样写我的枚举,我认为这是最类似的行为像在java的枚举,当然,如果有人想使用更多的方法从java应该写在这里,或在抽象类,但核心思想是嵌入在下面的代码
class FruitsEnum {
static $APPLE = null;
static $ORANGE = null;
private $value = null;
public static $map;
public function __construct($value) {
$this->value = $value;
}
public static function init () {
self::$APPLE = new FruitsEnum("Apple");
self::$ORANGE = new FruitsEnum("Orange");
//static map to get object by name - example Enum::get("INIT") - returns Enum::$INIT object;
self::$map = array (
"Apple" => self::$APPLE,
"Orange" => self::$ORANGE
);
}
public static function get($element) {
if($element == null)
return null;
return self::$map[$element];
}
public function getValue() {
return $this->value;
}
public function equals(FruitsEnum $element) {
return $element->getValue() == $this->getValue();
}
public function __toString () {
return $this->value;
}
}
FruitsEnum::init();
var_dump(FruitsEnum::$APPLE->equals(FruitsEnum::$APPLE)); //true
var_dump(FruitsEnum::$APPLE->equals(FruitsEnum::$ORANGE)); //false
var_dump(FruitsEnum::$APPLE instanceof FruitsEnum); //true
var_dump(FruitsEnum::get("Apple")->equals(FruitsEnum::$APPLE)); //true - enum from string
var_dump(FruitsEnum::get("Apple")->equals(FruitsEnum::get("Orange"))); //false
我所见过的PHP中枚举最常见的解决方案是创建一个通用枚举类,然后扩展它。你可以看看这个。
更新:或者,我从phpclasses.org找到了这个。
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中内置的工具)。