How to implement Enum like functionality in PHP?
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 [email protected]
*
* 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;
}
}
?>
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;
}
}
Using const
, perhaps.
class SomeClass {
const FIRSTVAL = 1;
const SECONDVAL = 2;
};
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());