Skip to content

Instantly share code, notes, and snippets.

@AndrewGreen
Last active August 29, 2015 13:57
Show Gist options
  • Save AndrewGreen/9866433 to your computer and use it in GitHub Desktop.
Save AndrewGreen/9866433 to your computer and use it in GitHub Desktop.
Another PHP typesafe enum idea
<?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() );
@AndrewGreen
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment