Pull to refresh

Comments 33

antanas-arvasevicius/enumerable-type — Строго типизированная реализация Enum.

Сколько себя помню :) всегда люди пытались реализовать Enum в PHP. Даже я пару раз пытался. Эта реализация выглядит лучше многих, но опять ограничения, прежде всего с десериализацией :( Что не так с PHP, что нельзя впилить такой тип в ядро?

Посмотрел код проекта. Оказалось все еще хуже чем я думал. Список доступных значений для ENUM получается с помощью рефлексии наследующего класса. Метод valueToString вообще мастерский говнокод.


Я ENUM в PHP рассматриваю как ValueObject и отношусь к нему соответствующе. В сущностях храню сам объект, а не его значение, список доступных значений описываю в явном виде и добавляю методы необходимыми для бизнеслогики.

enum — ValueObject или значения типа enum — ValueObject?

не очень понял вопрос.


Значение, для свойства объекта доменной области, может быть перечисление (enum). Для контроля за этим перечислением его удобно описать в виде объекта значения (ValueObject). Получается что-то вроде:


$my_type = TicketType::priority();
if ($ticket->type()->equals($my_type)) {
}

или так


if ($ticket->type()->isPriority()) {
}

Понял. Значения типа Enum — ValueObject.

все реализации ENUM в PHP через рефлексию. Так как у вас обычно классы ENUM условно маленькие а результат операции разумно кешируется во внутреннем поле, то накладные расходы весьма малы

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


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

Можно реализовать все тоже самое кеширование и прочие радости без рефлексии

это я не понял вообще


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

Это почему нельзя? что мне помешает?


Вообще всё что там дает рефлексия это убирает необходимость дублировать сами значения и как независимые единицы (в данном случае методы конструкторы) и в списке всех значений, что избавляет от дублирования и возможной человеческой ошибки. При этом как я и говорил ранее не особо нанося урон производительности.


Я вот пользуюсь реализацией myclabs/php-enum и там указываются константы.

Это почему нельзя? что мне помешает?

Пример на основе того же myclabs/php-enum. Я добавляю человеческое описание для вывода на frontend


class Action extends Enum
{
    const VIEW = 'view';
    const EDIT = 'edit';

    // константы воспринимаются как варианты значения
    // формат предназначен для перевода
    const TITLE_VIEW = 'acme.demo.action.view';
    const TITLE_EDIT = 'acme.demo.action.edit';

    // можно использовать для radio/checkbox/select
    public static function choices()
    {
        return [
            self::VIEW => self::TITLE_VIEW,
            self::EDIT => self::TITLE_EDIT,
        ];
    }

    public function title()
    {
        return static::choices()[$this->getValue()];
        // можно былоб писать проще и не создавать метод choices,
        // но тогда такую конструкцию тяжелее использовать для radio/checkbox/select
        //return 'acme.demo.action.'.$this->getValue();
    }
}

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


Пример на основе antanas-arvasevicius/enumerable-type. Я добавляю свои методы для бизнеслогики


class Action extends EnumerableType
{
    final public static View()
    {
        return static::get('view');
    }

    final public static Edit()
    {
        return static::get('edit');
    }

    // воспринимается как вариант значения
    final public static equals(Action $action)
    {
        return $this->id() === $action->id();
    }
}

Чёт я запутался… вы пишете:


… случае использования подхода с рефлексией этого сделать нельзя.

я с этим не согласился и потом вы мне приводите именно пример что можно! Что то пошло не так?


в случая же 2 вы как то сразу схитрили. Зачем метод делать final public static? Да именно такие, и только такие, методы воспринимаются как значения. Ну ограничение той реализации. Точно так же как в myclabs/php-enum константы. Что особо не влияет на саму возможность делать


методы необходимые для бизнес логики и человеческое описание для вариантов значения для вывода на frontend.
я с этим не согласился и потом вы мне приводите именно пример что можно! Что то пошло не так?

Как раз я привожу пример что нельзя. myclabs/php-enum воспринимает константы с описанием как варианты значения. Тоесть метод Action::toArray() вернет TITLE_VIEW, что не корректно. Можно объявить TITLE_VIEW как не константу, но это все ухищрения чтоб угодить инструменту, а не бизнесу.


Хорошее решение этой проблемы предлагает библиотека marc-mabe/php-enum, которая воспринимает только публичные константы, но нужен PHP 7.1.


в случая же 2 вы как то сразу схитрили. Зачем метод делать final public static?

Да, схитрил. Естественно. Потому что с точки зрения бизнеса не должно быть final и не final методов. Весь объект должен быть final. А если инструмент требует помечать методы как final, то я вынужден и остальные методы тоже помечать как final, чтоб защитить класс.


Я хочу сказать, что используя подход с рефлексией, мы получаем автоматизацию генерации списка доступных значений (Action::choices()), и в тоже время некоторые ограничения на использование методов или констант.
Создав один метод Action::choices() вручную, мы сохраним все те же самые функции, но не будем иметь ограничений накладываемых рефлексией.

А… да по поводу непонимания мой косяк. Извиняюсь был невнимателен. День тяжелый.


Но всё же.


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

Да, собственно я с этого начал. И да, вы правы ограничения есть, однако это не это не равно утверждению "с рефлексией этого сделать нельзя".

И да, вы правы ограничения есть, однако это не это не равно утверждению "с рефлексией этого сделать нельзя".

Если ограничения не позволяют нам что-то сделать, значит это сделать нельзя. Разве нет?
Если ограничения накладываются в результате использования рефлексии, то значит "с рефлексией этого сделать нельзя".
Вроде все логично.


Предлагаю подвести итоги.
Можно использовать как явное описание списка доступных значений, так и динамически генерируемое с помощью рефлексии. Оба вариант имеют свои плюсы и минусы. Я услышал ваше мнение и признаю, что вариант через рефлексию тоже имеет право на жизнь.

Попробовал реализацию через рефлексию. Оказалось действительно в чем-то удобнее. Спасибо что просветили.


Проблему с человеческим описанием в myclabs/php-enum можно решить так:


final class Action extends Enum
{
    const VIEW = 'view';
    const EDIT = 'edit';

    // можно использовать для radio/checkbox/select
    public static function choices()
    {
        $choices = [];
        foreach (self::values() as $value) {
            $choices[$value->getValue()] = (string) $value;
        }

        return $choices;
    }

    public function __toString()
    {
        return 'acme.demo.action.'.strtolower($this->getKey());
    }
}

Но вот решил я сравнить производительность реализации через рефлексию и с явным описанием вариантов значений и получилось что рефлексия в 3-4 раза медленнее в зависимости от версии php.


$ php tests/benchmark.php 100000
Reflection enum: 1.25 MiB - 14079 ms
Reflection enum no magic: 1.25 MiB - 11286 ms
Explicit enum: 1.25 MiB - 3221 ms

Тест no magic это тест без использования магических функций __call() и __callStatic() и даже он оказывается медленнее в 3 раза.
Отсюда делаем вывод. За удобство нужно платить.

Хотелось бы взглянуть на бенчмарк полностью.

Вангую что нет кэша для рефлексий.


В целом же профит от enum-ов в возможности проверять по типам. Без этого смысла заморачиваться нет.

огорчу. кеш есть.


Для сравнения добавил в тест:



Результат:


$ php tests/benchmark.php 100000
Reflection enum: 1.25 MiB - 10401 ms
Reflection enum no magic: 1.25 MiB - 11382 ms
MyClabs enum: 1.25 MiB - 18531 ms
MarcMabe enum: 1.25 MiB - 11422 ms
MarcMabe enum no magic: 1.25 MiB - 11278 ms
HappyTypes enum: 1.50 MiB - 6992 ms
Explicit enum: 1.50 MiB - 2848 ms

билд и сорцы.

Отличная подборка в этот раз. Нашёл для себя новые штуки. Одно только огорчило:


Двухфакторная аутентификация в Laravel с помощью SMS (Twilio)

Не стоит рекламировать SMS в качестве фактора аутентификации. Дырявый канал.

А можно попросить накинуть инфы об этом? В гугле не забанили, но мб у вас под рукой есть инфа.

https://wiki.php.net/rfc/fallback-to-root-scope-deprecation

Самый странный RFC который я видел, это кому то реально нужно?

Как я понимаю, сильно упростит создание автозагрузчиков функций.

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

Автору Maxlab/stacker большое спасибо, недавно начал осваивать Docker, а тут все и сразу да еще и с виде о описанием.

На здоровье!) Ролики, только нужно переписать будет, на скорость делал, с первого раза — гомно конечно.

justinrainbow/json-schema не очень удобно использовать. thephpleague/json-guard как-то поприятнее.

Sign up to leave a comment.