13

Перечисления в PHP

7

Я знаю, что в PHP пока нет нативных перечислений (Enum). Но я привык к ним из мира Java и хотел бы использовать перечисления как способ предоставления предопределенных значений, которые могли бы распознавать функции автозаполнения в IDE.

Константы решают эту задачу, но есть проблема с конфликтом пространств имен, так как они являются глобальными. Массивы не имеют проблем с пространством имен, но они слишком расплывчаты, их можно перезаписывать во время выполнения, и IDE редко умеют автоматически заполнять их ключи без дополнительных аннотаций или атрибутов для статического анализа.

Существуют ли какие-либо решения или обходные пути, которые вы обычно используете? Помнит ли кто-либо, рассуждали ли разработчики PHP о перечислениях или принимали какие-то решения по этому поводу?

5 ответ(ов)

1

Да, в PHP существует нативное расширение — SplEnum.

SplEnum предоставляет возможность эмулировать и создавать объекты перечислений (enumeration) нативно в PHP.

Дополнительную информацию можно найти здесь: Документация SplEnum

Обратите внимание:

Расширение PECL не включено в стандартную версию PHP.

На данный момент DLL для этого PECL-расширения недоступна.

0

Вопрос: Что насчет констант класса?

Ответ:

В 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, что является корректным способом обращения к константе внутри метода.

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

0

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

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

print Enum::NAME;

В данном примере вы создали класс Enum, в котором определены две константы: NAME и SOME_VALUE. Константы объявляются с помощью ключевого слова const, и их значения могут быть доступны через оператор ::. Когда вы вызываете print Enum::NAME;, выводится значение 'aaaa'.

Использование классов с константами может быть удобным, если вам нужно сгруппировать связанные значения, что делает код более читаемым и поддерживаемым. Однако, если вы планируете использовать лишние или очень много значений, возможно, стоит рассмотреть использование перечислений (enums) в более новых версиях PHP.

0

Лучший ответ выше замечательный. Однако, если вы расширяете класс разными способами, то тот способ, который будет выполнен первым, приведёт к созданию кеша при вызове функций. Этот кеш будет использован для всех последующих вызовов, независимо от того, каким способом были инициированы вызовы.

Чтобы решить эту проблему, замените переменную и первую функцию на следующий код:

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];
}

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

0

Я прокомментировал некоторые из других ответов на этот вопрос, так что решил тоже высказать свое мнение. В конечном итоге, поскольку 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!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь