| 
<?php
namespace Co3\CodeGen;
 use \InvalidArgumentException as InvalidArgumentException;
 use \BadMethodCallException as BadMethodCallException;
 /**
 * @package Co3
 * @subpackage CodeGen
 * @author Oliver Anan <[email protected]>
 */
 /**
 * Base class of enumerations.
 * Service provider for generating Enumerations.
 *
 * <p>This class can generate other enum classes at runtime and provides methods to work with enums.
 * All enum classes are derived from this class.</p>
 * <p>Creation and managment are not seperated to allow the usage
 * of a protected constructor.</p>
 * <p>Creating a new enumeration involves craeting the source code and one instance
 * for each member.</p>
 * <p><b>Manually deriving from this class will not work</b>.</p>
 * <p>Supports type hinting
 * <code>
 * //create the enumearion 'Direction'
 *  Enum::create('Direction',array('Up','Down','Left','Right'));
 *
 * function move(Direction $dir,$distance){
 *   //code
 * }
 * </code></p>
 * <p>implements __toString()</p>
 * @author Oliver Anan <[email protected]>
 * @version 1.0
 * @since 0.1
 * @copyright 2011 oliver anan
 * @license LGPL 3 http://www.gnu.org/licenses/lgpl.html
 */
 class Enum{
 //------ STATIC ------//
 private static $_members = array();
 private static $_arrays = array();
 
 /**
 * Create a new enumeration class.
 *
 * <p>if the class name contains a \ character the class will be generated in a namespace.
 * Everything before the last \ is considered as namespace</p>
 * <p>All values of the $members array must be valid php identifiers.
 * Values o the $members array must not be PHP keywords.
 * The keys of the $members array are preserved unless $baseTwoKeysm is true</p>
 * <code>
 *  //create the enumearion 'Direction' in the namespace 'My\Namespace'
 *  Enum::create('My\Namespae\Direction',array('Up','Down','Left','Right'));
 * </code>
 * @param string $name the name of the class including the namespace
 * @param array $members an array  containng all memebers of the enumeration.
 * The keys are preserved unless baseTwoKeys is true.
 * @param boolean $baseTwoKeys If this is true all keys will be replaced with powers of 2.
 * @return void;
 * @static
 * @throws \InvalidArgumentException
 * @throws Co3\Exception\FormatException (Only if class is available)
 * @access public
 */
 public static function create($name, $members,$baseTwoKeys=false){
 $code = self::generateSourceCode($name,$members);
 eval($code);
 //create instances
 self::$_arrays[$name]  = array();
 self::$_members[$name] = array();
 if($baseTwoKeys){
 $i = 0;
 }
 foreach($members as $key => $member){
 if($baseTwoKeys){
 $key = pow(2,$i++);
 }
 $instance = new $name($key,$member);
 self::$_members[$name][$member] = $instance;
 self::$_arrays[$name][$key] = $instance;
 }
 }
 /**
 * get an array with all instances of this enumeration.
 * @return array
 * @access public
 * @static
 * @throws \BadMethodCallException
 */
 public static function toArray(){
 $calledClass = get_called_class();
 if($calledClass==__CLASS__){
 throw new BadMethodCallException(__CLASS__ . '::' . __METHOD__ . ' may only be called from deriver classes.');
 }
 return self::$_arrays[get_called_class()];
 }
 
 /**
 * create the php source code for an enum class
 *
 * @param $name the name o the new class including the namespace.
 * @param $members
 * @return string
 * @access private
 * @static
 * @throws \InvalidArgumentException
 * @throws Co3\Exception\FormatException (Only if class is available)
 */
 private static function generateSourceCode($name,$members){
 $code      = "";
 //remove leading '\'
 $name = ltrim($name,'\\');
 $parts     = explode('\\',$name);
 //validate class name (including namespace)
 foreach($parts as $part){
 if(!self::isValidIdentifier($part)){
 $message = "Invalid class/namespace name '{$className}'";
 if(class_exists('Co3\Exception\FormatException',true)){
 throw new Co3\Exception\FormatException($message);
 } else {
 throw new InvalidArgumentException($message);
 }
 }
 }
 $className      = array_pop($parts);;
 $namespace = implode('\\',$parts);
 //create source code
 if($namespace){
 $code .= "namespace {$namespace};\n";
 }
 $code .= "final class {$className} extends \\" . __CLASS__ . "{\n";
 foreach($members as $key => $member){
 if(!self::isValidIdentifier($member)){
 $message = "Invalid member name '{$member}' at index {$key}.";
 if(class_exists('Co3\Exception\FormatException')){
 throw new Co3\Exception\FormatException($message);
 } else {
 throw new InvalidArgumentException($message);
 }
 }
 $code .= "const {$member} = '{$member}';\n";
 }
 $code .= "}";
 return $code;
 }
 
 /**
 * Get an instance of the called enum class.
 *
 * <p>Return an instance of the called class if there is an instance
 * with an name that equals $name.</p>
 * <p>Enumeations define class constants for every member.
 * You can use the name or the constant to get an instance</p>
 * <p>Subsequent calls to get with the same $name will return the same instance</p>
 * <code>
 * Enum::create('Direction',array('Up','Down','Left','Right'));
 * $up = Direction::get(Direction::Up);
 * //is the same as
 * $upToo = Direction::get('Up');
 * //There is just one instance for every member.
 * $up === $upToo //true
 * </code>
 * <p>If there is no matching instance a InvalidArgumentException is thrown.</p>
 * @param string $name the name of the instance.
 * @return unknown_type an instance of the called class
 * @access public
 * @static
 * @throws \BadMethodCallException
 */
 public static function get($name){
 $calledClass = get_called_class();
 if($calledClass==__CLASS__){
 throw new BadMethodCallException(__CLASS__ . '::' . __METHOD__ . ' may only br called from deriver classes.');
 }
 if ($calledClass::isMember($name)){
 return Enum::$_members[$calledClass][$name];
 } else {
 throw new InvalidArgumentException("{$name} is no valid member of {$calledClass}");
 }
 }
 
 /**
 * Get an instance of the called enum class.
 *
 * Return an instance of the called class if there is an instance
 * with an key that equals $key.
 * If there is no matching instance a InvalidArgumentException is thrown.
 * @param $key the key of the instance.
 * @access public
 * @static
 * @return unknown_type an instance o the called class
 * @throws \BadMethodCallException
 */
 public static function getByKey($key){
 $calledClass = get_called_class();
 if($calledClass==__CLASS__){
 throw new BadMethodCallException(__CLASS__ . '::' . __METHOD__ . ' may only br called from deriver classes.');
 }
 if($calledClass::isKey($key)){
 return self::$_arrays[$calledClass][$key];
 } else {
 throw new InvalidArgumentException("{$name} is no valid key of {$calledClass}");
 }
 }
 
 /**
 * Returns an boolean indicating if there is a member of this enum type with this name.
 * @param $name
 * @return boolean
 * @access public
 * @static
 * @throws \BadMethodCallException
 */
 public static function isMember($name){
 $calledClass = get_called_class();
 if($calledClass==__CLASS__){
 throw new BadMethodCallException(__CLASS__ . '::' . __METHOD__ . ' may only be called from deriver classes.');
 }
 return array_key_exists($name,Enum::$_members[$calledClass]);
 }
 
 /**
 * Returns an boolean indicating if there is a member of this enum type with this key.
 * @return boolean
 * @access public
 * @static
 * @throws \BadMethodCallException
 */
 public static function isKey($key){
 $calledClass = get_called_class();
 if($calledClass==__CLASS__){
 throw new BadMethodCallException(__CLASS__ . '::' . __METHOD__ . ' may only br called from deriver classes.');
 }
 return array_key_exists($key, self::$_arrays[$calledClass]);
 }
 
 /**
 * Returns an boolean indicating if the given string is a valid PHP identifier
 *
 * @todo move this method to another class
 * @param $string
 * @return unknown_type
 * @access protected
 * @static
 */
 private static function isValidIdentifier($string){
 return preg_match("|^[a-zA-Z_][a-zA-Z0-9_]*$|i", $string);
 }
 //------ INSTANCE ------//
 /**
 * The internal key of this instance
 * @var unknown_type
 * @access protected
 */
 private $key;
 
 /**
 * The name of this instance
 * @var unknown_type
 * @access protected
 */
 private $name;
 
 /**
 * constructs a new instance
 * @param $key the key o the instance
 * @param $name the name o the instance
 * @return unknown_type
 * @access protected
 */
 protected function __construct($key, $name){
 $this->key = $key;
 $this->name = $name;
 }
 
 /**
 * Returns the internal key of this instance.
 * @return unknown_type
 * @access public
 */
 public function getKey(){
 return $this->key;
 }
 
 /**
 * returns the name of this instance.
 * @return string
 * @access public
 */
 public function __toString(){
 return $this->name;
 }
 
 }
 ?>
 
 
 |