Skip to content

Instantly share code, notes, and snippets.

@craigjbass
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save craigjbass/9019092 to your computer and use it in GitHub Desktop.
Save craigjbass/9019092 to your computer and use it in GitHub Desktop.
PHP Enums
<?php
/**
* Enum Abstract Class
*
* Warning: Uses Reflection and LSB. :D
* ==========================================================
*
* To use this class:
*
* Create a concrete implementation, and define class constants.
*
* ==========================================================
*
* To validate call ConcreteEnum::isValid( 'SOME_ENUM_VAL' )
*
* This will return a boolean response
*
* === OR ===================================================
*
* new ConcreteEnum( 'SOME_ENUM_VAL' )
*
* The second version will throw an exception
*
* ==========================================================
*/
namespace {
abstract class Devls_Enum {
/* @var string[][] */
private static $constants = [];
/* @var string */
private $value;
/* @var string */
protected $default = false;
/**
* @param [string $value]
* @throws \UnexpectedValueException
*/
final public function __construct( $value = null )
{
if( ! static::isValid( $value )
|| ( $value === null && $this->default === false ) ) {
$this->throwUnexpectedValueException();
} elseif ( $value === null
&& $this->default !== false ) {
$this->value = (string)$this->default;
} else {
$this->value = (string)$value;
}
}
/**
* Override this to change the exception thrown.
*
* @throws \Exception
*/
protected function throwUnexpectedValueException()
{
throw new UnexpectedValueException;
}
/**
* @return string
*/
final public function __toString()
{
return $this->value;
}
/**
* @return string
*/
final public function __invoke()
{
return $this->value;
}
/**
* @return string
*/
final public function getValue()
{
return $this->value;
}
/**
* @return string[]
*/
final public static function getEnumerations()
{
$class = get_called_class();
if( isset( self::$constants[$class] ) ) {
return self::$constants[$class];
}
$reflect = new ReflectionClass($class);
return self::$constants[$class] = $reflect->getConstants();
}
/**
* @param string[] $enumerations
* @param [string $default]
* @return \Devls_Enum_Dynamic
*/
final public static function defineDynamicEnum( array $enumerations, $default = null )
{
$enumName = \Devls\Enum\dodgy_enum_creator();
$enumerations = array_combine( $enumerations, $enumerations );
self::$constants[$enumName] = $enumerations;
return new \Devls_Enum_Dynamic( $enumName, $default === null ? false : $default );
}
/**
* @param $value
* @return bool
*/
final public static function isValid( $value )
{
return array_key_exists( $value, static::getEnumerations() );
}
}
}
namespace Devls\Enum {
/**
* @return string
*/
function dodgy_enum_creator() {
$name = 'enum'.time().md5(mt_rand(1,100000));
$php = <<<PHP
class {$name} extends \Devls_Enum {
}
PHP;
eval ( $php );
return $name;
}
}
<?php
/**
* More heavy reflection
*/
class Devls_Enum_Dynamic {
/* @var $string */
private $name;
/* @var string */
private $default;
public function __construct( $name, $default )
{
if( !class_exists( $name ) || !is_subclass_of( $name, 'Devls_Enum' ) ) {
throw new Exception( 'Invalid use of Devls_Enum_Dynamic' );
}
$this->name = $name;
$this->default = $default;
}
/**
* @param [string $value]
* @return \Devls_Enum
*/
public function getInstance( $value = null )
{
$reflect = new ReflectionClass( $this->name );
/* @var $instance \Devls_Enum */
$instance = $reflect->newInstanceWithoutConstructor();
$property = $reflect->getProperty( 'default' );
$property->setAccessible( true );
$property->setValue( $instance, $this->default );
$property->setAccessible( false );
$instance->__construct( $value );
return $instance;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment