我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
我知道PHP还没有本地枚举。但是我已经习惯了来自Java世界的它们。我喜欢使用枚举来提供ide的自动补全功能能够理解的预定义值。
常量可以解决这个问题,但是存在名称空间冲突问题,而且(实际上是因为)它们是全局的。数组没有名称空间的问题,但是它们太模糊了,它们可以在运行时被覆盖,而且ide很少知道如何在没有额外的静态分析注释或属性的情况下自动填充它们的键。
你有什么常用的解决方案/变通办法吗?有人记得PHP的人对枚举有什么想法或决定吗?
当前回答
这可能很简单
enum DaysOfWeek {
Sunday,
Monday,
// ...
}
在未来。
枚举类型
其他回答
我使用接口而不是类:
interface DaysOfWeek
{
const Sunday = 0;
const Monday = 1;
// etc.
}
var $today = DaysOfWeek::Sunday;
下面的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();
我知道这是一个非常非常非常古老的话题,但我对此有一个想法,想知道人们是怎么想的。
注:我在玩这个,并意识到如果我只是修改__call()函数,你可以更接近实际的枚举。__call()函数处理所有未知函数调用。假设你想创建三个枚举RED_LIGHT, YELLOW_LIGHT和GREEN_LIGHT。你现在只需做以下事情就可以做到:
$c->RED_LIGHT();
$c->YELLOW_LIGHT();
$c->GREEN_LIGHT();
一旦定义了,你所要做的就是再次调用它们来获取值:
echo $c->RED_LIGHT();
echo $c->YELLOW_LIGHT();
echo $c->GREEN_LIGHT();
得到0 1 2。玩得开心!这个现在也在GitHub上。
更新:我已经这样做了,所以__get()和__set()函数现在都被使用。这允许您不必调用函数,除非您愿意。相反,现在你可以说:
$c->RED_LIGHT;
$c->YELLOW_LIGHT;
$c->GREEN_LIGHT;
对于价值的创造和获取。因为变量最初还没有定义,所以调用__get()函数(因为没有指定值),它会发现数组中的条目还没有创建。因此,它创建条目,将最后一个给定值赋给它+1(+1),对最后一个值变量加1,并返回TRUE。如果你设置这个值:
$c->RED_LIGHT = 85;
然后调用__set()函数,最后一个值被设置为新值+1(+1)。现在我们有了一个很好的方法来处理枚举,并且可以动态地创建它们。
<?php
################################################################################
# Class ENUMS
#
# Original code by Mark Manning.
# Copyrighted (c) 2015 by Mark Manning.
# All rights reserved.
#
# This set of code is hereby placed into the free software universe
# via the GNU greater license thus placing it under the Copyleft
# rules and regulations with the following modifications:
#
# 1. You may use this work in any other work. Commercial or otherwise.
# 2. You may make as much money as you can with it.
# 3. You owe me nothing except to give me a small blurb somewhere in
# your program or maybe have pity on me and donate a dollar to
# sim_sales@paypal.com. :-)
#
# Blurb:
#
# PHP Class Enums by Mark Manning (markem-AT-sim1-DOT-us).
# Used with permission.
#
# Notes:
#
# VIM formatting. Set tabs to four(4) spaces.
#
################################################################################
class enums
{
private $enums;
private $clear_flag;
private $last_value;
################################################################################
# __construct(). Construction function. Optionally pass in your enums.
################################################################################
function __construct()
{
$this->enums = array();
$this->clear_flag = false;
$this->last_value = 0;
if( func_num_args() > 0 ){
return $this->put( func_get_args() );
}
return true;
}
################################################################################
# put(). Insert one or more enums.
################################################################################
function put()
{
$args = func_get_args();
#
# Did they send us an array of enums?
# Ex: $c->put( array( "a"=>0, "b"=>1,...) );
# OR $c->put( array( "a", "b", "c",... ) );
#
if( is_array($args[0]) ){
#
# Add them all in
#
foreach( $args[0] as $k=>$v ){
#
# Don't let them change it once it is set.
# Remove the IF statement if you want to be able to modify the enums.
#
if( !isset($this->enums[$k]) ){
#
# If they sent an array of enums like this: "a","b","c",... then we have to
# change that to be "A"=>#. Where "#" is the current count of the enums.
#
if( is_numeric($k) ){
$this->enums[$v] = $this->last_value++;
}
#
# Else - they sent "a"=>"A", "b"=>"B", "c"=>"C"...
#
else {
$this->last_value = $v + 1;
$this->enums[$k] = $v;
}
}
}
}
#
# Nope! Did they just sent us one enum?
#
else {
#
# Is this just a default declaration?
# Ex: $c->put( "a" );
#
if( count($args) < 2 ){
#
# Again - remove the IF statement if you want to be able to change the enums.
#
if( !isset($this->enums[$args[0]]) ){
$this->enums[$args[0]] = $this->last_value++;
}
#
# No - they sent us a regular enum
# Ex: $c->put( "a", "This is the first enum" );
#
else {
#
# Again - remove the IF statement if you want to be able to change the enums.
#
if( !isset($this->enums[$args[0]]) ){
$this->last_value = $args[1] + 1;
$this->enums[$args[0]] = $args[1];
}
}
}
}
return true;
}
################################################################################
# get(). Get one or more enums.
################################################################################
function get()
{
$num = func_num_args();
$args = func_get_args();
#
# Is this an array of enums request? (ie: $c->get(array("a","b","c"...)) )
#
if( is_array($args[0]) ){
$ary = array();
foreach( $args[0] as $k=>$v ){
$ary[$v] = $this->enums[$v];
}
return $ary;
}
#
# Is it just ONE enum they want? (ie: $c->get("a") )
#
else if( ($num > 0) && ($num < 2) ){
return $this->enums[$args[0]];
}
#
# Is it a list of enums they want? (ie: $c->get( "a", "b", "c"...) )
#
else if( $num > 1 ){
$ary = array();
foreach( $args as $k=>$v ){
$ary[$v] = $this->enums[$v];
}
return $ary;
}
#
# They either sent something funky or nothing at all.
#
return false;
}
################################################################################
# clear(). Clear out the enum array.
# Optional. Set the flag in the __construct function.
# After all, ENUMS are supposed to be constant.
################################################################################
function clear()
{
if( $clear_flag ){
unset( $this->enums );
$this->enums = array();
}
return true;
}
################################################################################
# __call(). In case someone tries to blow up the class.
################################################################################
function __call( $name, $arguments )
{
if( isset($this->enums[$name]) ){ return $this->enums[$name]; }
else if( !isset($this->enums[$name]) && (count($arguments) > 0) ){
$this->last_value = $arguments[0] + 1;
$this->enums[$name] = $arguments[0];
return true;
}
else if( !isset($this->enums[$name]) && (count($arguments) < 1) ){
$this->enums[$name] = $this->last_value++;
return true;
}
return false;
}
################################################################################
# __get(). Gets the value.
################################################################################
function __get($name)
{
if( isset($this->enums[$name]) ){ return $this->enums[$name]; }
else if( !isset($this->enums[$name]) ){
$this->enums[$name] = $this->last_value++;
return true;
}
return false;
}
################################################################################
# __set(). Sets the value.
################################################################################
function __set( $name, $value=null )
{
if( isset($this->enums[$name]) ){ return false; }
else if( !isset($this->enums[$name]) && !is_null($value) ){
$this->last_value = $value + 1;
$this->enums[$name] = $value;
return true;
}
else if( !isset($this->enums[$name]) && is_null($value) ){
$this->enums[$name] = $this->last_value++;
return true;
}
return false;
}
################################################################################
# __destruct(). Deconstruct the class. Remove the list of enums.
################################################################################
function __destruct()
{
unset( $this->enums );
$this->enums = null;
return true;
}
}
#
# Test code
#
# $c = new enums();
# $c->RED_LIGHT(85);
# $c->YELLOW_LIGHT = 23;
# $c->GREEN_LIGHT;
#
# echo $c->RED_LIGHT . "\n";
# echo $c->YELLOW_LIGHT . "\n";
# echo $c->GREEN_LIGHT . "\n";
?>
上面的答案太棒了。但是,如果以两种不同的方式进行扩展,那么无论先进行哪种扩展,都会导致对函数的调用,从而创建缓存。这个缓存将被所有后续调用使用,无论调用是由哪个扩展发起的…
要解决这个问题,将变量和第一个函数替换为:
private static $constCacheArray = null;
private static function getConstants() {
if (self::$constCacheArray === null) self::$constCacheArray = array();
$calledClass = get_called_class();
if (!array_key_exists($calledClass, self::$constCacheArray)) {
$reflect = new \ReflectionClass($calledClass);
self::$constCacheArray[$calledClass] = $reflect->getConstants();
}
return self::$constCacheArray[$calledClass];
}
我所见过的PHP中枚举最常见的解决方案是创建一个通用枚举类,然后扩展它。你可以看看这个。
更新:或者,我从phpclasses.org找到了这个。