Hen*_*aul 1114 php enumeration
我知道PHP没有本机枚举.但我已经从Java世界习惯了它们.我希望使用枚举作为一种方式来提供IDE的自动完成功能可以理解的预定义值.
常量可以解决问题,但是存在名称空间冲突问题,并且(或实际上因为)它们是全局的.数组没有命名空间问题,但是它们太模糊了,它们可以在运行时覆盖,IDE很少(从不?)知道如何自动填充其键.
您是否经常使用任何解决方案/解决方法?有谁回忆一下PHP家伙是否对枚举有任何想法或决定?
Bri*_*ine 1458
根据用例,我通常会使用如下简单的东西:
abstract class DaysOfWeek
{
const Sunday = 0;
const Monday = 1;
// etc.
}
$today = DaysOfWeek::Sunday;
Run Code Online (Sandbox Code Playgroud)
但是,其他用例可能需要更多的常量和值验证.根据以下关于反思的评论以及其他一些注释,这里有一个扩展的例子,可以更好地服务于更广泛的案例:
abstract class BasicEnum {
private static $constCacheArray = NULL;
private static function getConstants() {
if (self::$constCacheArray == NULL) {
self::$constCacheArray = [];
}
$calledClass = get_called_class();
if (!array_key_exists($calledClass, self::$constCacheArray)) {
$reflect = new ReflectionClass($calledClass);
self::$constCacheArray[$calledClass] = $reflect->getConstants();
}
return self::$constCacheArray[$calledClass];
}
public static function isValidName($name, $strict = false) {
$constants = self::getConstants();
if ($strict) {
return array_key_exists($name, $constants);
}
$keys = array_map('strtolower', array_keys($constants));
return in_array(strtolower($name), $keys);
}
public static function isValidValue($value, $strict = true) {
$values = array_values(self::getConstants());
return in_array($value, $values, $strict);
}
}
Run Code Online (Sandbox Code Playgroud)
通过创建一个扩展BasicEnum的简单枚举类,您现在可以使用方法进行简单的输入验证:
abstract class DaysOfWeek extends BasicEnum {
const Sunday = 0;
const Monday = 1;
const Tuesday = 2;
const Wednesday = 3;
const Thursday = 4;
const Friday = 5;
const Saturday = 6;
}
DaysOfWeek::isValidName('Humpday'); // false
DaysOfWeek::isValidName('Monday'); // true
DaysOfWeek::isValidName('monday'); // true
DaysOfWeek::isValidName('monday', $strict = true); // false
DaysOfWeek::isValidName(0); // false
DaysOfWeek::isValidValue(0); // true
DaysOfWeek::isValidValue(5); // true
DaysOfWeek::isValidValue(7); // false
DaysOfWeek::isValidValue('Friday'); // false
Run Code Online (Sandbox Code Playgroud)
作为旁注,任何时候我在静态/ const类上使用反射至少一次数据不会改变(例如在枚举中),我会缓存那些反射调用的结果,因为每次使用新的反射对象最终会产生明显的性能影响(存储在多个枚举的关联数组中).
现在大多数人已经最终升级到至少5.3,并且SplEnum可用,这当然是一个可行的选择 - 只要你不介意在整个代码库中实际实现枚举实例的传统不直观的概念.在上述例子中,BasicEnum并DaysOfWeek不能在所有的实例化,也不应.
mar*_*kus 181
还有一个原生扩展.该SplEnum
SplEnum提供了在PHP中本地模拟和创建枚举对象的功能.
http://www.php.net/manual/en/class.splenum.php
Pet*_*ley 45
类常量怎么样?
<?php
class YourClass
{
const SOME_CONSTANT = 1;
public function echoConstant()
{
echo self::SOME_CONSTANT;
}
}
echo YourClass::SOME_CONSTANT;
$c = new YourClass;
$c->echoConstant();
Run Code Online (Sandbox Code Playgroud)
Nei*_*end 34
上面的最佳答案是太棒了.但是,如果您extend以两种不同的方式使用它,那么无论哪个扩展都先完成,导致对函数的调用将创建缓存.然后,所有后续呼叫都将使用此缓存,无论呼叫由哪个分机发起......
要解决此问题,请将变量和第一个函数替换为:
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];
}
Run Code Online (Sandbox Code Playgroud)
And*_*i T 28
我使用interface而不是class:
interface DaysOfWeek
{
const Sunday = 0;
const Monday = 1;
// etc.
}
var $today = DaysOfWeek::Sunday;
Run Code Online (Sandbox Code Playgroud)
yiv*_*ivi 27
基本语法如下所示:
enum TransportMode {
case Bicycle;
case Car;
case Ship;
case Plane;
case Feet;
}
Run Code Online (Sandbox Code Playgroud)
function travelCost(Vehicle $vehicle, int $distance): int
{ /* implementation */ }
$mode = TransportMode::Boat;
$bikeCost = travelCost(TransportMode::Bicycle, 90);
$boatCost = travelCost($mode, 90);
// this one would fail: (Enums are singletons, not scalars)
$failCost = travelCost('Car', 90);
Run Code Online (Sandbox Code Playgroud)
默认情况下,枚举不受任何类型的标量支持。所以TransportMode::Bicycle不是0,你不能比较使用>或<枚举之间。
但以下工作:
$foo = TransportMode::Car;
$bar = TransportMode::Car;
$baz = TransportMode::Bicycle;
$foo === $bar; // true
$bar === $baz; // false
$foo instanceof TransportMode; // true
$foo > $bar || $foo < $bar; // false either way
Run Code Online (Sandbox Code Playgroud)
您还可以拥有“支持”的枚举,其中每个枚举案例都由 anint或 a “支持” string。
enum Metal: int {
case Gold = 1932;
case Silver = 1049;
case Lead = 1134;
case Uranium = 1905;
case Copper = 894;
}
Run Code Online (Sandbox Code Playgroud)
value属性:Metal::Gold->value。最后,支持的枚举在BackedEnum内部实现了一个接口,它公开了两个方法:
from(int|string): selftryFrom(int|string): ?self它们几乎是等价的,但是如果找不到值,第一个将抛出异常,第二个将简单地 return null。
// usage example:
$metal_1 = Metal::tryFrom(1932); // $metal_1 === Metal::Gold;
$metal_2 = Metal::tryFrom(1000); // $metal_2 === null;
$metal_3 = Metal::from(9999); // throws Exception
Run Code Online (Sandbox Code Playgroud)
枚举可能有方法,从而实现接口。
// usage example:
$metal_1 = Metal::tryFrom(1932); // $metal_1 === Metal::Gold;
$metal_2 = Metal::tryFrom(1000); // $metal_2 === null;
$metal_3 = Metal::from(9999); // throws Exception
Run Code Online (Sandbox Code Playgroud)
Pure Enums 和 Backed Enums 都在内部实现了接口UnitEnum,其中包括(静态)方法UnitEnum::cases(),并允许检索枚举中定义的案例数组:
$modes = TransportMode::cases();
Run Code Online (Sandbox Code Playgroud)
现在$modes是:
[
TransportMode::Bicycle,
TransportMode::Car,
TransportMode::Ship,
TransportMode::Plane
TransportMode::Feet
]
Run Code Online (Sandbox Code Playgroud)
枚举可以实现自己的static方法,这些方法通常用于专门的构造函数。
这涵盖了基础知识。要获得全部信息,请访问相关的 RFC,直到该功能在 PHP 文档中发布并发布。
小智 26
我使用了常量类:
class Enum {
const NAME = 'aaaa';
const SOME_VALUE = 'bbbb';
}
print Enum::NAME;
Run Code Online (Sandbox Code Playgroud)
Dan*_*ugg 25
我在这里评论了其他一些答案,所以我想我也会权衡.在一天结束时,由于PHP不支持键入的枚举,您可以采用以下两种方法之一:破解类型枚举,或者生活在它们非常难以有效破解的事实中.
我更喜欢接受这个事实,而是使用const此处其他答案以某种方式使用的方法:
abstract class Enum
{
const NONE = null;
final private function __construct()
{
throw new NotSupportedException(); //
}
final private function __clone()
{
throw new NotSupportedException();
}
final public static function toArray()
{
return (new ReflectionClass(static::class))->getConstants();
}
final public static function isValid($value)
{
return in_array($value, static::toArray());
}
}
Run Code Online (Sandbox Code Playgroud)
枚举示例:
final class ResponseStatusCode extends Enum
{
const OK = 200;
const CREATED = 201;
const ACCEPTED = 202;
// ...
const SERVICE_UNAVAILABLE = 503;
const GATEWAY_TIME_OUT = 504;
const HTTP_VERSION_NOT_SUPPORTED = 505;
}
Run Code Online (Sandbox Code Playgroud)
使用Enum作为基类,所有其他枚举延伸允许辅助方法,诸如toArray,isValid等.对我来说,键入的枚举(以及管理它们的实例)最终会变得太乱.
如果,存在一种__getStatic神奇的方法(并且最好也是一种__equals神奇的方法),可以用一种多重模式来缓解这种情况.
(以下是假设的,它不会工作,虽然也许有一天它会)
final class TestEnum
{
private static $_values = [
'FOO' => 1,
'BAR' => 2,
'QUX' => 3,
];
private static $_instances = [];
public static function __getStatic($name)
{
if (isset(static::$_values[$name]))
{
if (empty(static::$_instances[$name]))
{
static::$_instances[$name] = new static($name);
}
return static::$_instances[$name];
}
throw new Exception(sprintf('Invalid enumeration value, "%s"', $name));
}
private $_value;
public function __construct($name)
{
$this->_value = static::$_values[$name];
}
public function __equals($object)
{
if ($object instanceof static)
{
return $object->_value === $this->_value;
}
return $object === $this->_value;
}
}
$foo = TestEnum::$FOO; // object(TestEnum)#1 (1) {
// ["_value":"TestEnum":private]=>
// int(1)
// }
$zap = TestEnum::$ZAP; // Uncaught exception 'Exception' with message
// 'Invalid enumeration member, "ZAP"'
$qux = TestEnum::$QUX;
TestEnum::$QUX == $qux; // true
'hello world!' == $qux; // false
Run Code Online (Sandbox Code Playgroud)
小智 23
好吧,对于像php这样的简单java,我使用:
class SomeTypeName {
private static $enum = array(1 => "Read", 2 => "Write");
public function toOrdinal($name) {
return array_search($name, self::$enum);
}
public function toString($ordinal) {
return self::$enum[$ordinal];
}
}
Run Code Online (Sandbox Code Playgroud)
并称之为:
SomeTypeName::toOrdinal("Read");
SomeTypeName::toString(1);
Run Code Online (Sandbox Code Playgroud)
但我是一个PHP初学者,在语法方面苦苦挣扎,所以这可能不是最好的方法.我用类常量实验了一些,使用Reflection从它的值中获取常量名称,可能更整洁.
Buc*_*ing 17
四年后,我又遇到了这个.我目前的方法是这样,因为它允许在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;
}
}
Run Code Online (Sandbox Code Playgroud)
示例枚举:
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); }
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
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');
Run Code Online (Sandbox Code Playgroud)
请注意,相同枚举条目的所有实例都是相同的:
$monday1 = DaysOfWeek::Monday();
$monday2 = DaysOfWeek::Monday();
$monday1 === $monday2; // true
Run Code Online (Sandbox Code Playgroud)
您也可以在switch语句中使用它:
function getGermanWeekDayName(DaysOfWeek $weekDay)
{
switch ($weekDay)
{
case DaysOfWeek::Monday(): return 'Montag';
case DaysOfWeek::Tuesday(): return 'Dienstag';
// ...
}
Run Code Online (Sandbox Code Playgroud)
您还可以按名称或值创建枚举条目:
$monday = DaysOfWeek::fromValue(2);
$tuesday = DaysOfWeek::fromName('Tuesday');
Run Code Online (Sandbox Code Playgroud)
或者您可以从现有的枚举条目中获取名称(即函数名称):
$wednesday = DaysOfWeek::Wednesday()
echo $wednesDay->getName(); // Wednesday
Run Code Online (Sandbox Code Playgroud)
如果您需要使用全局唯一的枚举(即使在比较不同枚举之间的元素时)并且易于使用,请随意使用以下代码.我还添加了一些我觉得有用的方法.您可以在代码最顶部的注释中找到示例.
<?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";
}
}
?>
Run Code Online (Sandbox Code Playgroud)
小智 7
我也喜欢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
Run Code Online (Sandbox Code Playgroud)
我在github上找到了这个库,我认为它提供了一个非常好的替代方案.
function setAction(Action $action) {format,parse,...)final可以防止它)<?php
use MyCLabs\Enum\Enum;
/**
* Action enum
*/
class Action extends Enum
{
const VIEW = 'view';
const EDIT = 'edit';
}
Run Code Online (Sandbox Code Playgroud)
<?php
$action = new Action(Action::VIEW);
// or
$action = Action::VIEW();
Run Code Online (Sandbox Code Playgroud)
<?php
function setAction(Action $action) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
最后,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.
}
Run Code Online (Sandbox Code Playgroud)
如果您使用命名空间,代码完成应该可以工作。
protected然而,这样做时,您将失去隐藏类族 ( ) 或单独的类 ( )中的常量的能力private。根据定义, an 中的所有内容Interface都是public。
更新:
PHP 8.1 现在有枚举。
abstract class Enumeration
{
public static function enum()
{
$reflect = new ReflectionClass( get_called_class() );
return $reflect->getConstants();
}
}
class Test extends Enumeration
{
const A = 'a';
const B = 'b';
}
foreach (Test::enum() as $key => $value) {
echo "$key -> $value<br>";
}
Run Code Online (Sandbox Code Playgroud)
我下面的枚举类定义是强类型的,并且非常自然使用和定义
定义:
class Fruit extends Enum {
static public $APPLE = 1;
static public $ORANGE = 2;
}
Fruit::initialize(); //Can also be called in autoloader
Run Code Online (Sandbox Code Playgroud)
切换枚举
$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
Run Code Online (Sandbox Code Playgroud)
将 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
Run Code Online (Sandbox Code Playgroud)
将枚举作为字符串回显
echo "I have an $myFruit\n";
>> I have an APPLE
Run Code Online (Sandbox Code Playgroud)
通过整数获取 Enum
$myFruit = Fruit::getByValue(2);
echo "Now I have an $myFruit\n";
>> Now I have an ORANGE
Run Code Online (Sandbox Code Playgroud)
按名称获取枚举
$myFruit = Fruit::getByName("APPLE");
echo "But I definitely prefer an $myFruit\n\n";
>> But I definitely prefer an APPLE
Run Code Online (Sandbox Code Playgroud)
枚举类:
/**
* @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;
}
}
Run Code Online (Sandbox Code Playgroud)
添加
当然,您也可以为 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();
Run Code Online (Sandbox Code Playgroud)
小智 5
这是一个用于处理php中类型安全枚举的github库:
这个库处理类生成,类缓存,它实现了Type Safe Enumeration设计模式,有几个辅助方法来处理枚举,比如为枚举组合检索枚举排序或检索二进制值的序数.
生成的代码使用一个普通的旧php模板文件,该文件也是可配置的,因此您可以提供自己的模板.
它是用phpunit覆盖的完整测试.
<?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";
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
我开始使用下面的方法,因为它使我能够实现函数参数的类型安全、NetBeans 中的自动完成以及良好的性能。我不太喜欢的一件事是你必须[extended class name]::enumerate();在定义类之后调用。
abstract class Enum {
private $_value;
protected function __construct($value) {
$this->_value = $value;
}
public function __toString() {
return (string) $this->_value;
}
public static function enumerate() {
$class = get_called_class();
$ref = new ReflectionClass($class);
$statics = $ref->getStaticProperties();
foreach ($statics as $name => $value) {
$ref->setStaticPropertyValue($name, new $class($value));
}
}
}
class DaysOfWeek extends Enum {
public static $MONDAY = 0;
public static $SUNDAY = 1;
// etc.
}
DaysOfWeek::enumerate();
function isMonday(DaysOfWeek $d) {
if ($d == DaysOfWeek::$MONDAY) {
return true;
} else {
return false;
}
}
$day = DaysOfWeek::$MONDAY;
echo (isMonday($day) ? "bummer it's monday" : "Yay! it's not monday");
Run Code Online (Sandbox Code Playgroud)
我意识到这是一个非常非常非常古老的话题,但我对此有过思考,并且想知道人们的想法。
注意:我正在研究这个,并意识到如果我只是修改该__call()函数,您可以更接近实际的enums. 该__call()函数处理所有未知的函数调用。假设您想要制作三个enums红光、黄光和绿光。您现在只需执行以下操作即可做到这一点:
$c->RED_LIGHT();
$c->YELLOW_LIGHT();
$c->GREEN_LIGHT();
Run Code Online (Sandbox Code Playgroud)
定义后,您所要做的就是再次调用它们以获取值:
echo $c->RED_LIGHT();
echo $c->YELLOW_LIGHT();
echo $c->GREEN_LIGHT();
Run Code Online (Sandbox Code Playgroud)
你应该得到 0、1 和 2。玩得开心!该内容现在也已发布在 GitHub 上。
更新:我已经做到了,因此现在可以使用__get()和函数。__set()这些允许您不必调用函数,除非您愿意。相反,现在你可以说:
$c->RED_LIGHT;
$c->YELLOW_LIGHT;
$c->GREEN_LIGHT;
Run Code Online (Sandbox Code Playgroud)
对于价值的创造和获取。由于变量最初尚未定义,因此__get()调用该函数(因为未指定值),该函数会发现数组中的条目尚未创建。因此,它创建条目,为其分配给定的最后一个值加一 (+1),递增最后一个值变量,然后返回 TRUE。如果您设置该值:
$c->RED_LIGHT = 85;
Run Code Online (Sandbox Code Playgroud)
然后__set()调用该函数,并将最后一个值设置为新值加一 (+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";
?>
Run Code Online (Sandbox Code Playgroud)