Про web-разработку
Для программистов, фрилансеров, предпринимателей
Инструменты

Enumerator в php

В php нет встроенных enumerator-ов.

Можно быстренько написать свои. Для этого нам понадобится версия php не ниже 7.4.

Для этого нам понадобится абстрактный класс, который умеет выполнять базовые операции со списком.

А именно:

  • Инициализировать значения
  • Получать массив ключей
  • Получать массив значений
  • Возвращать весь список элементов

В этом нам очень поможет функция get_class_vars, которая возвращает список переменных класса.

Описываем абстрактный класс, а затем на его основе будем создавать нужные нам классы и будем правильно их инициализировать.

/**
 * Описываем абстрактный класс, который умеет инициализировать список вариантов используя собственные статичные переменные
 * Class ENum
 */
abstract class ENum {
    /**
     * Ленивый вариант для инициализации переменных.
     * Можно использовать в случаях, когда нам не важно, какое именно уникальное значение хранить в переменной.
     * После описания класса вызвать функцию
     * <code>(static function(){ static::lazyInit(MyClassEnumElement::class); })->bindTo(null,MyClassEnum::class)();</code>
     * @param string $class название класса, который должен быть использован для инициализации, должен быть расширением класса EnumElement
     */
    protected static function lazyInit(string $class){
        $a = get_class_vars(static::class);
        $counter = 1;
        foreach ($a as $key=>$value){
            static::$$key = new $class($key,$counter++);
        }
    }

    /**
     * Функция, которая возвращает массив ключей
     * @return string[]
     */
    static function keys(){
        /** @var EnumElement[] $a */
        $a = get_class_vars(static::class);
        /** @var string[] $res */
        $res = [];
        foreach ($a as $v){
            $res[] = $v->key;
        }
        return $res;
    }
    /**
     * Функция, которая возвращает массив значений
     * @return string[]
     */
    static function values(){
        /** @var EnumElement[] $a */
        $a = get_class_vars(static::class);
        /** @var string[] $res */
        $res = [];
        foreach ($a as $v){
            $res[] = $v->value;
        }
        return $res;
    }

    /**
     * Функция, которая возвращает все элементы
     * @return array
     */
    static function elements(){
        return get_class_vars(static::class);
    }
}

После создания класса enumerator-а нам нужно сделать простейший класс его элемента. Этот класс просто хранит ключ и значение.

/**
 * Описываем класс для элемента Enum
 * Class EnumElement
 */
class EnumElement {
    var string $key;
    var $value;
    public function __construct(string $key,$value) {
        $this->key = $key;
        $this->value = $value;
    }
}

И всё. Это весь базовый код который необходим для работы.

Далее уже с их помощью выполняем нужную нам работу.

Пример

Привожу рабочий пример с использованием данных классов.

/**
 * Создаём пустой класс, на который будем ссылаться в качестве типа переменной
 * Class EType
 */
class EType extends EnumElement {}

/**
 * Создаём enumerator, в котором идёт перечисление возможных вариантов
 * Class ETypes
 */
class ETypes extends Enum{
    // Перечисляем публичные типы

    /** @var EType $TYPE1 первый тип */
    static EType $TYPE1;

    /** @var EType второй тип */
    static EType $TYPE2;

    /**
     * Создаём приватный метод для инициализации переменных.
     * Для случаев, когда нам важно значение переменных
     */
    private static function init(){
        self::$TYPE1 = new EType('TYPE1',1);
        self::$TYPE2 = new EType('TYPE2',2);
    }

}
// Вызываем первичную приватную инициализацию
// Подробнее можно почитать тут https://www.php.net/manual/ru/closure.bindto.php
(static function(){
    static::init(); // используем этот вариант, когда нам важны значения ключей
   //static::lazyInit(EType::class); // а этот, когда не важны
})->bindTo(null,ETypes::class)();

После того как наши enumerator-ы описаны, мы можем их использовать в своём коде.

/**
 * Создаём класс, в котором хотим использовать enumerator
 * Сlass MyClass
 */
class MyClass {
    // Тут мы уже можем ссылаться на класс для описания нашего типа
    /** @var EType тип класса */
    var Etype $type;

    /**
     * И в конструкторе мы можем ссылаться на класс EType
     * @param EType $_type
     */
    public function __construct(EType $_type) {
        $this->type = $_type;
    }

    /**
     * Небольшая функция, которая как-то по разному работаем в зависимости от типа.
     * В нашем случае просто выводит его название на человеческом языке
     * @param EType $type
     */
    function echoMyType(Etype $type){
        switch ($type){
            case ETypes::$TYPE1: echo 'Type 1'; break;
            case ETypes::$TYPE2: echo 'Type 2'; break;
        }
        echo '<br/>';
    }
}

// И далее во время работы с классом можем использовать наш enumerator для создания объектов
// На эту строчки PHPStorm не ругается, ему всё нравится, компилятору тоже.
$a = new MyClass(ETypes::$TYPE1);
$a->echoMyType($a->type); // Type 1

$a->type = ETypes::$TYPE2;
$a->echoMyType($a->type); // Type 2

/*
// Эти строчки не скомпилируются и IDE должна выдать предупрждение.
// В этой строчке PHPStorm выделит единицу с предупреждением "Expected parameter of 'EType', 'int' provided
$a = new MyClass(1);
// В следующей строчке будет предупреждение "Incompatible types: "Expected parameter of '\EType', 'int' provided"
$a->type = 5;
/**/

Весь код можно посмотреть у меня в gists https://gist.github.com/viktorgvozdikov/c77d1b3b019ad646b3228e6c3c95d567

Поделиться:

Enumerator в php
Enumerator в php