Перечисления в PHP
Я знаю, что в PHP пока нет нативных перечислений (Enum). Но я привык к ним из мира Java и хотел бы использовать перечисления как способ предоставления предопределенных значений, которые могли бы распознавать функции автозаполнения в IDE.
Константы решают эту задачу, но есть проблема с конфликтом пространств имен, так как они являются глобальными. Массивы не имеют проблем с пространством имен, но они слишком расплывчаты, их можно перезаписывать во время выполнения, и IDE редко умеют автоматически заполнять их ключи без дополнительных аннотаций или атрибутов для статического анализа.
Существуют ли какие-либо решения или обходные пути, которые вы обычно используете? Помнит ли кто-либо, рассуждали ли разработчики PHP о перечислениях или принимали какие-то решения по этому поводу?
5 ответ(ов)
Да, в PHP существует нативное расширение — SplEnum.
SplEnum предоставляет возможность эмулировать и создавать объекты перечислений (enumeration) нативно в PHP.
Дополнительную информацию можно найти здесь: Документация SplEnum
Обратите внимание:
Расширение PECL не включено в стандартную версию PHP.
На данный момент DLL для этого PECL-расширения недоступна.
Вопрос: Что насчет констант класса?
Ответ:
В PHP константы класса определяются с помощью ключевого слова const
. Они являются статическими и могут быть доступны без создания экземпляра класса. В приведенном вами примере мы создаем класс YourClass
, в котором определена константа SOME_CONSTANT
со значением 1.
class YourClass
{
const SOME_CONSTANT = 1; // Определяем константу класса
public function echoConstant()
{
echo self::SOME_CONSTANT; // Доступ к константе внутри метода через self
}
}
Константы класса могут быть доступны как через self
, так и через имя класса. В вашем коде используются оба подхода:
echo YourClass::SOME_CONSTANT; // Доступ к константе без создания экземпляра класса
Здесь мы сразу выводим значение константы, используя синтаксис класса.
$c = new YourClass;
$c->echoConstant(); // Вызов метода экземпляра, который выводит значение константы
При вызове метода echoConstant
мы также используем self::SOME_CONSTANT
, что является корректным способом обращения к константе внутри метода.
Таким образом, константы класса являются удобным и эффективным способом хранения неизменяемых значений, которые логически связаны с классом.
Ваш код использует классы с константами, и это вполне допустимый способ объявления значений, которые в дальнейшем не планируются к изменению. Вот пример того, как вы это реализовали:
class Enum {
const NAME = 'aaaa';
const SOME_VALUE = 'bbbb';
}
print Enum::NAME;
В данном примере вы создали класс Enum
, в котором определены две константы: NAME
и SOME_VALUE
. Константы объявляются с помощью ключевого слова const
, и их значения могут быть доступны через оператор ::
. Когда вы вызываете print Enum::NAME;
, выводится значение 'aaaa'.
Использование классов с константами может быть удобным, если вам нужно сгруппировать связанные значения, что делает код более читаемым и поддерживаемым. Однако, если вы планируете использовать лишние или очень много значений, возможно, стоит рассмотреть использование перечислений (enums) в более новых версиях PHP.
Лучший ответ выше замечательный. Однако, если вы расширяете
класс разными способами, то тот способ, который будет выполнен первым, приведёт к созданию кеша при вызове функций. Этот кеш будет использован для всех последующих вызовов, независимо от того, каким способом были инициированы вызовы.
Чтобы решить эту проблему, замените переменную и первую функцию на следующий код:
private static $constCacheArray = null;
private static function getConstants() {
if (self::$constCacheArray === null) self::$constCacheArray = array();
$calledClass = get_called_class();
if (!array_key_exists($calledClass, self::$constCacheArray)) {
$reflect = new \ReflectionClass($calledClass);
self::$constCacheArray[$calledClass] = $reflect->getConstants();
}
return self::$constCacheArray[$calledClass];
}
Это позволит избежать нежелательного кеширования, так как консистентность кеша будет обеспечиваться при каждом вызове функции.
Я прокомментировал некоторые из других ответов на этот вопрос, так что решил тоже высказать свое мнение. В конечном итоге, поскольку PHP не поддерживает типизированные перечисления, у вас есть два пути: либо реализовать типизированные перечисления с помощью неофициальных решений, либо смириться с тем, что это очень сложно сделать эффективно.
Лично я предпочитаю смириться с этим и использовать метод const
, как это сделали некоторые другие ответы здесь:
abstract class Enum
{
const NONE = null;
final private function __construct()
{
throw new NotSupportedException(); //
}
final private function __clone()
{
throw new NotSupportedException();
}
final public static function toArray()
{
return (new ReflectionClass(static::class))->getConstants();
}
final public static function isValid($value)
{
return in_array($value, static::toArray());
}
}
Вот пример перечисления:
final class ResponseStatusCode extends Enum
{
const OK = 200;
const CREATED = 201;
const ACCEPTED = 202;
// ...
const SERVICE_UNAVAILABLE = 503;
const GATEWAY_TIME_OUT = 504;
const HTTP_VERSION_NOT_SUPPORTED = 505;
}
Использование класса Enum
в качестве базового класса для всех других перечислений позволяет использовать вспомогательные методы, такие как toArray
, isValid
и т.п. Для меня типизированные перечисления (и управление их экземплярами) просто становятся слишком сложными и неуправляемыми.
Гипотетически
Если бы существовал магический метод __getStatic
(и желательно также метод __equals
), многие проблемы могли бы быть решены с помощью своего рода паттерна мультифон.
(Следующее гипотетично; это не будет работать, но, возможно, когда-нибудь будет)
final class TestEnum
{
private static $_values = [
'FOO' => 1,
'BAR' => 2,
'QUX' => 3,
];
private static $_instances = [];
public static function __getStatic($name)
{
if (isset(static::$_values[$name]))
{
if (empty(static::$_instances[$name]))
{
static::$_instances[$name] = new static($name);
}
return static::$_instances[$name];
}
throw new Exception(sprintf('Неправильное значение перечисления, "%s"', $name));
}
private $_value;
public function __construct($name)
{
$this->_value = static::$_values[$name];
}
public function __equals($object)
{
if ($object instanceof static)
{
return $object->_value === $this->_value;
}
return $object === $this->_value;
}
}
$foo = TestEnum::$FOO; // object(TestEnum)#1 (1) {
// ["_value":"TestEnum":private]=>
// int(1)
// }
$zap = TestEnum::$ZAP; // Uncaught exception 'Exception' with message
// 'Неправильный член перечисления, "ZAP"'
$qux = TestEnum::$QUX;
TestEnum::$QUX == $qux; // true
'hello world!' == $qux; // false
Надеюсь, это поможет вам лучше понять, как можно обрабатывать перечисления в PHP!
Как вывести ошибки PHP на экран?
UTF-8 на всех уровнях!
Функции startsWith() и endsWith() в PHP
Что такое потокобезопасность и непотокобезопасность в PHP?
Как получить расширение файла в PHP?