Last active
August 29, 2015 13:57
-
-
Save AndrewGreen/9866433 to your computer and use it in GitHub Desktop.
Another PHP typesafe enum idea
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* To create some typesafe enums, just extend this class, declare and initialize | |
* to null a protected static property called $values, and implement getNames() | |
* to return an array with the enums you want. Then you can access your | |
* enums with YourClassName::ENUM_NAME(). Also, your subclass should be final. | |
* | |
* Disadvantages: | |
* - Usage is ugly. | |
* - No IDE autocompletion. | |
* | |
* Advantages: | |
* - Enums can't be modified by mistake (or by malicious code, except via reflection). | |
* - No initialization required. | |
*/ | |
abstract class TypesafeEnum { | |
/** | |
* Used in __toString() | |
* | |
* @var string | |
*/ | |
private $name; | |
/** | |
* This method should return an array of available enum names. | |
*/ | |
protected abstract static function getNames(); | |
/** | |
* Called when an enum is requested (actually implemented as unavailable | |
* methods). | |
*/ | |
public static function __callStatic( $calledMethod, $args ) { | |
$calledClass = get_called_class(); | |
// Set up the values the first time an enum from this class is used | |
if ( is_null( $calledClass::$values ) ) { | |
$calledClass::$values = array(); | |
foreach ( $calledClass::getNames() as $name ) { | |
$calledClass::$values[$name] = new $calledClass( $name ); | |
} | |
} | |
// Throw an exception if this enum wasn't defined | |
if ( !isset( $calledClass::$values[$calledMethod] ) ) { | |
throw new Exception( 'Undefined enum ' . $calledMethod ); | |
} | |
return $calledClass::$values[$calledMethod]; | |
} | |
private function __construct( $name ) { | |
$this->name = $name; | |
} | |
public function __toString() { | |
return $this->name; | |
} | |
} | |
// Example use | |
final class MyHappyEnum extends TypesafeEnum { | |
protected static $values = null; | |
protected static function getNames() { | |
return array( | |
'ENUM_ONE', | |
'ENUM_TWO', | |
'ENUM_THREE' | |
); | |
} | |
} | |
// Here's the function we'll use to demonstrate | |
$func = function( MyHappyEnum $enum ) { | |
// Got the right class | |
print 'Class of parameter is ' . get_class( $enum ) . "\n"; | |
// __toString() works | |
print 'Name is ' . $enum . "\n"; | |
// Switch also works normally | |
switch ( $enum ) { | |
case MyHappyEnum::ENUM_ONE(): | |
print "Switch: one\n"; | |
break; | |
case MyHappyEnum::ENUM_TWO(): | |
print "Switch: two\n"; | |
break; | |
case MyHappyEnum::ENUM_THREE(): | |
print "Switch: three\n"; | |
break; | |
default: | |
throw new Exception( "Shouldn't happen." ); | |
} | |
}; | |
print ( "Testing call with " . MyHappyEnum::ENUM_TWO() . "\n\n" ); | |
$func( MyHappyEnum::ENUM_TWO() ); | |
print ( "\n\nTesting call with " . MyHappyEnum::ENUM_THREE() . "\n\n" ); | |
$func( MyHappyEnum::ENUM_THREE() ); | |
// The following lines cause a fatal error or an exception. Uncomment any one | |
// to try it. | |
// $func( new MyHappyEnum( 'Can\'t instantiate anywhere else.' ) ); | |
// $func( 'Type hint prevents passing anything else.' ); | |
// $func( MyHappyEnum::CAN_T_USE_NON_EXISTENT_ENUM() ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Compare to https://gist.github.com/AndrewGreen/9846282