Реализация enum на php

stalxed

Новичок
Замечаю в коде Java и C# что enum прикольная штука.
В php обычно для этой цели использую константы классов. Т.е. проверка инвариантов ложиться на родительский класс, что мне не нравится. Т.е. setGroup($group) в классе User должен проверять, что группа существует. setGroup(GroupEnum $group) кажется куда логичнее(проверку инвариантов осуществляет сам GroupEnum).

Достойные реализации нашёл тут:
http://php.net/manual/en/class.splenum.php
https://github.com/myclabs/php-enum

Конструктор прост, принимает один аргумент, и кидает эксепшен, если значение не существует.
Какие я вижу проблемы:
1) Сравнение со значением:
Тут несколько вариантов:
  • $group->is(GroupEnum::Admin);
  • $group == GroupEnum::Admin;
  • GroupEnum::Admin == $group;
2 и 3 я даже не уверен, какое из них будет работать... Судя по коду php-enum 2 вариант работает, а вот 3 будет ли симметрично работать ли.. не уверен. Короче игра динамической типизации. А первый не очень то красив.
2) Сравнение двух enumов
Тоже несколько вариантов:
  • $group->isEqual($anotherGroup);
  • GroupEnum::compare($group, $anotherGroup);
тоже выглядит ужас...
хотя в теории наверное должно работать:
  • $group == $anotherGroup
Теория гласит, что должен быть одинаковый класс, и равны все члены классов. Наверное должно работать, но опять магия...

Хотелось бы узнать, есть ли у кого реально достойные примеры реализации enum?
 

WMix

герр M:)ller
Партнер клуба
PHP:
in_array($group, array('anonymus','guest','user','admin'))
 

stalxed

Новичок
PHP:
in_array($group, array('anonymus','guest','user','admin'))
Это просто проверка, а я о типе enum.

Что-то вроде:
PHP:
class GroupEnum
{
    const ANONYMUS = 'anonymus';
    const GUEST = 'guest';
    const USER = 'user';
    const ADMIN = 'admin';

    private $current;

    public function __construct($current)
    {
        if (! in_array($current, $this->getAll())) {
            throw new \InvalidArgumentException();
        }

        $this->current = $current;
    }

    public function getAll()
    {
        return [
            self::ANONYMUS,
            self::GUEST,
            self::USER,
            self::ADMIN
        ];
    }

    public function __toString()
    {
        return $this->value();
    }

    public function value()
    {
        return $this->current;
    }

    public function isEqual(GroupEnum $another)
    {
        return self::compare($this, $another);
    }

    public static function compare(GroupEnum $first, GroupEnum $second)
    {
        return ($first->value() == $second->value());
    }
}
К моему удивлению, вся возможная магия работает:
PHP:
$test = new GroupEnum(GroupEnum::ADMIN);
$another = new GroupEnum(GroupEnum::ADMIN);
echo ($test == GroupEnum::ADMIN) ? '+' : '-';
echo (GroupEnum::ADMIN == $test) ? '+' : '-';
echo ($test->isEqual($another)) ? '+' : '-';
echo (GroupEnum::compare($test, $another)) ? '+' : '-';
echo ($test == $another) ? '+' : '-';
Выводятся одни плюсы.

Просто в 2015 году немного странно изобретать ENUM...
И магия здесь тоже...

Просто сторонники ООП уже имеют наверное в закромах отточенную реализацию ENUM, надеюсь что кто-нибудь поделится.
 

c0dex

web.dev 2002-...
Команда форума
Партнер клуба
Просто до 2015го года никому в голову не пришло изобретать этот велосипед :D
 

WMix

герр M:)ller
Партнер клуба
попрос зачем мне константы групп, если в базе в таблице групп у меня strings и группы динамично наполняются?
 

stalxed

Новичок
попрос зачем мне константы групп, если в базе в таблице групп у меня strings и группы динамично наполняются?
Не обязательно string, если группы заданы статично и их не нужно менять в админке.
А преобразовывать в SQL enum не проблема, в doctrine это выглядит так:

PHP:
class GroupEnumType extends Type
{
    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
        $values = '\'' . implode('\', \'', GroupEnum::getAll()) . '\'';

        return sprintf('ENUM(%s) COMMENT \'(DC2Type:%s)\'', $values, $this->getName());
    }

    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        return new GroupEnum($value);
    }

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        return $value->value();
    }

    public function getName()
    {
        return 'GroupEnum';
    }
}
 

stalxed

Новичок
когда уже готово динамично, зачем делать статично?
Этот пример является примером.

У меня в проекте сейчас до 10 случаев есть, в которых я пользуюсь просто константами, которые заданы в родительском классе, относительно enum переменной.
Код был бы значительно читабельнее, если использовать enum...
 

AnrDaemon

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

WMix

герр M:)ller
Партнер клуба
Код был бы значительно читабельнее, если использовать enum...
поставил галочку, есть права, убрал, нет прав. вообще interface это по своему enum подходят только те классы/обьекты которые этим типом являются, хоть даже interface пустой.

ну а так юзай константы, на здоровье
 

Redjik

Джедай-мастер
Просто до 2015го года никому в голову не пришло изобретать этот велосипед :D
у меня есть такой же велосипед с почти идентичной реализацией как у https://github.com/myclabs/php-enum/blob/master/src/Enum.php

вот, создал gist
https://gist.github.com/Redjik/52a801da35d7d2fe9108
все руки не дойдут доделать и тестами покрыть ;)

ЗЫ. с помощюсь serialize можно напрямую с доктриной работать
 
Последнее редактирование:

stalxed

Новичок
вот, создал gist
https://gist.github.com/Redjik/52a801da35d7d2fe9108
все руки не дойдут доделать и тестами покрыть ;)
Спасибо за код. Магии много :)
Думаю в своей реализации такое исключить GroupEnum::ADMIN();
так как new GroupEnum(GroupEnum::ADMIN) рефакторинг средства правят автоматом(название константы).

ЗЫ. с помощюсь serialize можно напрямую с доктриной работать
В доктрине удобнее использовать enum(т.е. чтобы в БД был енум), код выше(сообщение 6 в этой теме).
Минус, что для каждого конкретного типа enum такой класс создавать. Но там всего 10 строчек кода.
Поиск работает с таким. Да и если руками понадобится в БД лесть - тоже удобнее.

Выдумывать примеры, чтобы потом на их примере создавать себе сложности - дело нехитрое. И бабла под это дело тоже можно отжать нехило.
В каждом проекте есть enum типы.

поставил галочку, есть права, убрал, нет прав. вообще interface это по своему enum подходят только те классы/обьекты которые этим типом являются, хоть даже interface пустой.

ну а так юзай константы, на здоровье
Интерфейсы не подойдут, а вот классы кстати можно, пустые классы:
interface EnumGroupInterface{}
class AdminEnumGroup implements EnumGroupInterface {}
Но имхо - не удобно...

Минус констант - нужна в нескольких местах валидация списка значений! В НЕСКОЛЬКИХ! И порой эта валидация теряется.
 
Сверху