Web

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

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

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

Есть ли какие-то решения/обходные пути, которые вы обычно используете? Кто-нибудь помнит, были ли у ребят из PHP какие-либо мысли или решения по поводу перечислений?

 

Ответ 1

В зависимости от условий использования, я бы обычно использовал что-то простое, например, следующее:

abstract class DaysOfWeek {

    const Sunday = 0;

    const Monday = 1;

    // и т.д.

}

$today = DaysOfWeek::Sunday;

Однако в других случаях может потребоваться более тщательная проверка констант и значений. На основе комментариев ниже о reflection и некоторых других замечаний, вот расширенный пример, который может лучше служить гораздо более широкому кругу вариантов:

abstract class BasicEnum {

    private static $constCacheArray = NULL;

    private static function getConstants() {

        if (self::$constCacheArray == NULL) {

            self::$constCacheArray = [];

        }

        $calledClass = get_called_class();

        if (!array_key_exists($calledClass, self::$constCacheArray)) {

            $reflect = new ReflectionClass($calledClass);

            self::$constCacheArray[$calledClass] = $reflect->getConstants();

        }

        return self::$constCacheArray[$calledClass];

    }

    public static function isValidName($name, $strict = false) {

        $constants = self::getConstants();

        if ($strict) {

            return array_key_exists($name, $constants);

        }

        $keys = array_map('strtolower', array_keys($constants));

        return in_array(strtolower($name), $keys);

    }

    public static function isValidValue($value, $strict = true) {

        $values = array_values(self::getConstants());

        return in_array($value, $values, $strict);

    }

}

Создав простой класс enum, который расширяет BasicEnum, вы теперь можете использовать методы таким образом для простой проверки ввода:

abstract class DaysOfWeek extends BasicEnum {

    const Sunday = 0;

    const Monday = 1;

    const Tuesday = 2;

    const Wednesday = 3;

    const Thursday = 4;

    const Friday = 5;

    const Saturday = 6;

}

DaysOfWeek::isValidName('Humpday');                  // false

DaysOfWeek::isValidName('Monday');                   // true

DaysOfWeek::isValidName('monday');                   // true

DaysOfWeek::isValidName('monday', $strict = true);   // false

DaysOfWeek::isValidName(0);                          // false

DaysOfWeek::isValidValue(0);                         // true

DaysOfWeek::isValidValue(5);                         // true

DaysOfWeek::isValidValue(7);                         // false

DaysOfWeek::isValidValue('Friday');                  // false

В качестве дополнительного примечания: каждый раз, когда я использую reflection хотя бы один раз в статическом/константном классе, где данные не будут меняться (например, в перечислении), я кэширую результаты этих вызовов reflections, так как использование новых объектов каждый раз в конечном итоге будет иметь заметное влияние на производительность (хранение в ассоциативном массиве для нескольких перечислений).

Теперь, когда большинство людей, наконец, обновились по крайней мере до 5.3, и SplEnum доступен, это, безусловно, также жизнеспособный вариант если вы не возражаете против традиционно неинтуитивного понятия наличия фактических инстанций перечислений в вашей кодовой базе. В приведенном выше примере BasicEnum и DaysOfWeek вообще не могут быть инстанцированы, да и не должны.

 

Ответ 2

Как насчет констант класса?

<?php

class YourClass {

    const SOME_CONSTANT = 1;

    public function echoConstant() {

        echo self::SOME_CONSTANT;

    }

}

echo YourClass::SOME_CONSTANT;

$c = new YourClass;

$c->echoConstant();

 

Ответ 3

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

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

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

}

 

Ответ 4

Я предпочитаю использовать метод 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('Invalid enumeration value, "%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; // Не пойманное исключение 'Exception' с сообщением

                       // 'Недопустимый член перечисления, "ZAP"

 

$qux = TestEnum::$QUX;

TestEnum::$QUX == $qux; // true

'hello world!' == $qux; // false

 

 

Схожие статьи

Web

Простейшее двустороннее шифрование с использованием PHP

Web

Как загрузить файл с помощью библиотеки Java HttpClient, работающей с PHP

Web

Как проверить, является ли массив PHP ассоциативным или последовательным

Web

Как сделать так, чтобы все значения PHP echo попадали в таблицу HTML