PHP-Дайджест № 176 (11 – 23 марта 2020)


    Свежая подборка со ссылками на новости и материалы. В выпуске: обновления PHP и Composer 1.10, переносы конференций, 3 новых RFC предложения из PHP Internals, порция полезных инструментов, видео, подкасты и многое другое.

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



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


    • PHP 7.4.4, PHP 7.3.16, PHP 7.2.29 — Секьюрити релизы, всем рекомендуется обновиться.
    • Composer 1.10.0 — Среди прочих изменений, добавлено новое поле «funding» в composer.json, в котором можно указать, как поддержать проект. По умолчанию данные берутся из FUNDING.yml на GitHub. Также добавлена новая команда composer fund, которая показывает информацию о спонсорстве для ваших зависимостей.
    • Перенос мероприятий
      В связи с эпидемией и ограничительными мерами, многие мероприятия перенесены или переходят в онлайн.
      PHP Russia – Конференция перенесена, а 13 мая пройдёт первая PHP Russia Online.
      habr Митапы PHP-сообществ в марте, которые переходят в онлайн или переносятся.
      • Переходят в онлайн: Laracon EU Madrid, Midwest PHP.

    PHP Internals


    • [RFC] str_contains — Предложение принято. Функция str_contains() будет проверять, содержится ли строка в другой строке.
    • [RFC] throw expression — В PHP нельзя бросить исключение из стрелочной функции, потому что throw – это оператор, а не выражение. Предлагается сделать его выражением и тогда возможно будет использование в коротких лямбдах, в тернарных операторах и других конструкциях:
      $callable = fn() => throw new Exception();
      
      $value = $nullableValue ?? throw new InvalidArgumentException();
      
      $value = $falsableValue ?: throw new InvalidArgumentException();
      
      $value = !empty($array)
                  ? reset($array)
                  : throw new InvalidArgumentException();
    • [RFC] Compact Object Property Assignment — Предлагается добавить компактный синтаксис для присвоения значений нескольким свойствам объекта в одном выражении. Идея уже раньше рассматривалась в рамках [RFC] Object Initializer, но не прошла голосование. На этот раз синтаксис весьма странный:
      doTheFoo((new Foo)->[
          a = 1,
          b = 2,
          c = 3,
      ]);
      
      // Эквивалентно:
      
      $myObj = new Foo();
      
      $myObj->a = 1;
      $myObj->b = 2;
      $myObj->c = 3;
      
      doTheFoo($myObj);
    • [RFC] Is Literal Check — Предлагается добавить функцию is_literal(), чтобы разработчики/фреймворки могли быть уверены, что они работают с безопасным значением, то есть созданным из одного или нескольких литералов, опредёленных в PHP, а не сформированных динамически из пользовательского ввода.
      define('TABLE', 'example');
      
      $sql = 'SELECT * FROM ' . TABLE . ' WHERE id = ?';
      
      is_literal($sql); // Returns true
      
      $sql .= ' AND id = ' . mysqli_real_escape_string($db, $_GET['id']);
      
      is_literal($sql); // Returns false
      

      К предложению много вопросов, и реализация нетривиальная. Похоже, шансов мало.
    • [RFC] Write-Once Properties — Судя по ходу голосования за неизменяемые свойства, предложение в текущем виде не преодолеет порог. Одним из основных аргументов против является тот факт, что свойства, объявленные неизменяемыми, нельзя будет изменить даже изнутри класса.
    • Отличный репозиторий с описанием всех непринятых RFC пополнился статьёй о пропуске дефолтных значений при вызове функций.
      Все статьи содержат описания трудностей и прогнозы дальнейшей судьбы.
      Ещё автор добавил статью Understanding RFC attitudes о том, почему вообще какие-то предложения проходят, а какие-то нет, и как участники PHP Internals оценивают RFC.

    Инструменты


    • async-aws/aws — Легковесная и асинхронная альтернатива официальному SDK для AWS.
    • lisachenko/z-engineNightTiger продолжает демонстрировать все новые возможности PHP благодаря FFI.
      В документации пример создания расширения для PHP на PHP, с помощью которого данные можно сохранять в памяти между запросами.
    • Badcow/DNS — Объектное представление записей DNS в PHP, а также парсер и билдер записей.
    • yswery/PHP-DNS-SERVER — И полноценный DNS-сервер, написанный полностью на PHP с использованием предыдущей библиотеки.
    • nikolaposa/rate-limit — Рейт-лимитер общего назначения с бекендом на Redis.
    • Spartaques/phpkafkacore — Библиотека для работы с Kafka.

    Symfony



    Laravel



    Async PHP



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



    Аудио/Видео



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

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

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

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

    Similar posts

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

    More
    Ads

    Comments 21

      +3
      [RFC] Is Literal Check
      На pecl для того же был модуль taint. Идея взята из Перла.
        +1
        спасибо!
          +5
          > Одним из основных аргументов против является тот факт, что свойства, объявленные неизменяемыми, нельзя будет изменить даже изнутри класса.

          Бред. Они не понимаю смысл термина «неизменяемый»?
            +1
            Так-то оно так, но есть мысль, что в таком виде оно не очень отражает реальную потребность. В треде externals.io/message/109097#109107 есть больше информации от Грекаса. Что думаешь на этот счет?
              +3
              В очередной раз столкновение философий. Автор РФЦ хочет создать что-то новое, чего раньше не было в пхп. Он не предлагает обновлять старый код — он предлагает писать новый код. Грекс же хочет синтаксичейкий сахар для существующего кода.

              Очень грубо — это как столкновение ФП и ООП.

              Тут нет правых и не правых. К сожалению, это приведет к блокировки фичи. Потому что люди физически не способны договорится.

              Кажется, я понимаю, чего хочет Грекс. Но, ИМХО, его предложение просто ужастно. Потому что вместо понимания одного ключегового слова — надо понимать пару `readonly public`. Для его целей лучше вводить уже аксесссоры полей. Когда `getProp()` можно использовать как `$this->prop`. Тогда он получает все свои хотелки и обратную совместимость.
                +1
                В очередной раз столкновение философий.

                Не столько философий, сколько целей.
                "once set, only read allowed" vs "private set"


                Собственно оба подхода полезны и не мешают друг другу

            +2
            > PHP reimagined — Прагматичная подборка изменений, которые могли бы сделать PHP лучше.

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

            1) Хотеки finаl. Они разрушают консистентность языка. Либо все делать по умолчанию финал и дать возможность мутировать. Либо наоборот. Держать в голове что по умолчанию финал, а что нет и ловить из-за этого ошбики — явый путь во фрактал плохого дизайна.

            2) Разрешить сужать интерфейс наследования и ввести дженерики. Мало кто понимает, но оба варианта наследования (и расширение интерфейса и сужение интерфейса) — нарушают принцип Лисковой. Наследовать можно только с идентичным интерфейсом. Единственный способ ввести вариативность в наследование — это как раз дженерики.

            Именно поэтому они так сильно нужны. Чтобы наконец-то писать код, который отвечает SOLID. А не для того чтобы аннотации стали короче.

            3) Void by default. Зачем? Кто в реальной жизни использует отличие void от none?

            4) Енам и стракт вместе тянут на мажорный релиз. Во всяком случае, если их делать правильно. И чтобы учить людей ими пользоваться — это тоже как писать на другом языке.

            Остальные хотелки делаются через статический анализ.
              +1

              Ну расширить интерфейс можно же

                0
                Тут очень хороший пример столкновения практики и теории.

                Пример. `InterfaceA {fun do(InterfaceB $b){}}`.

                По теории `ClassA implements InterfaceA {fun do(InterfaceB $b){}}`.
                То есть любой реализатор интерфейса А должен в параметрах принимать любой екземплят, который реализует интерфейс Б.

                На практике.
                `ClassB implements InterfaceB`
                `ClassA implements InterfaceA {fun do(ClassB $b){}}`
                То есть мы сузили интерфейс. Не все реализаторы Б могут быть использованы в классе А.

                Самый типичный пример — интерфейс для ентити из ОРМ. Ты делаешь интерфейс для любой ентити. Но когда его реализуешь — оно работает только с конкретными полями-классами.

                Практическая необходимость в ПХП для сужения интерфейса безусловно есть. Именно потому что нет дженериков. Но! Если дженерики появятся — имхо в ПХП стоит запретить и сужение и расширение наследования.
                  0

                  А в чем проблема расширения интерфейса?


                  `ClassA implements InterfaceA {fun do(object $b){}}`

                  пример принцип Лисков не нарушает, на сколько я его понимаю

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

                      Мне кажется, вы не обратили внимание на мой пример. Там нет отдельной реализации интерфейса Б, он заменен на более общий object, т.о. все клиенты InterfaceA смогут безболезненно использовать InterfaceB в качестве аргумента. В свою очередь клиенты ClassA смогут использовать любой объект

                        0
                        простите, теперь понял.

                        Чтобы показать как расширение нарушает принцип Лисковой — надо заменить интерфейсы на родительские классы.

                        Тут есть проблема с терминологией. Принцип Лисковой имеет отношения к «контрактам». Очень грубо говоря «контракт» — это то что мы пишем в пхпдок. Интерфейсы — это что использует пхп (и многие другие языки) чтобы ввести в язык контракт.

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

                        Интерфейсы можно использовать без контракта. Например, сделать пустой интерфейс, который будет нужен исключительно для isinstance. Или использовать object в интерфейсе. Практически — иногда приходится такое иппользовать. Но теоретически — это говнокод который следует немедленно удалить. Потому что имеет смысл только контракты, а не интерфейсы.

                        Переходя к примеру.

                        То что у меня интерфейсы — классы А1 и Б1. То что классы — А2 и Б2 (теперь обьект).

                        Теперь смотрите. Если в коде есть пример, когда $a1->do(object $c) оно упадет с ошибкой. Но если заменить на $a2 — оно уже не будет падать с ошибкой. В этом и есть нарушение принципа Лисковой. При замене родителя на наследника — код меняет поведение.
                          0
                          Теперь смотрите. Если в коде есть пример, когда $a1->do(object $c) оно упадет с ошибкой. Но если заменить на $a2 — оно уже не будет падать с ошибкой. В этом и есть нарушение принципа Лисковой. При замене родителя на наследника — код меняет поведение.
                          Это из-за realtime типизации PHP рассуждения «упадёт с ошибкой, не упадёт = поведение». В нормальной типизации $parent->do(object $c) невалиден (с т.з. грамматики то же, что и случайный набор символов) и не запустится, а $child->do(object $c) — валиден. Код валиден или невалиден потому, что не подчиняется принципу Лисков, поведение тут не причем.
                          С т.з. LSP расширение нормально, подмена касается только сигнатур и должна быть соблюдаться контравариантность в аргументах. Если учесть, что тут замена тут имеет смысл и дело только в сигнатуре do, по-видимому вызывающий код ожидает именно Parent, и использовать объект как Child с его расширенной сигнатурой нельзя.
                          Дженерики и прочая параметризация тут вторичны.
                            0
                            С точки зрения Лисковой — нет разницы упадет в рантайме или на стат. анализе. Раньше не работало — теперь работает.
                              +1
                              Код не падает на анализе типов, он в этот момент просто невалиден. Он себя никак не ведёт в этот момент. Вы будете сравнивать соответствие LSP случайного набора символов и использования Parent/Child? Вот терминология Liskov substitution principle:
                              Contravariance of method arguments in the subtype.
                              Preconditions cannot be strengthened in a subtype.
                              Сontravariance:
                              — covariant if it preserves the ordering of types (≤), which orders types from more specific to more generic;
                              contravariant if it reverses this ordering;

                              Из определения следует, что на место родителя мы можем подставить любого наследника. Из определения не следует, что свойства наследника должны повторять свойства родителя. У родителя нет свойства (поведения) do(any $c). У него есть свойствоdo(concrete $c). У наследника оно тоже неявно есть по определению типа, но не только оно. LSP — набор правил для того, чтобы тип можно было заменить подтипом.
                              Вы своим примером пытаетесь доказать, что если у родителя нет doAny(), то и наследник не должен иметь, потому что $parent->doAny() не компилируется, а такой же $child->doAny() — компилируется.
                                +1

                                С точки зрения Лисковой такого ограничения нет. Есть ограничение "Раньше работало и дальше должно продолжать работать"

                0
                Спасибо, с удовольствием читаю ваши подборки
                  +1
                  nikolaposa/rate-limit — Рейт-лимитер общего назначения с бекендом на Redis.

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


                  В Yii 3 есть реализация GRCA, предотвращающая пики: https://github.com/yiisoft/yii-web/tree/master/src/RateLimiter


                  Ну и стоит отметить, что если у вас есть контроль над сервером, нагрузку лучше размазывать средствами Nginx. Там хорошая вариация на тему leaky bucket.

                    0

                    GRCA -> GCRA

                    +1
                    PHP reimagined — отличная статья и вместо сахара, типа str_contains в RFC было бы приятней увидеть структуры. Тем более, что это фактически частный случай классов.

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