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