The SplEnum class
(PECL spl_types >= 0.1.0)
Введение
SplEnum gives the ability to emulate and create enumeration objects natively in PHP.
Обзор классов
Предопределенные константы
SplEnum::__default
Примеры
Пример #1 SplEnum usage example
<?php
class Month extends SplEnum {
const __default = self::January;
const January = 1;
const February = 2;
const March = 3;
const April = 4;
const May = 5;
const June = 6;
const July = 7;
const August = 8;
const September = 9;
const October = 10;
const November = 11;
const December = 12;
}
echo new Month(Month::June) . PHP_EOL;
try {
new Month(13);
} catch (UnexpectedValueException $uve) {
echo $uve->getMessage() . PHP_EOL;
}
?>
Результат выполнения данного примера:
6 Value not a const in enum Month
Содержание
- SplEnum::getConstList — Returns all consts (possible values) as an array.
Коментарии
Here's a clearer example usage in case anyone else finds the
current documentation confusing (as I did).
<?php
class Fruit extends SplEnum
{
// If no value is given during object construction this value is used
const __default = 1;
// Our enum values
const APPLE = 1;
const ORANGE = 2;
}
$myApple = new Fruit();
$myOrange = new Fruit(Fruit::ORANGE);
$fail = 1;
function eat(Fruit $aFruit)
{
if (Fruit::APPLE == $aFruit) {
echo "Eating an apple.\n";
} elseif (Fruit::ORANGE == $aFruit) {
echo "Eating an orange.\n";
}
}
eat($myApple); // Eating an apple.
eat($myOrange); // Eating an orange.
eat($fail); // PHP Catchable fatal error: Argument 1 passed to eat() must be an instance of Fruit, integer given
?>
SplEnum is a nice start for enumerated type support as an extension (making enum a part of the language would be much better), but it lacks a lot of features that enums have in other languages. I needed functionality like the hasKey() method supported by Java. I extended the class as SplEnumPlus and used that subclass in my code as follows:
<?php
class SplEnumPlus extends SplEnum {
static function hasKey($key) {
$foundKey = false;
try {
$enumClassName = get_called_class();
new $enumClassName($key);
$foundKey = true;
} finally {
return $foundKey;
}
}
}
class Fruit extends SplEnumPlus {
const APPLE = 1;
const ORANGE = 2;
}
echo (Fruit::hasKey(Fruit::APPLE) ? 'yes' : 'no') . PHP_EOL; // yes
echo (Fruit::hasKey('banana') ? 'yes' : 'no') . PHP_EOL; // no
?>
Other useful features, like reverse value-to-key lookups, could be done in this way. It would be helpful if this and other useful functionality were made part of SplEnum.
Source: https://stackoverflow.com/a/254543/508666
if class SplEnum is not present, I would normally use something simple like the following:
abstract class DaysOfWeek
{
const Sunday = 0;
const Monday = 1;
// etc.
}
$today = DaysOfWeek::Sunday;
However, other use cases may require more validation of constants and values.
abstract class BasicEnum {
private static $constCacheArray = NULL;
private function __construct(){
/*
Preventing instance :)
*/
}
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) {
$values = array_values(self::getConstants());
return in_array($value, $values, $strict = true);
}
}
By creating a simple enum class that extends BasicEnum, you now have the ability to use methods thusly for simple input validation:
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
As a side note, any time I use reflection at least once on a static/const class where the data won't change (such as in an enum), I cache the results of those reflection calls, since using fresh reflection objects each time will eventually have a noticeable performance impact (Stored in an assocciative array for multiple enums).
I use the following function getValue() to have a dynamically way to check and retrieve value without errors.
class MyEnum extends SplEnum
{
const __default = 0;
const C1 = 1;
const C2 = 2;
public function getValue($key)
{
$declaredElems = $this->getConstList();
if(array_key_exists($key, $declaredElems)){
$r = new \ReflectionClass($this);
return $r->getConstant($key);
}else{
return self::__default;
}
}
}
Stylistically, SplEnum kind of sucks in use. Take for example, the following:
<?php
class Animal extends SplType {
const MONKEY = 1;
const HORSE = 2;
}
function putOnBoat( Animal $animal ) {
// ...
}
putOnBoat( new Animal( Animal::MONKEY) );
?>
A little redundant, right? Here's a little extra stylistic step you can take to make usage a little cleaner.
<?php
class Animal extends SplType {
const TYPE_MONKEY = 1;
const TYPE_HORSE = 2;
static $MONKEY;
static $HORSE;
}
Animal::$MONKEY = new Animal( Animal::TYPE_MONKEY );
Animal::$HORSE = new Animal( Animal::TYPE_HORSE);
function putOnBoat( Animal $animal ) {
// ...
}
putOnBoat( Animal::$MONKEY );
?>
I wrote a simple solution using ... PHP! (classes and type hinting).
This package can help you, https://github.com/divengine/enum. It is a simple class, but the concept is more important!. Also you can build taxonomies of enums.
$ composer require divengine\enum;
<?php
use divengineenum;
class Temperature extends enum {}
class ExtremeTemperature extends Temperature {}
class FIRE extends ExtremeTemperature {}
class ICE extends ExtremeTemperature {}
class NormalTemperature extends Temperature {}
class HOT extends NormalTemperature {}
class COOL extends NormalTemperature {}
class COLD extends NormalTemperature {}
function WhatShouldIdo(Temperature $temperature) {
...
if ($temperature instanceof HOT) { ... }
if ($temperature == HOT::class) { ... }
...
}
WhatShouldIdo(new HOT());
There is a quite good and mature solution here - https://github.com/myclabs/php-enum
This PECL is mostly abandoned, but in case you want to use SplEnum anyway, there's a PHP 7 rewrite available for installation at https://github.com/igorsantos07/SPL_Types