PHP-Дайджест № 164 (27 августа – 9 сентября 2019)


    Свежая подборка со ссылками на новости и материалы. В выпуске: PHP 7.4.0 RC1, Laravel 6, Monolog 2 и другие релизы, Union Types и прочие новости из PHP Internals, порция полезных инструментов, пачка подкастов и многое другое.

    Приятного чтения!



    Новости и релизы


    • PHP 7.4.0 RC1 — Стартовал цикл релиз-кандидатов ветки 7.4. Ожидается всего не менее 6 выпусков для стабилизации.
    • PHP 7.1.32, PHP 7.2.22, PHP 7.3.9 — Релизы с исправлениями критичных ошибок безопасности, всем пользователям рекомендуется обновиться.
    • Monolog 2.0.0 — После 4 лет разработки представлен Monolog 2.0. Требует PHP 7.2, используются тайпхинты где возможно и strict_types. Добавлены новые хэндлеры (SqsHandler, TelegramBotHandler) и другое. Monolog 1.x будет поддерживаться и дальше.

    PHP Internals


    • [RFC] Reclassifying engine warnings — Предлагается пересмотреть бросаемые нотисы и ворнинги в движке PHP и поправить классификацию там, где необходимо. Во многих случаях предлагается повысить уровень ошибки: Notice -> Warning, например, при попытке получить свойство у не-объекта, и Warning -> Error exception, например, при попытке использовать скаляр как массив. Ну и «Undefined variable» будет бросать Warning.
    • [RFC] Union Types v2 — Предлагается ввести объединённые типы – это значит, что переменная может принимать один из перечисленных типов. Де-факто объединённые типы давно используются в PHPDoc, но теперь они действительно будут проверяться самим интерпретатором.
      Предлагаемый синтаксис T1|T2|... может быть использованы везде, где типы можно указывать сейчас:
      Скрытый текст
      class Number {
          private int|float $number;
      
          public function setNumber(int|float $number): void {
              $this->number = $number;
          }
      
          public function getNumber(): int|float {
              return $this->number;
          }
      }
      

      В качестве эксперимента RFC оформлен в виде пулл-реквеста и любой желающий может прокомментировать или выразить реакцию в виде emoji. Финальное голосование будет проходить так же, как и раньше на wiki.php.net. Судя по сообщению Никиты, первый эксперимент прошёл неплохо и было получено много ценных комментариев от сообщества.
    • error_reporting=E_ALL in PHP 8 — В PHP 8 по умолчанию уровень ошибок будет установлен в E_ALL вместо текущего: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED.

    Инструменты



    Symfony



    Laravel


    • Laravel 6 — На конференции LaraconEU представлено мажорное обновление фреймворка. Это LTS релиз и будет получать обновления безопасности в течение 3 лет. Также теперь релиз следует Semver, а значит версия 7 выйдет уже этой зимой. Более подробно об изменениях на русском, и в видео на Laracasts.
    • facade/ignition — Кроме прочего, в Laravel 6 реализована новая страница ошибок. Она базируется на Whoops, но предоставляет больше информации и даже предлагает исправлять простые ошибки не покидая браузера.
    • beyondcode/laravel-view-xray — Удобно подсветит и подпишет вьюшки на странице.
    • stefanzweifel/laravel-stats 2.0 — Artisan-команда, которая выведет разнообразную статистику кода.
    • video PHP Townhall #68: Behind the Facade — Taylor Otwell в гостях у Matt Trask и Ben Edmunds обсуждают, что нового в Laravel, бизнес-сторону дел, и организацию LaraconUS.

    Yii



    Async PHP



    Материалы для обучения



    Аудио/Видео



    Сообщество


    • Matthew Weier O'Phinney, лидер Zend Framework (Laminas) и один из основателей PHP-FIG, закончил работу в Zend и ищет новое место
    • Автор Xdebug Derick Rethans рассматривает возможность сделать Xdebug 3 платным для коммерческого использования. На что Joe Watkins ответил, что экосистеме нужен бесплатный отладчик и в таком случае он будет вынужден реализовать альтернативное расширение.

    Спасибо за внимание!

    Если вы заметили ошибку или неточность — сообщите, пожалуйста, в личку.
    Вопросы и предложения пишите на почту или в твиттер.

    Больше новостей и комментариев в Telegram-канале PHP Digest.

    Прислать ссылку
    Поиск ссылок по всем дайджестам
    Предыдущий выпуск: PHP-Дайджест № 163

    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 61

      +5

      Думаю, что самым обсуждаемым будет Union Types v2.
      Крайне не хотел бы его введения, будет помогать писать некачественный код.

        +2
        <sarcasm_mode>Останется только добавить mixed, который позволит использовать любой тип</sarcasm_mode>
          +1

          А потом и вовсе отказаться от статической типизации. И так по кругу.

          +1
          Согласен. Не вижу смысла его вводить после введения строгой типизации. А вы проголосовали в pull-request?
            0

            Не согласен. Почему сразу некачественный код? Лично мне нравится предложение Union Types. Бывают ситуации, когда нужно вернуть/передать какой-то тип вместе с другим (например, ?string). Теперь же можно будет добавить и еще каких-то типов, если нужно. Что в этом плохого? Лично я бы не пропагандировал их использовать везде, но это хорошая возможность управление данными. Сходу так сложно придумать пример из жизни, но они точно есть. Еще, к примеру, много встроенных функций языка возвращают комбинированные типы. И с этим прекрасно справляется статический анализатор. Насколько будет проще, когда не нужно будет писать PHPDoc комменты с пояснением, что за тип будет у переменной. PHP уже давно движется к полной строгой типизации, и в этом нет ничего плохого. Некачественный код получается не тогда, когда в языке есть возможность, а тогда, когда эти возможности используют бездумно. И сейчас язык вполне себе помогает писать некачественный код, даже без Union Types.

              +2

              Соглашусь с Вами, что некачественный код можно написать тысяча и одним способом и сейчас. Но вот предложение тоже не поддерживаю. Когда язык обязывает указывать строго один тип (в крайнем случае nullable), вам так или иначе придется преобразование типов выносить либо наверх, либо в другой метод, тем самым упрощая этот конкретный метод, избавляя его от необходимости обрабатывать несколько типов. В других строго-типизированных языках такие проблемы решаются перегрузкой, когда для разного набора аргументов (в том числе других типов) определяется другой одноименный метод, реализующий требуемую логику именно для такого набора. Вычитать такой код будет куда проще, чем когда все в одной куче. И уж если PHP идет в сторону более строгой типизации, то было бы логичнее придерживаться более строгого подхода и в данном случае тоже, и вполне достаточно того, что можно типы не указывать вообще. ИМХО, естественно.

                0

                levchick согласен по поводу декомпозиции. Но бывают ситуации, когда всё-таки нужно обработать несколько типов. Сейчас это только mixed. Помоему, с Union Types типизация станет намного строже в данных случаях. Я как раз-таки и смогу контролировать типы, а не использовать mixed и внутри метода проверять, а что же там пришло в аргументе.

                  +1

                  Так может декомпозировать и эти ситуации?:) Моя идея в том, что если язык движется в сторону строгости, то и разработчики должны писать более строгий код и стараться избегать ситуаций, где допустимы несколько типов одного аргумента или возвращаемых значений. Там где "капец как надо" или для легаси останется возможность тип не указывать вообще. А принятие этого предложения фактически легализует возможность юзать несколько типов везде где надо и не надо, что точно не приведет к росту качества php-кода.

                0

                Как пример (сильно не пинайте, возможно я чёт не правильно понимаю =) ):


                public function decode(array|MyClassInterface $object): string|false
                {
                    return json_encode($object);
                }

                Что мы имеем: я не проверяю, вернёт ли json_encode false, просто говорю в сигнатуре, что может вернуть false, и это уже проблема пользователя функции, а не моя. Если захочет — проверит и выкинет исключение. Так же я явно указываю, что метод примет или массив, или объект какого-то интерфейса. Остальное будет TypeError, и не нужно писать проверок и создавать и выбрасывать свои исключения. Замечательно же, нет?

                  0

                  В этом случае нужно в phpDoc описать в каких случаях у вас будет false. Кроме этого, чтобы получить информацию о том, почему же все таки false, пользователю метода нужно будет догадаться вызвать json_last_error, а если вы вдруг решите метод поменять и парсить json как-то по другому? Здесь как раз таки намного разумнее кинуть исключение, если string не может быть возвращен, с описанием собственно почему, а пользователю уже решать, что с этим делать.

                    0

                    Хмм… Хорошее замечание, я даже не подумал об этом. Но json_decode чисто для примера. Представьте, что это метод, который принимает или объект или массив для инициализации объекта и возвращает строку или false. Да, наверное, логичнее было бы вернуть ?string.

                      0
                      Представьте, что это метод, который принимает или объект или массив для инициализации объекта и возвращает строку или false

                      Так проблема то собственно не в json, а в том, что вы должны


                      1. Четко документировать в каких случаях может быть false (то есть от phpdoc мы не избавимся)
                      2. Предоставить механизм получения детальной информации, что пошло не так

                      И это все решается как раз таки механизмом исключений.


                      Пример, когда возвращается либо результат, либо false, в принципе очень не удачен. Для того же json_decode в 7.3 ввели опцию JSON_THROW_ON_ERROR, чтобы в случае ошибки не false возвращать, а исключение кидать. И в целом этот подход считается одним из самых не удачных во встроенных функциях php

                        –1

                        Хорошо, я согласен что return-type не лучший вариант для использования в данном конкретном примере. И честно говоря, я бы на самом деле делал с исключениями, как Вы и говорите, я просто стараюсь отстоять мнение, что Union Types это скорее хорошо, чем плохо. Например, что скажете, если я хочу в метод аргументом передать array|MyClassInterface и ничего более?

                          +1
                          Я, например, скажу, что это плохой код. Если вам надо обрабатывать массив — делайте метод который принимает массив.
                            0

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


                            Очевидно что, для MyClassInterface и для array логика обработки разная либо нам нужно привести аргументы к одному типу, тогда ваш метод будет выглядеть примерно так


                            public function decode(array|MyClassInterface $item): string
                            {
                                if (is_array($item)) {
                                    // do some logic for array or cast type to MyClassInterface
                                    return ...;
                                }
                            
                                // do some logic for MyClassInterface
                                return ...;
                            }

                            Я бы не назвал это хорошим кодом. В данном методе как минимум два блока кода с разной логикой, которая явно напрашивается быть разбитой на два метода. Кроме того, что если потребуется реализовать decode для какого-нибудь другого типа? Нам придется модифицировать метод и добавлять еще одно условие и т.д.


                            В классических языках со строгой типизацией это бы решилось простой перегрузкой и код превратился бы в что-то типо того


                            public function decode(array $item): string
                            {
                                // do some logic for array or cast type and call decode with MyClassInterface argument
                                return ...;
                            }
                            
                            public function decode(MyClassInterface $item): string
                            {
                                // do some logic for MyClassInterface
                                return ...;
                            }

                            И таким образом компилятор/интерпретатор бы сам решал, какой метод использовать в зависимости от аргумента. К сожалению, в php такой код работать не будет, но мы можем реализовать его немного по другому:


                            public function decodeArray(array $item): string
                            {
                                // do some logic for array or cast type and call decode with MyClassInterface argument
                                return ...;
                            }
                            
                            public function decodeMyClass(MyClassInterface $item): string
                            {
                                // do some logic for MyClassInterface
                                return ...;
                            }

                            Таким образом, если нам необходимо будет реализовать decode для чего-то еще нам нужно всего лишь добавить еще один метод (а не модифицировать старый Open/Closed из SOLID) и мы гарантированно не сломаем то, что уже работает.


                            Либо, если array можно представить в виде MyClassInterface, то реализовать MyArrayClass, который бы создавался из массива и имплементировал MyClassInterface, и у нас бы остался decode с одним типом аргумента. Ну и конечно наоборот, когда MyClassInterface можно представить в виде массива…


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

                              0
                              Единственный валидный кейс который я вижу это в функции дергающей внешнее апи возвращать SuccessResponse или ErrorResponse в зависимости от того что вернуло апи, но как по мне этого явно недостаточно для введение такой сомнительной фичи.
                                +1

                                Так пусть возвращаемым типом будет ResponseInterface, а SuccessResponse и ErrorResponse будут его имплементировать. И фич никаких не нужно.

                                  0
                                  Именно так я и делаю. Я просто привёл более-менее валидный кейс. Сам я тоже против такой фичи.
                                –1
                                В данном методе как минимум два блока кода с разной логикой, которая явно напрашивается быть разбитой на два метода.

                                Не совсем согласен. Допустим, у меня массив с параметрами инициализации дефолтного объекта MyClassInterface. Что-то типа такого:


                                public function decode(array|MyClassInterface $item): string
                                {
                                    if (is_array($item)) {
                                        $item = new DefaultItem($item);
                                    }
                                
                                    if(!$item instanceof MyClassInterface) {
                                        throw new InvalidArgumentException('...');
                                    }
                                    return $item->decode();
                                }

                                Это нужно читать так: я предоставляю метод, который является оберткой к decode и может принимать разные типы аргумента. Но не любые. Это должен быть или массив для инициализации объекта, или сам объект. Да, я могу предоставить два метода для decodeArray и для decodeMyClass. А могу один. Соблюдая строгую типизацию.
                                Т.е. я просто привожу типы в одном методе, а реализация по факту одна — MyClassInterface::decode. Так же в Вашем примере всё также нужно проверить, какой тип пришел, что бы вызвать соответствующий метод и нужно так или иначе полагаться на mixed (где-то же Вы должны решить какой из методов decodeArray или decodeMyClass вызвать) и выбрасывать исключение, если это не array или MyClassInterface. В случае с Union Type всё что не array|MyClassInterface будет автоматически TypeError, а в Вашем случае — не факт что разумно названное исключение (если не просто Exception или InvalidArgumentException).
                                По этому мне в коде достаточно проверить, массив ли это, если да — преобразовать, потом на всякий случай проверить преобразовало ли, если был массив (потому что иначе это в любом случае MyClassInterface), а потом вызвать MyClassInterface::decode. Более того, не всякий массив подойдёт и может сфейлится конструктор, к примеру. То, о чем Вы говорите, и как есть сейчас — придется в любом случае проверить тип, и писать свитч? (утрирую, извините =) )


                                А вообще, это можно, видимо, долго спорить =) Кто-то из нас не полностью прав в отношении к Union Types. Возможно я. Возможно Вы. Увидим после голосования =)


                                P.S. решил почитать больше информации. Нашел уже отклоненный v1. Надо изучить подробней, чем они отличаются, а то судьба у первой версии незавидная.

                                  0
                                  Понимаю, что это пример. Но, что можно делать методом decode() с массивом?
                                    –1

                                    тот метод можно назвать как угодно. Пускай это будет doSomething и принимать не массив, а что-то другое. Допустим — string. Не цепляйтесь за слова, пожалуйста.

                                      +1
                                      Хорошо, не буду цепляться.
                                      Данный пример мне очень сильно напоминает WordPress, с их WP_Post|Int|Null и пачкой проверок внутри. В связи с этим и хочется найти практическое применение UT, а не примеры в вакууме. Сам я пока не могу придумать, где и как такое применять.
                                        –1
                                        Сам я пока не могу придумать, где и как такое применять.

                                        Думаю, Вы согласитесь, что это совсем не означает, что кейсов не существует. Как минимум — это отказ от типов в аннотациях (наконец-то). Еще фичи, что мне нравятся: type alias, псевдотипы, нормальный nullable (а не ?), В PR не сравнивают это с WordPress, и видимо, видят кейсы, судя по комментариям.
                                        Не знаю. Мне нравятся UT. Если Вам нет — не пользуйтесь, никто же насильно не будет заставлять. Я устал дискутировать, честно говоря. За сим откланяюсь.

                          –1
                          Здесь как раз таки намного разумнее кинуть исключение, если string не может быть возвращен, с описанием собственно почему, а пользователю уже решать, что с этим делать.

                          С Union Types можно это исключение просто вернуть. Его и сейчас можно вернуть, только обрабатывать это неудобно, и контролировать сложнее, надо не использовать типизацию вообще.

                          +3
                          Такой метод не должен возвращать false
                          Он должен или возвращать строку если все получилось, или кидать исключение, если что то пошло не так.
                            –1

                            Не должен, но json_encode возвращает. И не только эта функция. В данном конкретном примере я предоставляю метод который возвращает результат работы функции SPL. Допустим там еще много вычислений перед возвратом. Конечный пользователь может не использовать метод, посчитать сам и использовать json_encode, если ему больше нравится return false оттуда

                              –1

                              К слову, нам и не нужно возвращать false, т.к. благодаря union types у нас гарантировано (я так думаю) будет строка. И исключений выбрасывать не нужно. В общем, это просто пример.

                                +1
                                с версии 7.3 он уже может кидать исключение при указании JSON_THROW_ON_ERROR
                                Ну и в целом стандартная либа php явно не образец для подражания в плане качественого API
                                  –2

                                  Ну, как-бы, читать не надо, да. Главное — вцепиться в конкретный пример с json_encode =) Я на всё это выше уже отвечал. Не вижу смысла повторять еще раз для того, кто не читает =)

                                    +2

                                    Пример json_encode как раз показывает, что такой подход не многим нравится. Если бы такое поведением было приемлемым, то его бы не меняли.

                                +1
                                Поддерживаю. Хотя в некоторых ситуациях использую ?Type, чтобы вернуть null.
                                  0

                                  Вот пример Union Types. Строка так же может быть валидным Uri.


                                  <?php
                                  
                                  use Psr\Http\Message\UriInterface;
                                  
                                  class MyClass
                                  {
                                      public function request(string|UriInterface $uri): void
                                      {
                                          $uri = $uri instanceof UriInterface ? $uri->getPath() : $uri;
                                          if (!filter_var($uri, FILTER_VALIDATE_URL)) {
                                              throw new \InvalidArgumentException('...');
                                          }
                                          # ...
                                      }
                                  }

                                  Спасибо, что сливаете мне рейтинг =) Надеюсь, увижу пример отрефактореного метода.

                                    –1

                                    Еще парочка примеров:


                                    $user = getUser();
                                    
                                    function getUser(string|int $id): User|null
                                    {
                                        ...
                                    }
                                    
                                    $node = parseNode($str);
                                    
                                    function parseNode(): NodeType1|NodeType2|NodeType3
                                    {
                                        ...
                                    }
                                      +1

                                      Переписал на текущем синтаксисе. И зачем тут Union?


                                      $user = getUser((string) $id);
                                      
                                      function getUser(string $id): ?User
                                      {
                                          ...
                                      }
                                      
                                      $node = parseNode($str);
                                      
                                      function parseNode(): NodeInterface
                                      {
                                          ...
                                      }
                                        –1
                                        И зачем тут Union?

                                        Так ?User это и есть Union, просто частный случай. Зачем оставлять только частный случай, если можно сделать универсально?


                                        NodeInterface

                                        Это не то же самое. Они все должны теперь этот интерфейс откуда-то импортировать и добавить в объявление класса, даже если parseNode никаких функций не вызывает. Это повышает взаимозависимости. Я бы даже сказал, интерфейс в данном случае это имитация множественных типов, за счет того что от него могут разные типы наследоваться. Интерфейсы без методов это вот как раз такая штука. А если скажем есть еще один тип, который этот интерфейс реализует, но возвращать в этом месте программы его не надо, то все еще сложнее становится.

                                          0
                                          Так ?User это и есть Union

                                          NullPointer? Объект в php — указатель. Указатель может принимать значение null. Тут нет таких противоречий и этот тип уже есть в языке.


                                          Это не то же самое. Они все должны теперь этот интерфейс откуда-то импортировать и добавить в объявление класса, даже если parseNode никаких функций не вызывает. Это повышает взаимозависимости. Я бы даже сказал, интерфейс в данном случае это имитация множественных типов, за счет того что от него могут разные типы наследоваться

                                          Ой какая каша у вас в знаниях…


                                          1. Классы не наследуются от интерфейсов.
                                          2. Интерфейсы позволяют унифицировать поведение, если у вас есть какие-то классы обладающие одинаковым api, коду уже будет не важно в большинстве случаев какая конкретно у вас реализация.
                                          3. Интерфейсы уменьшают взаимозависимости т.к. скрывают детали реализации.

                                          Просто для примера: допустим у вас есть сущности совершенно разных классов которые умеют кастится в сообщение для логов. Нам достаточно будет накинуть интерфейс с методом, условно, toLog и в принимающем методе логгера затайпхинтить по этому интерфейсу. Или вы предлагаете и в логгере написать Union со всеми классами которые этот метод имплементируют?

                                            0
                                            Объект в php — указатель. Указатель может принимать значение null

                                            php -r "class C{} var_dump(gettype(new C()), gettype(null));"
                                            Command line code:1:
                                            string(6) "object"
                                            Command line code:1:
                                            string(4) "NULL"

                                            В PHP это union type. Это же написано и в обсуждаемом RFC.


                                            Ой какая каша у вас в знаниях…
                                            Классы не наследуются от интерфейсов.

                                            Знаете, я как-то рассчитывал, что с учетом темы дискуссии вы не будете придираться к этой формулировке, тем более что далее у меня есть фраза "А если скажем есть еще один тип, который этот интерфейс реализует".


                                            В контексте проверки типов это ничем не отличается от наследования, instanceof вернет true в обоих случаях.


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

                                            Да, я в курсе. И как мой комментарий этому противоречит?


                                            Какое поведение задает интерфейс без методов? Поискал в проекте на Symfony, аж 70 штук нашлось. Один из них даже связан с деревом нод Symfony\Component\Config\Definition\Builder\NodeParentInterface.


                                            Интерфейсы уменьшают взаимозависимости т.к. скрывают детали реализации.

                                            Покажите пожалуйста, где тут детали реализации, если SomeType это класс, и почему их нет, если SomeType интерфейс?


                                            function f(SomeType $v) {
                                                $v->doSomething($arg1);
                                                $v->doSomethingElse($arg2);
                                            }
                                            

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


                                            Нам достаточно будет накинуть интерфейс с методом, условно, toLog

                                            Ну накиньте на какой-нибудь класс из папки vendor. Получится?)


                                            Или вы предлагаете и в логгере написать Union со всеми классами которые этот метод имплементируют?

                                            Я предложил конкретный пример с parseNode(). Это не значит, что так нужно делать везде.

                                              +1
                                              Знаете, я как-то рассчитывал, что с учетом темы дискуссии вы не будете придираться к этой формулировке

                                              Прошу прощения, это было лишним.


                                              Какое поведение задает интерфейс без методов

                                              Это маркер, но мы скорее говорим о непустых интерфейсах


                                              В моем примере NodeTypeN могут находиться вообще в разных библиотеках, разрабатываемых разными людьми и ничего не знать друг про друга

                                              Тогда вам и обрабатывать их придётся совершенно раздельно, иначе остаётся только надежда, что у них не изменится поведение.
                                              А как это поведение тестировать вообще не представляю


                                              Ну накиньте на какой-нибудь класс из папки vendor. Получится?)

                                              Разумеется получится. Адаптеры, прокси, декораторы на выбор


                                              Я предложил конкретный пример с parseNode()

                                              Вы указали абстрактный пример с parseNode()

                                                0
                                                Это маркер, но мы скорее говорим о непустых интерфейсах

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


                                                Тогда вам и обрабатывать их придётся совершенно раздельно

                                                Ну да, поэтому также нужна конструкция для удобной обработки на вызывающей стороне. Но это не главное, они даже могут иметь один интерфейс. Запись с union types задает ограничение. Допустим, у нас есть еще один тип NodeType4, но возвращать его в этой части программы не надо. В вашем варианте есть возможность ошибки.


                                                Разумеется получится. Адаптеры, прокси, декораторы на выбор

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


                                                Вы указали абстрактный пример с parseNode()

                                                Согласен, но я обозначил конкретную область применения — парсинг строки. Это не означает, что тот же подход подойдет для логирования.

                                          –1

                                          Перепишите еще мой пример.

                                  +1
                                  Бывают ситуации, когда нужно вернуть/передать какой-то тип вместе с другим

                                  Возможно Вы имеете в виду «вернуть несколько аргументов из метода» — так для этого Union Types не нужен — с этим вполне справляется и массив (как минимум):
                                  return [$val1, $val2];

                                  В C# для этого завезли кортежи, в Java можно часто встретить что-то типа:
                                  class Pair<T, U> { ... }

                                  Что к слову тоже не является примером отличного кода — ведь как так? Вы не знаете какой тип Вы должны вернуть и прибегаете к обобщенному коду?
                                  (например, ?string)

                                  Ну так это влияние других ЯП e.g., Java/C#, что к слову не самое топовое решение, почитайте про null pointer hell.
                                  Еще, к примеру, много встроенных функций языка возвращают комбинированные типы.

                                  А вот это вообще отдельная песня. SPL уж точно не является примером для подражания. Нашумевшая, хоть уже и несколько устаревшая статья «PHP — fractal of bad design» является отличной иллюстрацией моих слов.
                                  Насколько будет проще, когда не нужно будет писать PHPDoc комменты с пояснением, что за тип будет у переменной.

                                  Я возможно скажу что-то новое для Вас, но PHPDoc комменты нужны скорее для того, чтобы понять зачем «интерфейсу» метода нужны те или иные параметры. Типы PHPDoc'a уже не так нужны, если Вы не работаете со старым legacy кодом конечно.
                                  Некачественный код получается не тогда, когда в языке есть возможность, а тогда, когда эти возможности используют бездумно. И сейчас язык вполне себе помогает писать некачественный код, даже без Union Types.

                                  Согласен, вот только сама возможность использовать Union Types не будет помагать с решением этой проблемы ничем — только усугублять.
                                    –1
                                    Возможно Вы имеете в виду «вернуть несколько аргументов из метода» — так для этого Union Types не нужен

                                    Нет же. Я как раз о возвращаемом типе значения. Допустим — это или int, или string.


                                    Что к слову тоже не является примером отличного кода

                                    Полностью согласен, ведь я говорил не об возврате нескольких значений, а о типе.


                                    SPL уж точно не является примером для подражания

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


                                    Я возможно скажу что-то новое для Вас, но PHPDoc комменты нужны

                                    PHPDoc комменты нужны также для автокомплита в IDE, например где то в теле метода я получил обьект, но у метода не было return-type и IDE не знает что это, ну я и пишу:


                                    public function method():void
                                    {
                                    # some code
                                    /** @var InstanceInterface $object */
                                    $object = SomeOtherClass::get($someObjectName);
                                    # some code

                                    так же указание типов в PHPDoc важно и для статических анализаторов.

                                      +1
                                      Тем не менее, к сожалению, я вынужден ней пользоватся.

                                      К сожалению, мы все вынуждены ей пользоваться. Но Вы ведь понимаете, что union types не сделает SPL лучше?
                                      Как говорили выше — куда лучше выбрасывать ошибку в непонятной ситуации, а не false.
                                      Ну и как бы там ни было — ни того ни другого скорее всего в SPL не будет, твит на тему.

                                      PHPDoc комменты нужны также для автокомплита в IDE, например где то в теле метода я получил обьект, но у метода не было return-type и IDE не знает что это, ну я и пишу

                                      Опять таки, ну и чем Вам здесь поможет union types?

                                      Как бы то ни было, я просто хочу подрезюмировать свое отношение к данному предложению — строго отрицательное. И вот почему: жизнь это не упростит аж никак (ну хочется прогеру поговнокодить, вернуть несколько различных типов — никто же не заставляет писать m():types), а вот потенциальных неприятных вещей добавит уж точно.
                                        –1
                                        union types не сделает SPL лучше

                                        Понимаю, и не утверждаю, что сделает


                                        ну и чем Вам здесь поможет union types?

                                        а тем, что вместо написания union types в PHPDoc я буду писать его в коде, там где мне нужно. И не нужно говорить о том, что это всё надо рефакторить, я это понимаю. Просто у меня уже привычка в PHPDoc писать тип для свойства класса. В 7.3 эта привычка не нужна (хотя я честно говоря еще особо не пользовался 7.3 =)). Так же мне иногда приходится писать union types в PHPDoc для статического анализатора (и не только потому, что мне так хочется). Честно говоря, я был бы не против иметь возможность писать эти типы "легально", а не с помощью аннотаций. По моему это просто самый обычный самодокументирующийся код. Но опять же, я прекрасно понимаю, что могу ошибаться. Ну хочется мне поговнокодить, что поделать. Вам от этого хуже не будет =) Я вообще считаю, что как минимум union types лучше, чем вообще не указывать ничего, если таки нужно вернуть/передать не единственный тип. А такие случаи реальны, их не нужно списывать со счетов, потому что это "говнокод".

                                  0
                                  Согласен. Тем более что вряд ли будет разрешено использовать мульти-объекты: getModel(): Post|Category
                                    0

                                    i80586 думаю, скорее всего будет можно, но я могу ошибаться. В данном конкретном случае, если у Вас больше двух моделей, то лучше использовать какой-то ModelInterface в return-type, ИМХО. Но если именно хотите эти два класса — то ок, почему нет? Вот есть у меня их 3, а я хочу разрешить только 2 для метода. Union types поможет разрешить передачу/возврат только определенных типов данных, и, по факту, отказаться от mixed.

                                      0
                                      Понимаю. Но тогда для IDE будет путаница какой класс использовать в данном случае в качестве подсказчика.
                                    –2
                                    Кажется, я чего-то не понимаю – что делаете без UnionTypes? Вот два примера:

                                    class User
                                    {
                                        public function getFriends() : Collection|User[] { /* ... */ }
                                    }
                                    


                                    class Like
                                    {
                                        public function getTarget() : Post|Comment { /* ... */ }
                                    }
                                    


                                    Контекст примеров: какой-нибудь типичный CRUD на Symfony+Doctrine. Сейчас это решается вообще отсутствием возвращаемого типа и описанием в DocBlock.
                                    Решить, конечно, можно.. но геморроя больше чем пользы.
                                    В первом случае можно решить создав дофига типизированных коллекций, а в геттерах ещё и каждый раз в них оборачивать доктринские (она вставит в аттрибут всё-равно свою коллекцию) – уж не перебор ли?

                                    Во втором можно разделить Like на CommentLike и PostLike, но я пока, видимо, не сталкивался с задачами, где лишние сущности имеют смысл – «общего лайка» было достаточно, да и код копипастить не нужно.


                                    будет помогать писать некачественный код

                                    А PHP и так сейчас помогает писать любой говнокод просто не используя типы. Но это ведь не значит что надо вдруг в обязательном порядке всех заставлять явно их указывать?
                                      +2

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


                                      А PHP и так сейчас помогает писать любой говнокод просто не используя типы.

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

                                        0
                                        Про первый спасибо, видимо, невнимательно прочитал и понял RFC неверно, печально, ждём.
                                        Про второй – догдался сам когда уже написал сообщение. Наличие интерфейса типа `LikeableInterface` подойдёт в качестве нормального кода?

                                        выходят фичи, которые помогают писать хороший код

                                        В таком случае не вижу как UnionTypes «помогут» писать плохой код: его бы и так написали без типов вообще при отсутствии нормального ревью или при соответствующей политике команды. Может, «не предотвратят»?
                                          0

                                          Замаскируют отсутствие типа перечислением типов. Но смысл тот же самый: мы не можем надеяться на какой-то тип.

                                            –1
                                            мы не можем надеяться на какой-то тип.

                                            Как это мы не можем надеяться на какой-то тип? Мы не можем надеяться на mixed. А на тип — вполне себе. Даже можно сказать, что Union Types — это такой себе mixed, но более строгий. Там немного выше я привёл еще один пример. Как бы Вы рефакторили? mixed $uri, или сахар для одного из двух типов? Или может есть другие мысли? Условие одно: метод может принимать аргументом uri только string или UriInterface (так как эти два типа могут быть валидным $uri). Для любого другого типа uri должно быть исключение.
                                            В первом случае — mixed, и это хуже, т.к. придти может вообще что угодно. Плюс надо на одну проверку больше по сравнению с UT (если я принимаю два типа — мне достаточно проверить один, если не он, то второй 100%).
                                            Во втором случае — дополнительный метод. Плюс ко всему надо еще придумать как его назвать, что бы было понятно что это тот же request, только аргументом принимает другой тип.
                                            Возможно Вы предложите решение лучше. Но помните, что надо именно два типа, ни больше, ни меньше.


                                            В случае UT — оно как минимум не вредит коду. Единственный метод, можно передать или строку или UriInterface. И ничего более — остальные типы будут выкидывать исключение TypeError, (что вполне себе логично). Неужели не видно, что это как раз строгая типизация?


                                            Замаскируют отсутствие типа перечислением типов

                                            Понятно, что легально будет написать


                                            use int|string|null|bool|MyClass as mixed;
                                            public function method(mixed $arg): MyClass|false|null
                                            {
                                            }

                                            , что совсем не есть хорошо (да и как минимум всех типов не перечислишь, и уже не всё можно будет передать в метод — будет TypeError). Уверен, те кто знают про strict_types и UT такого городить не будут. А те, кто писали без понятия о strict_types так и продолжат использовать mixed. UT это никак не "маскировка отсутствия". Это как раз таки строгое указание взамен полного отсутствия в определенных случаях.


                                            По поводу примеров TrogWarZ. Для return-types пока что сложно придумать более внятное применение чем : Something|null вместо ?Something (что, внезапно, уже реализованый Union Type в языке)

                                              +1
                                              Как это мы не можем надеяться на какой-то тип?

                                              Мы не сможем надеяться на какой-то тип в функции, использующей union return type. Нам надо будет вокруг полученного значения как-то крутиться и проверять его тип. И это совсем не правильно с точки зрения проектирования.
                                              К ?Something я крайне отрицательно по этой же причине отношусь, но это еще относительно легкий вариант.

                                                –1
                                                Мы не сможем надеяться на какой-то тип в функции, использующей union return type.

                                                Странное заявление. Сможем. Если это string|UriInterface — это только string или UriInterface и никаких других (достаточно проверить только один из них). Если не указывать вообще — то любое и тогда не сможем надеяться и придется проверять все типы. Если же указывать один тип — или отсекаем очевидную возможность (например, id сущности в в базе может быть null до persist, но в целом это практически всегда int), или спагетти-код. Или же вот самый первый пример:


                                                public function decode(array|MyClassInterface $object): string
                                                {
                                                    return json_encode($object);
                                                }

                                                Попрошу заметить, что json_encode никогда не вернет false. Что это как не надежда на типы из UT? Я только указанием типа уже знаю, что результатом метода всегда будет валидная JSON строка.


                                                Нам надо будет вокруг полученного значения как-то крутиться и проверять его тип. И это совсем не правильно с точки зрения проектирования.

                                                Нам и без UT где-то нужно будет в любом случае решить, какой из методов для принимаемого аргумента нужно вызвать. Без UT это mixed в любом случае, который в лучшем случае объявлен в PHPDoc. Кстати, с UT это как минимум на одну проверку типа меньше (я писал об этом в предыдущем сообщении).


                                                К ?Something я крайне отрицательно по этой же причине отношусь, но это еще относительно легкий вариант.

                                                class MyEntity
                                                {
                                                    protected $id;
                                                
                                                    public function __construct()
                                                    {
                                                    }
                                                
                                                    public function getId(): int
                                                    {
                                                        return $this->id;
                                                    }
                                                
                                                    public function setId(int $id): MyEntity
                                                    {
                                                        $this->id = $id;
                                                        return $this;
                                                    }
                                                }
                                                
                                                (new MyEntity())->getId(); # TypeError

                                                Если свойство не установлено и не инициализировано в конструкторе, то оно может быть null (потому что protected $id; эквивалентно protected $id = null;). Варианты решения:


                                                • установить дефолтный int для protected $id или инициализировать дефолтное значение в конструкторе (но у меня его на данных этапах просто нет, он null)
                                                • проверять в методе getId() свойство id и если не int — exception (плохо в конкретном случае, этот метод не должен бросать исключения, если id не установлен. id — null, не int) или без exception возвращать какой-то int (что еще хуже т.к. это по факту не id и он просто не установлен)
                                                • или же не использовать return type int, и не устанавливать никаких типов (читай mixed, хотя по факту id это int|null и никогда ничего более, смотрите сеттер), что крайне вероятно, если не использовать nullable (что, собственно, вообще без типизации).
                                                • или же public function getId(): ?int, что очевидно решает все вышеуказанные проблемы.

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

                                    +1
                                    > [RFC] Object Initializer

                                    Реализация завязана на публичные методы. Первое (и единственное для меня) место где это нужно — в доктрине — не заработает.

                                    Могло бы взлететь, если бы в пыхе были бы public final атрибуты — это когда один раз засетил и потом нельзя менять. Тогда можно все dependency полностью убрать из конструктора и сделать публичными. Что в свою очередь сократит очень много бойлерплейта.
                                      0
                                      Иммутабельность то бишь?
                                        0
                                        Иммутабельность аттрибута.

                                        Иммутабельность, все таки, это когда ничего в обьекте менять нельзя.

                                        Хотя полная иммутабельность и этот синтакстис гораздо вкуснее.
                                      –2
                                      Union Types

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

                                        –1

                                        Кто-то не согласен, что раз на передающей стороне (выход функции) появились новые возможности, то надо и на принимающей (место вызова) сделать средства их обработки? Или просто с тем, что это полезно? Раз в других языках делают, то наверно есть какая-то польза от этого.

                                        +1
                                        Union Types. Первый шаг к дженерикам? Почему бы сразу их не реализовать?
                                          0
                                          Привет, еще из, надеюсь, кому-то полезного: мы делаем PHP-митапы в Самаре 28-го сентября и Ульяновске 19-го октября — приходите или приезжайте, если где-то рядом)

                                          Only users with full accounts can post comments. Log in, please.