[Из песочницы] Реализация перечислений (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)); } Все работает, как мы и хотели. На этом все. Надеюсь кому-то данная конструкция пригодится.