How to implement Enum like functionality in PHP? [duplicate]
How can Enum-like functionality (as provided in Java and other high level languages) be used in PHP? I know PHP doesn't allow you to create enums currently, but what's the closest one could get?
Using const
, perhaps.
class SomeClass {
const FIRSTVAL = 1;
const SECONDVAL = 2;
};
This is an updated version from the @Kris code, to work better with newer versions of PHP. It was made based on @lassombra comment.
/**
* Implements the abstract base for all enum types
* @see http://stackoverflow.com/a/2324746/1003020
* @see http://stackoverflow.com/a/254543/1003020
*
* Example of a typical enum:
*
* class DayOfWeek extends Enum
* {
* const Sunday = 0;
* const Monday = 1;
* const Tuesday = 2;
* const Wednesday = 3;
* const Thursday = 4;
* const Friday = 5;
* const Saturday = 6;
* }
*
* Usage examples:
*
* $monday = DayOfWeek::Monday // (int) 1
* DayOfWeek::isValidName('Monday') // (bool) true
* DayOfWeek::isValidName('monday', $strict = true) // (bool) false
* DayOfWeek::isValidValue(0) // (bool) true
* DayOfWeek::fromString('Monday') // (int) 1
* DayOfWeek::toString(DayOfWeek::Tuesday) // (string) "Tuesday"
* DayOfWeek::toString(5) // (string) "Friday"
**/
abstract class Enum
{
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);
}
public static function fromString($name)
{
if (self::isValidName($name, $strict = true)) {
$constants = self::getConstants();
return $constants[$name];
}
return false;
}
public static function toString($value)
{
if (self::isValidValue($value, $strict = true)) {
return array_search($value, self::getConstants());
}
return false;
}
}
Since I posted this answer, @Vinicius-Garcia has improved upon this solution and his version is undoubtedly better for most users.
Old answer below:
I use class constants, and a bit of reflection trickery.
<?php
/**
* @package Red.Core
* @author kris@theredhead.nl
*
* Implements the abstract base for all enum types
*
* example of a typical enum:
*
* class DayOfWeek extends Enum
* {
* const Sunday = 0;
* const Monday = 1;
* const Tuesday = 2;
* const Wednesday = 3;
* const Thursday = 4;
* const Friday = 5;
* const Saturday = 6;
* }
*
* usage examples:
*
* $monday = Enum::FromString( 'DayOfWeek::Monday' ); // (int) 1
* $monday = DayOfWeek::Monday // (int) 1
* $monday = Enum::ToString( 'DayOfWeek', DayOfWeek::Monday ); // (string) "DayOfWeek::Monday"
* $monday = Enum::Label( 'DayOfWeek', DayOfWeek::Monday ); // (string) "Monday"
*
**/
abstract class Enum
{
// make sure there are never any instances created
final private function __construct()
{
throw new Exception( 'Enum and Subclasses cannot be instantiated.' );
}
/**
* Give the integer associated with the const of the given string in the format of "class:const"
*
* @param string $string
* @return integer
*/
final public static function FromString( $string )
{
if ( strpos( $string, '::' ) < 1 )
{
throw new Exception( 'Enum::FromString( $string ) Input string is not in the expected format.' );
}
list( $class, $const ) = explode( '::', $string );
if ( class_exists( $class, false ) )
{
$reflector = new ReflectionClass( $class );
if ( $reflector->IsSubClassOf( 'Enum' ) )
{
if ( $reflector->hasConstant( $const ) )
{
return eval( sprintf( 'return %s;', $string ) );
}
}
}
throw new Excption( sprintf( '%s does not map to an Enum field', $string ) );
}
final public static function IsValidValue( $enumType, $enumValue )
{
if ( class_exists( $enumType ) )
{
$reflector = new ReflectionClass( $enumType );
if ( $reflector->IsSubClassOf( 'Enum' ) )
{
foreach( $reflector->getConstants() as $label => $value )
{
if ( $value == $enumValue )
{
return true;
}
}
}
}
return false;
}
final public static function IsValidLabel( $enumType, $enumValue )
{
if ( class_exists( $enumType ) )
{
$reflector = new ReflectionClass( $enumType );
if ( $reflector->IsSubClassOf( 'Enum' ) )
{
foreach( $reflector->getConstants() as $label => $value )
{
if ( $label == $enumValue )
{
return true;
}
}
}
}
return false;
}
/**
* For a given $enumType, give the complete string representation for the given $enumValue (class::const)
*
* @param string $enumType
* @param integer $enumValue
* @return string
*/
final public static function ToString( $enumType, $enumValue )
{
$result = 'NotAnEnum::IllegalValue';
if ( class_exists( $enumType, false ) )
{
$reflector = new ReflectionClass( $enumType );
$result = $reflector->getName() . '::IllegalValue';
foreach( $reflector->getConstants() as $key => $val )
{
if ( $val == $enumValue )
{
$result = str_replace( 'IllegalValue', $key, $result );
break;
}
}
}
return $result;
}
/**
* For a given $enumType, give the label associated with the given $enumValue (const name in class definition)
*
* @param string $enumType
* @param integer $enumValue
* @return string
*/
final public static function Label( $enumType, $enumValue )
{
$result = 'IllegalValue';
if ( class_exists( $enumType, false ) )
{
$reflector = new ReflectionClass( $enumType );
foreach( $reflector->getConstants() as $key => $val )
{
if ( $val == $enumValue )
{
$result = $key;
break;
}
}
}
return $result;
}
}
?>
You may also use this one:
class Enum{
private $m_valueName = NULL;
private function __construct($valueName){
$this->m_valueName = $valueName;
}
public static function __callStatic($methodName, $arguments){
$className = get_called_class();
return new $className($methodName);
}
function __toString(){
return $this->m_valueName;
}
}
class NotificationType extends Enum{
const Notification = NULL;
const Warning = NULL;
const Error = NULL;
}
function Test(NotificationType $type){
echo "Test function, type: $type<br>";
}
Test(NotificationType::Warning());
There is an SplEnum
class provided.
Sample usage from the docs:
<?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;
}
The above example will output
6
Value not a const in enum Month
Another possibility is to use the myclabs/php-enum package.
You could use constants
class myClass {
const aValue = 123;
const aString = "ABC";
};
But it wouldn't give a nice way of iterating through them so i would probably opt for an associate array as it would be easier to manage:
class myClass{
$enum = array ("first" => 123,
"second" => "ABC");
}
A cheap trick is to create an array with possible values. However, unlike the above answers, I'd opt for an array where key/value pairs are equal, i.e.:
<?php
$enum = Array(
'apple' => 'apple',
'pear' => 'pear',
'orange' => 'orange'
);
?>
That way, if ($enum[$value] != $value)
, you know the value given is not in the set.
Ofcourse, if you want the key/value pairs to be different, a regular array would be the way to go.
As an array.
$arr = array('A','B','C','D');
$find = 'A';
$key = array_search($find,$arr);
echo $arr[$key];
In my case I needed to store permission names which are used throughout the app. I ended up with a base enum abstract class that defined several utility functions for enums, and then extended it. Here's the base enum class:
<?php
namespace App\Enums;
use ReflectionClass;
abstract class BasicEnum {
private static $constCacheArray = NULL;
public 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);
}
}
Here are example enums created by extending the abstract class:
<?php
namespace App\Enums;
class Permissions extends BasicEnum
{
const COMMENTS_CREATE = 'create comments';
const COMMENTS_VIEW = 'view comments';
const COMMENTS_EDIT = 'edit comments';
const COMMENTS_DELETE = 'delete comments';
const COMMENTS_RESTORE = 'restore comments';
const REACTIONS_CREATE = 'create reactions';
const REACTIONS_VIEW = 'view reactions';
const REACTIONS_EDIT = 'edit reactions';
const REACTIONS_DELETE = 'delete reactions';
const REACTIONS_RESTORE = 'restore reactions';
const QUESTIONS_CREATE = 'create questions';
const QUESTIONS_VIEW = 'view questions';
const QUESTIONS_EDIT = 'edit questions';
const QUESTIONS_DELETE = 'delete questions';
const QUESTIONS_RESTORE = 'restore questions';
const PERMISSIONS_CREATE = 'create permissions';
const PERMISSIONS_VIEW = 'view permissions';
const PERMISSIONS_EDIT = 'edit permissions';
const PERMISSIONS_DELETE = 'delete permissions';
const PERMISSIONS_RESTORE = 'restore permissions';
}
<?php
namespace App\Enums;
class PostTypes extends BasicEnum
{
const POST = 'post';
const NEWS = 'news';
const SERVICE = 'service';
}
And here's an example usage of the Permissions enum:
/**
* Determine whether the user can create reactions.
*
* @param User $user
* @return mixed
*/
public function create(User $user)
{
return $user->can(Permissions::REACTIONS_CREATE);
}
Hope this helps.
精彩评论