User Tools

Site Tools


php:libraries:enum

[PHP] Enum

Enums sind leider in PHP nicht wirklich implementiert. Mit dieser Abstrakten Klasse ist es relativ einfach um Enums zu erstellen und zu verwenden

Beispiele

Einfache Anwendung

Als erstes defineren wir einfach mal ein Enum mit jeweils einem Wert.

<?php
//Einbinden der AbstractEnum und createEnum() 
include_once('Enum.php');
 
//Einfacher Enum von Hand definieren
class Size extends Enum{
	public static function S(){return new self('S');}
	public static function M(){return new self('M');}
	public static function L(){return new self('L');}
	public static function XL(){return new self('XL');}
}
 
//Ein Wert aussuchen
$m = Size::M();
$xl = Size::XL();
 
//Prüfen ob es sich um ein XL handelt
//Variable gegen den Enum
echo ($xl->equal(Size::XL())) ? 'Du bist dick<br />': 'na schön<br />';
//und den Enum gegen die Variable
echo (Size::XL()->equal($m)) ? 'Du bist dick<br />': 'na schön<br />';
?>

Ausgabe:

Du bist dick
na schön

Weitere Varianten einen einfachen Enum zu erstellen

//Ein Einfacher Enum über einen Array erstellen. Array(name=>Value)
createEnum('Sex', array('men' => 'm', 'women' => 'w'));
echo Sex::men()->value;      //m
 
//Einfache Enums, bei denen die Namen und Values identisch sind, können noch einfacher erstellt werden
createEnum('Lang', 'de', 'en');
echo Lang::de()->value;      //de
 
Enum::create('Color', 'red', 'blue', 'green');
echo Color::red()->value;    //red

Verschiedene Zugriffe

//Ausgabe des Wertes
echo $m->value;
 
//Ein Enum anhand des Wertes ermitteln
$s = Size::getEnum('S');
 
//Ein Enum anhand des Keys ermitteln
$l = Size::getEnumKey('L');

Der Enum als Funktionsparameter

//Ein Wert aussuchen
$m = Size::M();
 
//Einer Funktion den Enum als Parameter mitgeben
function checkStock(Size $isize){
    //TODO: Bestand mittels DB abfragen. Hier als Beispiel ist es Hardcodiert
    switch ($isize){
        case Size::S(): return 3;
        case Size::M(): return 10;
        case Size::L(): return 0;
    }
}
 
//Funktion aufrufen
echo checkStock($m);  //10

Enum mit mehreren Paramtern

//Ein Enum mit weiteren Parametern
class Size extends Enum{
    //Zuerst die Parameter defineren. In dem Fall die Grössenbez. für Europa un Amerika
    protected $paramNames = array('eu', 'us');
    //Dann die einzelnen Werte defineren
	public static function S(){return new self('S', 44, 34);}
	public static function M(){return new self('M', 48, 38);}
	public static function L(){return new self('L', 54, 44);}
	public static function XL(){return new self('XL', 56, 46);}
}
 
//Ein Wert aussuchen
$m = Size::M();
 
//Und auf die verschiedenen Werte zugreiffen
echo "{$m->value} ist in EU-Grösse {$m->eu} und in US {$m->us}";

Ausgabe:

M ist in EU-Grösse 48 und in US 38

Ableiten von Enums

class Size extends Enum{
    //Zuerst die Parameter defineren. In dem Fall die Grössenbez. für Europa un Amerika
    protected $paramNames = array('eu', 'us');
    //Dann die einzelnen Werte defineren
	public static function S(){return new self('S', 44, 34);}
	public static function M(){return new self('M', 48, 38);}
	public static function L(){return new self('L', 54, 44);}
	public static function XL(){return new self('XL', 56, 46);}
}
 
//Die Size ableiten. Einige Werte überschreiben, einige Hinzufügen.
//Die Parameter eu und us müssen nicht mehr neu definiert werden
class ShortSize extends Size{
	public static function S(){return new self('S', 46, 36);}
	public static function M(){return new self('M', 50, 40);}
	public static function XXL(){return new self('XXL', 62, 52);}
}
 
echo ShortSize::S()->eu;    //46
echo ShortSize::L()->eu;    //54
echo ShortSize::XXL()->eu;  //62

Zugriff auf ein Enum dens nicht gibt

function exceptionErrorHandler($errno, $errstr, $errfile, $errline ) {
    if($errno & ini_get('error_reporting')) {
        throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
    }
}
set_error_handler("exceptionErrorHandler");
 
try{
    Enum::create('Sex', array('men' => 'm', 'women' => 'w'));
 
    $chield = Sex::getEnumKey('chield');
 
    } catch(Exception $e){
        echo "<b>".$e->getMessage()."</b><br />\n";
        echo nl2br($e->getTraceAsString());
    }
no Enum found for 'chield' in 'Sex'
#0 C:\xampp\htdocs\lib\enum\Enum.php(142): Enum->__getEnumKey('chield', 'Sex')
#1 C:\xampp\htdocs\test\test3.php(20): Enum::getEnumKey('chield')
#2 {main}

Code

Interface IEnum.php

IEnum.php
<?php
/**
* mpl           by ERB software
* @author       stefan.erb(at)erb-software.com
* @since        PHP 5.2
*/
 
interface IEnum{
/**
 * S T A T I S C H E   M E T H O D E N
 */
 
	/**
	 * analog der Funktion createEnum($name, $enums)
	 * @param String    Name des Enums
	 * @param Array oder Werteliste (siehe Beispiele)
	 * @static
	 */    
	public static function create($name, $enums);
 
	/**
	 * erstellt eine leere Instance der Enumklasse. Dies wird verwednet um auf
	 * Funktionen wie getEnum() etc zuzugreiffen ohne einen bestimmten ENUM auszuwählen
	 * @return Enum
	 * @static
	 */  
	public static function getInstance();
 
	/**
	 * Erstellt ein Enum anhand des Wertes
	 * @param Wert
	 * @return Enum
	 * @static
	 */    
	public static function getEnum($value);
 
	 /**
	 * Erstellt ein Enum anhand des Keys
	 * @param $key
	 * @return Enum
	 * @static
	 */
	public static function getEnumKey($key);
 
	/**
	 * wandelt die Enums in einen Array array('EnumName' => EnumValue)
	 * @return array
	 * @static
	 */
	public static function toArray($paramName = 'value');
 
	/**
	 * gibt alle Values als Array aus
	 * @param $glue
	 * @return String
	 * @static
	 */
	public static function implode($glue, $paramName = 'value');
 
/**
 * O B J E K T - M E T H O D E N
 */
 
	/**
	 * constructer
	 * @param $value
	 * @param $paramlist
	 */    
	public function __construct($value = NULL);
 
	/**
	 * gibt den Enum als String zurück. Dies entspricht dem Value
	 * @return String
	 */
	public function __toString();
 
	/**
	 * überprüft ob der Enum dem ausgewählten Enum entspricht
	 * @param $var
	 * @return Boolean
	 */
	public function equal($var);
 
	/**
	 * gibt ein Enum anhand eines passenden Wertes zurück
	 * @param $value
	 * @param $className
	 * @return Enum
	 */
	public function __getEnum($value, $className);
}
?>

Klasse Enum

Enum.php
<?php
/**
* mpl           by ERB software
* @author       stefan.erb(at)erb-software.com
* @since        PHP 5.2 (Für PHP 5.3 kann man einige Dinge einfacher machen)
*/
 
/**
 * Die Klasse Enum ist ein Kunstrukt zum Erstellen von Enumeratoren
 */
 
/**
 * php52.php beinhaltet die Funktion get_called_class() für PHP52
 * Ab PHP53 wirden diese Datei die die Funktion für PHP52 zur Verfügung stellt
 * nicht mehr gebraucht.
 */
require_once(dirname(__FILE__).'/php52.php');
require_once(dirname(__FILE__).'/IEnum.php');
 
 
/**
 * Die Funktion createEnum erstellt eine Enumklasse
 * @param String    Name des Enums
 * @param Array oder Werteliste (siehe Beispiele)
 * @example
 *      // Enum erstellen mit einer beliebigen Anzahl Parameter
 *      createEnum('Enum1', 'a', 'b');
 *      echo Enum1::a(); //-> a
 *
 *      // Enum erstellen mit einem Array. Der Key ist der Name der Funktion
 *      createEnum('Enum2', array('A' => 'aa', 'B' => 'bb'));
 *      echo Enum2::B(); //-> bb
 *      
 *      // Enum erstellen mit einem Array. Der Funktionsname ist gleich dem value
 *      createEnum('Enum3', array('aaa', 'bbb'));
 *      echo Enum3::aaa(); //-> aaa
 */
function createEnum($name, $enums){
	$params = func_get_args();  
	return call_user_func_array(array('Enum', 'create'), $params);
}
 
/*
* @example
*   class Enum1 extends AbstractEnum{
*       // die ersten 3 Funktionen müssen so 1:1 übernommen werden
*       protected static function className(){return __CLASS__;}
*       public static function getInstance(){return new self();}
*       public static function getEnum($value){$e = new self(); return $e->createEnum($value);}
*       // Ab hier kommen die verschiedenen Enums
*       public static function A(){return new self('a');}
*       public static function B(){return new self('b');}
*   }
*  
*   $enum = Enum1::A();
*   switch ($enum){
*       case Enum1::A(): echo 'a'; break;
*       case Enum1::B(): echo 'b'; break;
*       default: echo 'default';
*   }
*   if (Enum1::A()->equal($enum)) echo 'HalloA1';
*   $enum = Enum1::getEnum('a');
*/
class Enum implements IEnum{
	static public $index;
	protected $paramNames = array();
 
	/**
	 * S T A T I S C H E   M E T H O D E N
	 */
 
	/**
	 * analog der Funktion createEnum($name, $enums)
	 * @param String    Name des Enums
	 * @param Array oder Werteliste (siehe Beispiele)
	 * @static
	 * @example
	 *      // Enum erstellen mit einer beliebigen Anzahl Parameter
	 *      Enum::create('Enum1', 'a', 'b');
	 *      echo Enum1::a();    //-> a
	 *
	 *      // Enum erstellen mit einem Array. Der Key ist der Name der Funktion
	 *      Enum::create('Enum2', array('A' => 'aa', 'B' => 'bb'));
	 *      echo Enum2::B();    //-> bb
	 *      
	 *      // Enum erstellen mit einem Array. Der Funktionsname ist gleich dem Value
	 *      // zusätzlich als ist diese Klasse eine Ableitung von Enum2
	 *      Enum::create('Enum3 extends Enum2', array('aaa', 'bbb'));
	 *      echo Enum3::aaa();  //-> aaa     
	 *      echo Enum3::B();    //-> bb     
	 */
	public static function create($name, $enums){      
		if(!is_array($enums)){
			$enums = func_get_args();
			$name = array_shift($enums);
		}
		// Falls kein extends mitgegeben wurde, die Enum-Klasse als Extends angeben
		if (!preg_match('/^[[:alnum:]_]+[ ]+extends[ ]+[[:alnum:]_]+$/', $name)){
			$name = "{$name} extends ".__CLASS__;
		}
 
		// Classencode generieren
		$php = "class {$name}{\n";
		foreach($enums as $key => $value){
			if (is_numeric($key)) list($key, $value) = array($value, $value);
			preg_match_all('/[[:alnum:]_]+/i', $key, $keyElements);
			$key = implode('', $keyElements[0]);            
			$php .= "public static function {$key}(){return new self('{$value}');}\n";    
		}    
		$php .= "}\n";      
		eval($php);
	}
 
	/**
	 * erstellt eine leere Instance der Enumklasse. Dies wird verwednet um auf
	 * Funktionen wie getEnum() etc zuzugreiffen ohne einen bestimmten ENUM auszuwählen
	 * @return Enum
	 * @static
	 */  
	public static function getInstance(){
		$class = get_called_class();
		return new $class();        
	}
 
	/**
	 * Erstellt ein Enum anhand des Wertes
	 * @param Wert
	 * @return Enum
	 * @static
	 */
	public static function getEnum($value){
		return self::getInstance()->__getEnum($value, get_called_class());
	}
 
	/**
	 * Erstellt ein Enum anhand des Keys
	 * @param $key
	 * @return Enum
	 * @static
	 */
	public static function getEnumKey($key){
		return self::getInstance()->__getEnumKey($key, get_called_class());
	}    
 
	/**
	 * wandelt die Enums in einen Array array('EnumName' => EnumValue)
	 * @return array
	 * @static
	 */
	public static function toArray($paramName = 'value'){
		return self::getInstance()->__toArray(get_called_class(), 'value');
	}
 
	/**
	 * gibt alle Values als Array aus
	 * @param $glue
	 * @return String
	 * @static
	 */
	public static function implode($glue, $paramName = 'value'){
		return implode($glue, self::toArray());
	}
 
	/**
	 * O B J E K T - M E T H O D E N
	 */
 
	/**
	 * constructer
	 * @param $value
	 * @param $paramlist
	 */
	public function __construct($value = NULL){
		$this->params = func_get_args();
		$this->value = array_shift($this->params);
		foreach($this->paramNames as $index => $name){
			$this->$name = $this->params[$index];
		}
	}
	/**
	 * gibt den Enum als String zurück. Dies entspricht dem Value
	 * @return String
	 */
	public function __toString(){
		return (String) $this->value;
	}
	/**
	 * überprüft ob der Enum dem ausgewählten Enum entspricht
	 * @param $var
	 * @return Boolean
	 * @example
	 *          $enum = Enum1::A();
	 *          if (Enum1::A()->equal($enum)) echo 'HalloA1';
	 */
	public function equal($var){
		return (Boolean) (is_a($var, get_called_class())) and ($var == $this);
	}
 
	/**
	 * gibt ein Enum anhand eines passenden Wertes zurück
	 * @example
	 *      $enum = Enum1::getInstance()->__getEnum('a');    
	 * @param $value
	 * @param $className
	 * @return Enum
	 */
	public function __getEnum($value, $className){
		if($func = array_search($value, $this->__toArray($className))) return $this->$func();
		throw new Exception("no Enum found for '{$value}' in '{$className}'");
	}
 
	/**
	 * Erstellt ein Array mit allen Enums dieses Types
	 * @example $array = Enum1::getInstance()->__toArray();
	 * @return array('EnumName' => 'value')
	 */
	protected function __toArray($className, $paramName = 'value'){
		$enums = array_diff(get_class_methods($className), get_class_methods(__CLASS__));
		$er = error_reporting(E_ERROR);
		foreach($enums as $func){
			if($this->$func() instanceof self) $array[$func] = $this->$func()->$paramName;
		}
		error_reporting($er);
		return $array;        
	}
 
 
	/**
	 * gibt ein Enum anhand eines passenden Wertes zurück
	 * @example
	 *      $enum = Enum1::getInstance()->__getEnum('a');    
	 * @param $value
	 * @access protected
	 * @return Enum
	 */
	protected function __getEnumKey($key, $className){
		if(array_key_exists($key, $this->__toArray($className))) return $this->$key();
		throw new Exception("no Enum found for '{$key}' in '{$className}'");
	}
}
?>

PHP52 Funktionen

Unter PHP 5.3 ist die Funktion get_called_class() nicht bekannt. Diese ist aber sehr wichtig für die Enum-Klasse. In der Datei php52.php wird diese Funktion auch für PHP5.2 zr Verfügung gestellt.

php52.php
<?php
 
/********************************
 * Retro-support of get_called_class()
 * Tested and works in PHP 5.2.4
 * http://www.sol1.com.au/
 * http://www.php.net/manual/de/function.get-called-class.php#93799
 ********************************/ 
if(!function_exists('get_called_class')) {
	function get_called_class($bt = false,$l = 1) {
		if (!$bt) $bt = debug_backtrace();
		if (!isset($bt[$l])) throw new Exception("Cannot find called class -> stack level too deep.");
		if (!isset($bt[$l]['type'])) {
			throw new Exception ('type not set');
		}
		else switch ($bt[$l]['type']) {
			case '::':
				$lines = file($bt[$l]['file']);
				$i = 0;
				$callerLine = '';
				do {
					$i++;
					$callerLine = $lines[$bt[$l]['line']-$i] . $callerLine;
				} while (stripos($callerLine,$bt[$l]['function']) === false);
				preg_match('/([a-zA-Z0-9\_]+)::'.$bt[$l]['function'].'/',
							$callerLine,
							$matches);
				if (!isset($matches[1])) {
					// must be an edge case.
					throw new Exception ("Could not find caller class: originating method call is obscured.");
				}
				switch ($matches[1]) {
					case 'self':
					case 'parent':
						return get_called_class($bt,$l+1);
					default:
						return $matches[1];
				}
				// won't get here.
			case '->': switch ($bt[$l]['function']) {
					case '__get':
						// edge case -> get class of calling object
						if (!is_object($bt[$l]['object'])) throw new Exception ("Edge case fail. __get called on non object.");
						return get_class($bt[$l]['object']);
					default: return $bt[$l]['class'];
				}
 
			default: throw new Exception ("Unknown backtrace method type");
		}
	}
}
?>

php/libraries/enum.txt · Last modified: 09.12.2013 09:39:54 (external edit)