Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Wednesday, April 27, 2016

PHP and Enumerations

PHP and Enumerations


I know that PHP doesn't have native Enumerations. But I have become accustomed to them from the Java world. I would love to use enums as a way to give predefined values which IDEs' auto completion features could understand.

Constants do the trick, but there's the namespace collision problem and (or actually because) they're global. Arrays don't have the namespace problem, but they're too vague, they can be overwritten at runtime and IDEs rarely (never?) know how to autofill their keys.

Are there any solutions/workarounds you commonly use? Does anyone recall whether the PHP guys have had any thoughts or decisions around enums?

Answer by Noah Goodrich for PHP and Enumerations


The most common solution that I have seen to enum's in PHP has been to create a generic enum class and then extend it. You might take a look at this.

UPDATE: Alternatively, I found this from phpclasses.org.

Answer by andy.gurin for PHP and Enumerations


I used classes with constants:

class Enum {      const NAME       = 'aaaa';      const SOME_VALUE = 'bbbb';  }    print Enum::NAME;  

Answer by Peter Bailey for PHP and Enumerations


What about class constants?

echoConstant();  

Answer by Brian Cline for PHP and Enumerations


Depending upon use case, I would normally use something simple like the following:

abstract class DaysOfWeek  {      const Sunday = 0;      const Monday = 1;      // etc.  }    $today = DaysOfWeek::Sunday;  

However, other use cases may require more validation of constants and values. Based on the comments below about reflection, and a few other notes, here's an expanded example which may better serve a much wider range of cases:

abstract class BasicEnum {      private static $constCacheArray = NULL;        private static function getConstants() {          if (self::$constCacheArray == NULL) {              self::$constCacheArray = [];          }          $calledClass = get_called_class();          if (!array_key_exists($calledClass, self::$constCacheArray)) {              $reflect = new ReflectionClass($calledClass);              self::$constCacheArray[$calledClass] = $reflect->getConstants();          }          return self::$constCacheArray[$calledClass];      }        public static function isValidName($name, $strict = false) {          $constants = self::getConstants();            if ($strict) {              return array_key_exists($name, $constants);          }            $keys = array_map('strtolower', array_keys($constants));          return in_array(strtolower($name), $keys);      }        public static function isValidValue($value, $strict = true) {          $values = array_values(self::getConstants());          return in_array($value, $values, $strict);      }  }  

By creating a simple enum class that extends BasicEnum, you now have the ability to use methods thusly for simple input validation:

abstract class DaysOfWeek extends BasicEnum {      const Sunday = 0;      const Monday = 1;      const Tuesday = 2;      const Wednesday = 3;      const Thursday = 4;      const Friday = 5;      const Saturday = 6;  }    DaysOfWeek::isValidName('Humpday');                  // false  DaysOfWeek::isValidName('Monday');                   // true  DaysOfWeek::isValidName('monday');                   // true  DaysOfWeek::isValidName('monday', $strict = true);   // false  DaysOfWeek::isValidName(0);                          // false    DaysOfWeek::isValidValue(0);                         // true  DaysOfWeek::isValidValue(5);                         // true  DaysOfWeek::isValidValue(7);                         // false  DaysOfWeek::isValidValue('Friday');                  // false  

As a side note, any time I use reflection at least once on a static/const class where the data won't change (such as in an enum), I cache the results of those reflection calls, since using fresh reflection objects each time will eventually have a noticeable performance impact (Stored in an assocciative array for multiple enums).

Now that most people have finally upgraded to at least 5.3, and SplEnum is available, that is certainly a viable option as well--as long as you don't mind the traditionally unintuitive notion of having actual enum instantiations throughout your codebase. In the above example, BasicEnum and DaysOfWeek cannot be instantiated at all, nor should they be.

Answer by Christopher Fox for PHP and Enumerations


If you need to use enums that are globally unique (i.e. even when comparing elements between different Enums) and are easy to use, feel free to use the following code. I also added some methods that I find useful. You will find examples in the comments at the very top of the code.

   *   * @version 1.0   *   * This class provides the function of an enumeration.   * The values of Enum elements are unique (even between different Enums)   * as you would expect them to be.   *   * Constructing a new Enum:   * ========================   *   * In the following example we construct an enum called "UserState"   * with the elements "inactive", "active", "banned" and "deleted".   *    *    * Enum::Create('UserState', 'inactive', 'active', 'banned', 'deleted');   *    *   * Using Enums:   * ============   *   * The following example demonstrates how to compare two Enum elements   *   *    * var_dump(UserState::inactive == UserState::banned); // result: false   * var_dump(UserState::active == UserState::active); // result: true   *    *   * Special Enum methods:   * =====================   *   * Get the number of elements in an Enum:   *   *    * echo UserState::CountEntries(); // result: 4   *    *   * Get a list with all elements of the Enum:   *   *    * $allUserStates = UserState::GetEntries();   *    *   * Get a name of an element:   *   *    * echo UserState::GetName(UserState::deleted); // result: deleted   *    *   * Get an integer ID for an element (e.g. to store as a value in a database table):   * This is simply the index of the element (beginning with 1).   * Note that this ID is only unique for this Enum but now between different Enums.   *   *    * echo UserState::GetDatabaseID(UserState::active); // result: 2   *    */  class Enum  {        /**       * @var Enum $instance The only instance of Enum (Singleton)       */      private static $instance;        /**       * @var array $enums    An array of all enums with Enum names as keys       *          and arrays of element names as values       */      private $enums;        /**       * Constructs (the only) Enum instance       */      private function __construct()      {          $this->enums = array();      }        /**       * Constructs a new enum       *       * @param string $name The class name for the enum       * @param mixed $_ A list of strings to use as names for enum entries       */      public static function Create($name, $_)      {          // Create (the only) Enum instance if this hasn't happened yet          if (self::$instance===null)          {              self::$instance = new Enum();          }            // Fetch the arguments of the function          $args = func_get_args();          // Exclude the "name" argument from the array of function arguments,          // so only the enum element names remain in the array          array_shift($args);          self::$instance->add($name, $args);      }        /**       * Creates an enumeration if this hasn't happened yet       *        * @param string $name The class name for the enum       * @param array $fields The names of the enum elements       */      private function add($name, $fields)      {          if (!array_key_exists($name, $this->enums))          {              $this->enums[$name] = array();                // Generate the code of the class for this enumeration              $classDeclaration =     "class " . $name . " {\n"                          . "private static \$name = '" . $name . "';\n"                          . $this->getClassConstants($name, $fields)                          . $this->getFunctionGetEntries($name)                          . $this->getFunctionCountEntries($name)                          . $this->getFunctionGetDatabaseID()                          . $this->getFunctionGetName()                          . "}";                // Create the class for this enumeration              eval($classDeclaration);          }      }        /**       * Returns the code of the class constants       * for an enumeration. These are the representations       * of the elements.       *        * @param string $name The class name for the enum       * @param array $fields The names of the enum elements       *       * @return string The code of the class constants       */      private function getClassConstants($name, $fields)      {          $constants = '';            foreach ($fields as $field)          {              // Create a unique ID for the Enum element              // This ID is unique because class and variables              // names can't contain a semicolon. Therefore we              // can use the semicolon as a separator here.              $uniqueID = $name . ";" . $field;              $constants .=   "const " . $field . " = '". $uniqueID . "';\n";              // Store the unique ID              array_push($this->enums[$name], $uniqueID);          }            return $constants;      }        /**       * Returns the code of the function "GetEntries()"       * for an enumeration       *        * @param string $name The class name for the enum       *       * @return string The code of the function "GetEntries()"       */      private function getFunctionGetEntries($name)       {          $entryList = '';                    // Put the unique element IDs in single quotes and          // separate them with commas          foreach ($this->enums[$name] as $key => $entry)          {              if ($key > 0) $entryList .= ',';              $entryList .= "'" . $entry . "'";          }            return  "public static function GetEntries() { \n"              . " return array(" . $entryList . ");\n"              . "}\n";      }        /**       * Returns the code of the function "CountEntries()"       * for an enumeration       *        * @param string $name The class name for the enum       *       * @return string The code of the function "CountEntries()"       */      private function getFunctionCountEntries($name)       {          // This function will simply return a constant number (e.g. return 5;)          return  "public static function CountEntries() { \n"              . " return " . count($this->enums[$name]) . ";\n"              . "}\n";      }        /**       * Returns the code of the function "GetDatabaseID()"       * for an enumeration       *        * @return string The code of the function "GetDatabaseID()"       */      private function getFunctionGetDatabaseID()      {          // Check for the index of this element inside of the array          // of elements and add +1          return  "public static function GetDatabaseID(\$entry) { \n"              . "\$key = array_search(\$entry, self::GetEntries());\n"              . " return \$key + 1;\n"              . "}\n";      }        /**       * Returns the code of the function "GetName()"       * for an enumeration       *       * @return string The code of the function "GetName()"       */      private function getFunctionGetName()      {          // Remove the class name from the unique ID           // and return this value (which is the element name)          return  "public static function GetName(\$entry) { \n"              . "return substr(\$entry, strlen(self::\$name) + 1 , strlen(\$entry));\n"              . "}\n";      }    }      ?>  

Answer by zanshine for PHP and Enumerations


Here is a github library for handling type-safe enumerations in php:

This library handle classes generation, classes caching and it implements the Type Safe Enumeration design pattern, with several helper methods for dealing with enums, like retrieving an ordinal for enums sorting, or retrieving a binary value, for enums combinations.

The generated code use a plain old php template file, which is also configurable, so you can provide your own template.

It is full test covered with phpunit.

php-enums on github (feel free to fork)

Usage: (@see usage.php, or unit tests for more details)

getName()\n";  foreach (FruitsEnum::iterator() as $enum)  {    echo "  " . $enum->getName() . "\n";  }    echo "->getValue()\n";  foreach (FruitsEnum::iterator() as $enum)  {    echo "  " . $enum->getValue() . "\n";  }    echo "->getOrdinal()\n";  foreach (CachedFruitsEnum::iterator() as $enum)  {    echo "  " . $enum->getOrdinal() . "\n";  }    echo "->getBinary()\n";  foreach (CachedFruitsEnum::iterator() as $enum)  {    echo "  " . $enum->getBinary() . "\n";  }  

Output:

FruitsEnum::APPLE() == FruitsEnum::APPLE(): bool(true)  FruitsEnum::APPLE() == FruitsEnum::ORANGE(): bool(false)  FruitsEnum::APPLE() instanceof Enum: bool(true)  FruitsEnum::APPLE() instanceof FruitsEnum: bool(true)  ->getName()    APPLE    ORANGE    RASBERRY    BANNANA  ->getValue()    apple    orange    rasberry    bannana  ->getValue() when values have been specified    pig    dog    cat    bird  ->getOrdinal()    1    2    3    4  ->getBinary()    1    2    4    8  

Answer by aelg for PHP and Enumerations


Well, for a simple java like enum in php, I use:

class SomeTypeName {   private static $enum = array(1 => "Read", 2 => "Write");     public function toOrdinal($name) {    return array_search($name, self::$enum);   }     public function toString($ordinal) {    return self::$enum[$ordinal];   }  }  

And to call it:

SomeTypeName::toOrdinal("Read");  SomeTypeName::toString(1);  

But I'm a PHP beginner, struggling with the syntax so this might not be the best way. I experimented some with Class Constants, using Reflection to get the constant name from it's value, might be neater.

Answer by Arturgspb for PHP and Enumerations


Yesterday I wrote this class on my blog. I think it's maybe be easy for use in php scripts:

final class EnumException extends Exception{}    abstract class Enum  {      /**       * @var array ReflectionClass       */      protected static $reflectorInstances = array();      /**       * ?????? ?????????????????? ???????-????????? enum       * @var array       */      protected static $enumInstances = array();      /**       * ?????? ???????????? ????????->???? ???????????? ??? ???????? -        * ???? ?? ????????? ? ????? ?????????       * @var array       */      protected static $foundNameValueLink = array();        protected $constName;      protected $constValue;        /**       * ????????? ??????? "????????"       * ?????????? ?????? ?????????, ?? ?? ??? ?????? ??? ???????????? ?? ?????,        * ?.?. ??? ???? ?????????? "????????? ?????" __toString()       * ??? ?????? ?????????????? ?????? ??? ????????? ??? ??? ?????????       * @paradm Node       */      final public static function get($value)      {          // ??? ???????? ????? ??? ?????????? ?????????????????? (?? ??????? ~10%)          $name = self::getName($value);          if ($name === false)              throw new EnumException("?????????? ?????????");          $className = get_called_class();              if (!isset(self::$enumInstances[$className][$name]))          {              $value = constant($className.'::'.$name);              self::$enumInstances[$className][$name] = new $className($name, $value);          }            return self::$enumInstances[$className][$name];      }        /**       * ?????????? ?????? ???????? ??? ????-???????? ????? ????????????       * @return array        */      final public static function toArray()      {          $classConstantsArray = self::getReflectorInstance()->getConstants();          foreach ($classConstantsArray as $k => $v)              $classConstantsArray[$k] = (string)$v;          return $classConstantsArray;      }        /**       * ??? ???????????? ????????????? ? toArray ??? ????????? ??????? ???????? ????->????????        * @return ReflectionClass       */      final private static function getReflectorInstance()      {          $className = get_called_class();          if (!isset(self::$reflectorInstances[$className]))          {              self::$reflectorInstances[$className] = new ReflectionClass($className);          }          return self::$reflectorInstances[$className];      }        /**       * ???????? ??? ????????? ?? ?? ????????       * @param string $value       */      final public static function getName($value)      {          $className = (string)get_called_class();            $value = (string)$value;          if (!isset(self::$foundNameValueLink[$className][$value]))          {              $constantName = array_search($value, self::toArray(), true);              self::$foundNameValueLink[$className][$value] = $constantName;          }          return self::$foundNameValueLink[$className][$value];      }        /**       * ???????????? ?? ????? ??? ????????? ? ????????????       * @param string $name       */      final public static function isExistName($name)      {          $constArray = self::toArray();          return isset($constArray[$name]);      }        /**       * ???????????? ?? ????? ???????? ????????? ? ????????????       * @param string $value       */      final public static function isExistValue($value)      {          return self::getName($value) === false ? false : true;      }             final private function __clone(){}        final private function __construct($name, $value)      {          $this->constName = $name;          $this->constValue = $value;      }        final public function __toString()      {          return (string)$this->constValue;      }  }  

Usage:

class enumWorkType extends Enum  {          const FULL = 0;          const SHORT = 1;  }  

Answer by markus for PHP and Enumerations


There is a native extension, too. The SplEnum

SplEnum gives the ability to emulate and create enumeration objects natively in PHP.

http://www.php.net/manual/en/class.splenum.php

Answer by user667540 for PHP and Enumerations


I like enums from java too and for this reason I write my enums in this way, I think this is the most similiar behawior like in Java enums, of course, if some want to use more methods from java should write it here, or in abstract class but core idea is embedded in code below

  class FruitsEnum {        static $APPLE = null;      static $ORANGE = null;        private $value = null;        public static $map;        public function __construct($value) {          $this->value = $value;      }        public static function init () {          self::$APPLE  = new FruitsEnum("Apple");          self::$ORANGE = new FruitsEnum("Orange");          //static map to get object by name - example Enum::get("INIT") - returns Enum::$INIT object;          self::$map = array (              "Apple" => self::$APPLE,              "Orange" => self::$ORANGE          );      }        public static function get($element) {          if($element == null)              return null;          return self::$map[$element];      }        public function getValue() {          return $this->value;      }        public function equals(FruitsEnum $element) {          return $element->getValue() == $this->getValue();      }        public function __toString () {          return $this->value;      }  }  FruitsEnum::init();    var_dump(FruitsEnum::$APPLE->equals(FruitsEnum::$APPLE)); //true  var_dump(FruitsEnum::$APPLE->equals(FruitsEnum::$ORANGE)); //false  var_dump(FruitsEnum::$APPLE instanceof FruitsEnum); //true  var_dump(FruitsEnum::get("Apple")->equals(FruitsEnum::$APPLE)); //true - enum from string  var_dump(FruitsEnum::get("Apple")->equals(FruitsEnum::get("Orange"))); //false    

Answer by Anders for PHP and Enumerations


This is my take on "dynamic" enum... so that i can call it with variables, ex. from a form.

look at updated verison below this codeblock...

$value = "concert";  $Enumvalue = EnumCategory::enum($value);  //$EnumValue = 1    class EnumCategory{      const concert = 1;      const festival = 2;      const sport = 3;      const nightlife = 4;      const theatre = 5;      const musical = 6;      const cinema = 7;      const charity = 8;      const museum = 9;      const other = 10;        public function enum($string){          return constant('EnumCategory::'.$string);      }  }  

UPDATE: Better way of doing it...

class EnumCategory {        static $concert = 1;      static $festival = 2;      static $sport = 3;      static $nightlife = 4;      static $theatre = 5;      static $musical = 6;      static $cinema = 7;      static $charity = 8;      static $museum = 9;      static $other = 10;    }  

Call with

EnumCategory::${$category};  

Answer by Andi T for PHP and Enumerations


I use interface instead of class:

interface DaysOfWeek  {      const Sunday = 0;      const Monday = 1;      // etc.  }    var $today = DaysOfWeek::Sunday;  

Answer by Tiddo for PHP and Enumerations


I know this is an old thread, however none of the workarounds I've seen really looked like enums, since almost all workarounds requires you to manually assign values to the enum items, or it requires you to pass an array of enum keys to a function. So I created my own solution for this.

To create an enum class using my solution one can simply extend this Enum class below, create a bunch of static variables (no need to initialize them), and make a call to yourEnumClass::init() just below the definition of your enum class.

edit: This only works in php >= 5.3, but it can probably be modified to work in older versions as well

/**   * A base class for enums.    *    * This class can be used as a base class for enums.    * It can be used to create regular enums (incremental indices), but it can also be used to create binary flag values.   * To create an enum class you can simply extend this class, and make a call to ::init() before you use the enum.   * Preferably this call is made directly after the class declaration.    * Example usages:   * DaysOfTheWeek.class.php   * abstract class DaysOfTheWeek extends Enum{   *      static $MONDAY = 1;   *      static $TUESDAY;   *      static $WEDNESDAY;   *      static $THURSDAY;   *      static $FRIDAY;   *      static $SATURDAY;   *      static $SUNDAY;   * }   * DaysOfTheWeek::init();   *    * example.php   * require_once("DaysOfTheWeek.class.php");   * $today = date('N');   * if ($today == DaysOfTheWeek::$SUNDAY || $today == DaysOfTheWeek::$SATURDAY)   *      echo "It's weekend!";   *    * Flags.class.php   * abstract class Flags extends Enum{   *      static $FLAG_1;   *      static $FLAG_2;   *      static $FLAG_3;   * }   * Flags::init(Enum::$BINARY_FLAG);   *    * example2.php   * require_once("Flags.class.php");   * $flags = Flags::$FLAG_1 | Flags::$FLAG_2;   * if ($flags & Flags::$FLAG_1)   *      echo "Flag_1 is set";   *    * @author Tiddo Langerak   */  abstract class Enum{        static $BINARY_FLAG = 1;      /**       * This function must be called to initialize the enumeration!       *        * @param bool $flags If the USE_BINARY flag is provided, the enum values will be binary flag values. Default: no flags set.       */       public static function init($flags = 0){          //First, we want to get a list of all static properties of the enum class. We'll use the ReflectionClass for this.          $enum = get_called_class();          $ref = new ReflectionClass($enum);          $items = $ref->getStaticProperties();          //Now we can start assigning values to the items.           if ($flags & self::$BINARY_FLAG){              //If we want binary flag values, our first value should be 1.              $value = 1;              //Now we can set the values for all items.              foreach ($items as $key=>$item){                  if (!isset($item)){                                       //If no value is set manually, we should set it.                      $enum::$$key = $value;                      //And we need to calculate the new value                      $value *= 2;                  } else {                      //If there was already a value set, we will continue starting from that value, but only if that was a valid binary flag value.                      //Otherwise, we will just skip this item.                      if ($key != 0 && ($key & ($key - 1) == 0))                          $value = 2 * $item;                  }              }          } else {              //If we want to use regular indices, we'll start with index 0.              $value = 0;              //Now we can set the values for all items.              foreach ($items as $key=>$item){                  if (!isset($item)){                      //If no value is set manually, we should set it, and increment the value for the next item.                      $enum::$$key = $value;                      $value++;                  } else {                      //If a value was already set, we'll continue from that value.                      $value = $item+1;                  }              }          }      }  }  

Answer by Weke for PHP and Enumerations


I use a construction like the following for simple enums. Typically you can use them for switch statements.

  

It is not as conviniet as a native enum like in C++ but it seems to work and requires less maintenance if you later would like to add an option in between.

Answer by KDog for PHP and Enumerations


My attempt to create an enum with PHP...it's extremely limited since it doesn't support objects as the enum values but still somewhat useful...

class ProtocolsEnum {        const HTTP = '1';      const HTTPS = '2';      const FTP = '3';        /**       * Retrieve an enum value       * @param string $name       * @return string       */      public static function getValueByName($name) {          return constant('self::'. $name);      }         /**       * Retrieve an enum key name       * @param string $code       * @return string       */      public static function getNameByValue($code) {          foreach(get_class_constants() as $key => $val) {              if($val == $code) {                  return $key;              }          }      }        /**       * Retrieve associate array of all constants (used for creating droplist options)       * @return multitype:       */      public static function toArray() {                return array_flip(self::get_class_constants());      }        private static function get_class_constants()      {          $reflect = new ReflectionClass(__CLASS__);          return $reflect->getConstants();      }  }  

Answer by Vincent Pazeller for PHP and Enumerations


The accepted answer is the way to go and is actually what I am doing for simplicity. Most advantages of enumeration are offered (readable, fast, etc.). One concept is missing, however: type safety. In most languages, enumerations are also used to restrict allowed values. Below is an example of how type safety can also be obtained by using private constructors, static instantiation methods and type checking:

class DaysOfWeek{   const Sunday = 0;   const Monday = 1;   // etc.     private $intVal;   private function __construct($intVal){     $this->intVal = $intVal;   }     //static instantiation methods   public static function MONDAY(){     return new self(self::Monday);   }   //etc.  }    //function using type checking  function printDayOfWeek(DaysOfWeek $d){ //compiler can now use type checking    // to something with $d...  }    //calling the function is safe!  printDayOfWeek(DaysOfWeek::MONDAY());  

We could even go further: using constants in the DaysOfWeek class might lead to misusage: e.g. one might mistakenly use it this way:

printDayOfWeek(DaysOfWeek::Monday); //triggers a compiler error.  

which is wrong (calls integer constant). We can prevent this using private static variables instead of constants:

class DaysOfWeeks{      private static $monday = 1;    //etc.      private $intVal;    //private constructor    private function __construct($intVal){      $this->intVal = $intVal;    }      //public instantiation methods    public static function MONDAY(){      return new self(self::$monday);    }    //etc.        //convert an instance to its integer value    public function intVal(){      return $this->intVal;    }    }  

Of course, it is not possible to access integer constants (this was actually the purpose). The intVal method allows to convert a DaysOfWeek object to its integer representation.

Note that we could even go further by implementing a caching mechanism in instantiation methods to save memory in the case enumerations are extensively used...

Hope this will help

Answer by Brian Fisher for PHP and Enumerations


I have taken to using the approach below as it gives me the ability to have type safety for function parameters, auto complete in NetBeans and good performance. The one thing I don't like too much is that you have to call [extended class name]::enumerate(); after defining the class.

abstract class Enum {        private $_value;        protected function __construct($value) {          $this->_value = $value;      }        public function __toString() {          return (string) $this->_value;      }        public static function enumerate() {          $class = get_called_class();          $ref = new ReflectionClass($class);          $statics = $ref->getStaticProperties();          foreach ($statics as $name => $value) {              $ref->setStaticPropertyValue($name, new $class($value));          }      }  }    class DaysOfWeek extends Enum {      public static $MONDAY = 0;      public static $SUNDAY = 1;      // etc.  }  DaysOfWeek::enumerate();    function isMonday(DaysOfWeek $d) {      if ($d == DaysOfWeek::$MONDAY) {          return true;      } else {          return false;      }  }    $day = DaysOfWeek::$MONDAY;  echo (isMonday($day) ? "bummer it's monday" : "Yay! it's not monday");  

Answer by Herv Labas for PHP and Enumerations


Pointed out solution works well. Clean and smooth.

However, if you want strongly typed enumerations, you can use this:

class TestEnum extends Enum  {      public static $TEST1;      public static $TEST2;  }  TestEnum::init(); // Automatically initializes enum values  

With an Enum class looking like:

class Enum  {      public static function parse($enum)      {          $class = get_called_class();          $vars = get_class_vars($class);          if (array_key_exists($enum, $vars)) {              return $vars[$enum];          }          return null;      }        public static function init()      {          $className = get_called_class();          $consts = get_class_vars($className);          foreach ($consts as $constant => $value) {              if (is_null($className::$$constant)) {                  $constantValue = $constant;                  $constantValueName = $className . '::' . $constant . '_VALUE';                  if (defined($constantValueName)) {                      $constantValue = constant($constantValueName);                  }                  $className::$$constant = new $className($constantValue);              }          }      }        public function __construct($value)      {          $this->value = $value;      }  }  

This way, enum values are strongly typed and

TestEnum::$TEST1 === TestEnum::parse('TEST1') // true statement

Answer by Dan King for PHP and Enumerations


Some good solutions on here!

Here's my version.

  • It's strongly typed
  • It works with IDE auto-completion
  • Enums are defined by a code and a description, where the code can be an integer, a binary value, a short string, or basically anything else you want. The pattern could easily be extended to support orther properties.
  • It asupports value (==) and reference (===) comparisons and works in switch statements.

I think the main disadvantage is that enum members do have to be separately declared and instantiated, due to the descriptions and PHP's inability to construct objects at static member declaration time. I guess a way round this might be to use reflection with parsed doc comments instead.

The abstract enum looks like this:

code = $code;          $this->description = $description;      }        public function getCode()      {          return $this->code;      }        public function getDescription()      {          return $this->description;      }  }  

Here's an example concrete enum:

Which can be used like this:

getCode().' : '.EMyEnum::$MY_FIRST_VALUE->getDescription().PHP_EOL.PHP_EOL;    var_dump(EMyEnum::getAllMembers());    echo PHP_EOL.EMyEnum::getByCode(2)->getDescription().PHP_EOL;  

And produces this output:

1 : My first value

array(3) {
[1]=>
object(EMyEnum)#1 (2) {
["code":"AbstractEnum":private]=>
int(1)
["description":"AbstractEnum":private]=>
string(14) "My first value"
}
[2]=>
object(EMyEnum)#2 (2) {
["code":"AbstractEnum":private]=>
int(2)
["description":"AbstractEnum":private]=>
string(15) "My second value"
}
[3]=>
object(EMyEnum)#3 (2) {
["code":"AbstractEnum":private]=>
int(3)
["description":"AbstractEnum":private]=>
string(14) "My third value"
}
}

My second value

Answer by jglatre for PHP and Enumerations


abstract class Enumeration  {      public static function enum()       {          $reflect = new ReflectionClass( get_called_class() );          return $reflect->getConstants();      }  }      class Test extends Enumeration  {      const A = 'a';      const B = 'b';      }      foreach (Test::enum() as $key => $value) {      echo "$key -> $value
"; }

0 comments:

Post a Comment

Popular Posts

Powered by Blogger.