Реализация перечислений (Enum) в PHP с проверкой типа

от автора

Иногда в коде приходится использовать строго типизированные параметры, однако сам язык PHP не является строго типизированным (как, например, C#, в котором присутствует такой тип данных, как перечисления – Enum). Однако выход из данной ситуации все равно можно найти. Порывшись по просторам интернета я так и не нашел подходящего мне решения. Предлагаю вам мое решение данной проблемы.

Проблема заключалась в следующем. Необходимо реализовать функцию, которая бы на вход принимала строго типизированный объект (класс), однако в теле функций необходим перебор значений данного класса и сравнение его с предопределенными константами (этого же класса):

function test(Data $data) {     switch ((string)$data) {         case Data::ID:             echo 'This is ID' . PHP_EOL;             break;         case Data::STRING:             echo 'This is a STRING' . PHP_EOL;             break;      } } 

Однако, сразу становится очевидным, что PHP выдаст предупреждение о том, что невозможно использовать объект в качестве параметра оператора switch.

Решение проблемы состоит в том, чтобы реализовать абстрактный класс, в качестве базового для всех классов, где будут использоваться предопределенные константы, с которыми и будет происходить сравнение в операторе switch.

Код данного класса:

/**  * Base class, witch implements enumeration in child classes.  *  * PHP version 5  *  * @author Andrey Klimenko  * @license 2012, Andrey Klimenko  * @version 1.0.0  */ abstract class AbstractEnum {     /**      * @var AbstractEnum Class instance      */     protected static $instance = null;     /**      * @var mixed Value to compare with class constants      */     protected static $value;      /**      * Protected constructor (realize singleton pattern).      *      * @final      */     protected final function __construct()     {     }      /**      * Protect from object cloning (realize singleton pattern).      *      * @final      */     protected final function __clone()     {     }      /**      * Protect from reconstruct resources that the object may have (realize singleton pattern).      *      * @final      */     protected final function __wakeup()     {     }      /**      * Return instance of this object.      *      * @static      *      * @param mixed $value Constant value      *      * @return AbstractEnum      */     public static function getInstance($value)     {          if (self::$instance === null) {             self::$instance = new static();         }          self::setConstant($value); // Set value of constant          return self::$instance;     }      /**      * Prepare to return constant value, given in getInstance() function.      *      * @return string      */     public final function __toString()     {          return (string)static::$value;      }      /**      * Set constant value.      *      * @static      *      * @param mixed $value Constant value      *      */     protected static function setConstant($value)     {         $class = new \ReflectionClass(static::$instance); // Get this class reflection         $constants = array_flip($class->getConstants()); // Get constants of this object          // Check if constant with given value exist         if (array_key_exists($value, $constants)) {             $constantName = $constants[$value];             static::$value = $class->getConstant($constantName); // Set constant value         } else {             trigger_error('Class does not have constant with this value: `' . $value . '`', E_USER_ERROR);         }     } } 

Теперь реализуем класс, который наследуется от абстрактного класса, описанного выше. Данный класс является классом, который включает в себя константы, с которыми мы и будем производить сравнение.

/**  * Sample class, witch implement AbstractEnum abstract class.  *  * PHP version 5  *  * @author Andrey Klimenko  * @license 2012, Andrey Klimenko  * @version 1.0.0  */ class Data extends AbstractEnum {     const ID = 1;       // First constant     const STRING = 2;   // Second constant } 

Теперь реализуем функцию, в которой проверим работу нашего класса. Основной проблемой, как я уже сказал была строгая типизация параметра, передаваемого в нее. В качестве типа мы и указываем наш класс Data.

/**  * Test function.  *  * @param Data $data Class to check value with.  *  * @return void  */ function test(Data $data) {     // First - convert object to string     switch ((string)$data) {     // compare needed values         case Data::ID:             echo 'This is ID' . PHP_EOL;             break;         case Data::STRING:             echo 'This is a STRING' . PHP_EOL;             break;      } } 

И последнее – проверим нашу функцию:

for ($i = 1; $i < 3; $i++) {     test(Data::getInstance($i)); } 

Все работает, как мы и хотели. На этом все. Надеюсь кому-то данная конструкция пригодится.

ссылка на оригинал статьи http://habrahabr.ru/post/173311/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *