PHP 8 в восьми кусочках кода

Автор оригинала: Brent Roose
  • Перевод
В PHP 8 появилось много новых функций, в этом списке мы рассмотрим самые выдающиеся.

Disclaimer: ссылка на эту статью на английском языке уже была в посте последнего PHP дайджеста. Если владеете языком достаточно, возможно, стоит перейти к оригиналу, там очень много сносок на другие англоязычные статьи.

Раз


use \Support\Attributes\ListensTo;

class ProductSubscriber
{
    <<ListensTo(ProductCreated::class)>>
    public function onProductCreated(ProductCreated $event) { /* … */ }

    <<ListensTo(ProductDeleted::class)>>
    public function onProductDeleted(ProductDeleted $event) { /* … */ }
}

Вместо docblock — атрибуты.

Да, я знаю, синтаксис может быть не таким, как вы хотели или надеялись. Возможно, вы предпочли бы @, @:, или docblocks, или что-то еще. Но он будет именно таким. Единственное, что стоит упомянуть о синтаксисе, это то, что все варианты обсуждались, и есть очень веские причины, по которым этот синтаксис был выбран. Вы можете прочитать краткое резюме об этом в RFC, или вы можете прочитать всю дискуссию о RFC в списке внутренних компонентов.



Два


public function foo(Foo|Bar $input): int|float;

public function bar(mixed $input): mixed;

Объединение типов позволяет указывать сразу несколько типов. Также есть новый mixed тип, который представляет несколько типов одновременно.



Три


interface Foo
{
    public function bar(): static;
}

Можно указать static в return type.



Четыре


[JIT]
opcache.jit=5

Встроенный JIT-компилятор.

Что такое JIT?

«JIT» расшифровывается как «just in time» — «в нужный момент». Вы, вероятно, знаете, что PHP является интерпретируемым языком: он не скомпилирован как программа на C, Java или Rust. Вместо этого он переводится в машинный код — то, что понимает процессор — во время выполнения.

«JIT» — это метод, который компилирует части кода во время выполнения так, что вместо этого можно использовать скомпилированную версию.

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

Подробнее об этом уже писали на Хабре



Пять


$triggerError = fn() => throw new MyError();

$foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');

throw может быть использован в выражениях.



Шесть


try {
    // Something goes wrong
} catch (MySpecialException) {
    Log::error("Something went wrong");
}

Можно не указывать переменную исключения, если она вам не нужна.



Семь


public function(
    string $parameterA,
    int $parameterB,
    Foo $objectfoo,
) {
    // …
}

В конце списка параметров можно указать запятую.



Восемь


str_contains('string with lots of words', 'words');

str_starts_with('haystack', 'hay');

str_ends_with('haystack', 'stack');

Новые строковые функции. Я думаю, их название говорит само за себя
Давайте не будем обманывать себя: 8 блоков кода недостаточно для суммирования всех замечательных новых вещей в PHP 8. Итак, давайте просто добавим еще несколько.



Девять


function bar(Stringable $stringable) { /* … */ }

Новый Stringable interface, который автоматически добавляется в классы, которые реализуют метод __toString ().



Десять


$object::class

Вызов ::class сразу из объекта.


AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 34

    0
    А скажите пожалуйста, как обстоят дела с обобщениями в PHP 8?
      +5

      А куда делись остальные 16 "кусочков" (принятых RFC)?


      И это не PHP 8, а master бранч (8.0-dev) т.к. фичефриза ещё не было, а значит функционала будет больше.

        0

        P.S. Прошу прощения, уже 17 "кусочков". Т.к. судя по всему автоинициализация полей через конструктор — тоже проходит голосование =)

        +1

        я обычно вот эти 2 файла читаю, чтобы обобщить сведения по изменениям в версиях
        https://github.com/php/php-src/blob/master/NEWS
        https://github.com/php/php-src/blob/master/UPGRADING

          +1
          Вы, вероятно, знаете, что PHP является интерпретируемым языком: он не скомпилирован как программа на C, Java или Rust.
          Вместо этого он переводится в машинный код — то, что понимает процессор — во время выполнения.

          Не процессор, а виртуальная машина (Zend VM). Если бы машинный код исполнялся CPU, никакого бы JIT не нужно было.
            +3

            Фичи, которые хотелось бы видеть:


            • Асинхронность хотя бы как в JavaScript
            • Дженерики a.k.a. шаблоны
            • Больше возможностей для типизации, например, возможность указать, что аргумент является массивам с элементами такого-то типа
              –1

              Асинхронность есть в в виде reactphp

                –5

                Обычно PHP используют в режиме «процесс рождается, чтобы обработать один запрос». ReactPHP рассчитан на работу в режиме демона. PHP не рассчитан на работу в режиме демона, из-за этого в нём часто возникают утечки памяти там, где в других языках не возникают (например, при циклических ссылках объектов, в том числе ссылке не самого себя). К тому же, ReactPHP это сторонняя библиотека, а не единый стандарт.


                Ещё есть возможность запустить подпроцесс и таким образом получить полную параллельность. Но это не так удобно, как нативная поддержка в языке.

                  0

                  Так tick_handler полностью асинхронный, а yield вообще замена всем async/await, не?

                    0

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


                    Для асинхронности хватило бы и колбэков. Сложность в том, чтобы не блокировать input/output, например, одновременно делать запрос к БД через PDO и отправлять письмо через SwiftMailer.


                    Я могу сделать эти процессы параллельными, реализовав их с нуля, используя разные средства языка. Разработчики библиотек не могут делать свои решения асинхронными, потому что в языке нет единого стандарта. Иногда библиотеки предоставляют асинхронность строго в рамках своего фукнционала, например, Guzzle и ReactPHP.

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

                      while ($ctx->valid()) {
                          if ($ctx->current() === 0xDEAD_BEEF) { 
                              $ctx->send(42);
                          }
                      }

                      Такой? Ну не вижу особых проблем написать такое.


                      Но вообще, имхо, проблема асинхронности в пыхе не в сахаре этом всяком, а в корневых блокирующих функциях IO (в т.ч. классах stdlib, вроде PDO).


                      P.S. А, я слепой. Как раз про это второй абзац комментария. Да-да, всё верно, солидарен.

                    +4

                    Уже много лет нет утечек памяти из-за циклических зависимостей. В 7 версии хорошо обновили GC.

                      +2

                      Я тоже хотел возмутиться и протестил =)


                      Деструктор просто не вызывается при потере контекста такого объекта (с кодом $this->some = $this в конструкторе) из поля зрения (в моём случае — это пустая функция с телом new Some()).


                      Так что допускаю, что он вполне чистится потом вторичным GC (который срабатывает, при завершении работы файла), а не основным (который, всё чистит сразу же, как только пропадает ссылка на переменную) но я бы предложил убедиться на всякий случай. Да и вроде ещё там добавляли другие точки "stop the world"...


                      Я могу сам попробовать провести такие тесты, более полные, и выложить результат, но чуть попозже, вечерком если.

                        +1

                        Специально проверил $this->test = $thisв конструкторе на миллионе итераций внутри функции. Деструктор вызывается, память стабильна. Предполагаю что есть какой-то способ выстрелить в ногу.

                          0

                          Да, действительно. При 10000 объектах (в моём случае) срабатывает штатный GC, подчищающий лишнее.


                          Заголовок спойлера


                          (кликабельно)


                          Тоже самое касается и если по ссылке присвоить.


                          Предполагаю что есть какой-то способ выстрелить в ногу.

                          Были какие-то нюансы вроде при создании анонимок. Но сейчас сходу вспомнить не смогу, возможно NightTiger поможет.


                          Но в любом случае, если обходить GC пыха и самому выделять неконтролируемую память, то да, утечки будут)))


                          while (true) {
                              FFI::new('uint64_t', false);
                          }
                  0
                  Асинхронность хотя бы как в JavaScript
                  yield есть, а экосистема — ну что поделать, не нужна среднестатистическому пхп разработчику асинхронность, вот и не популярно.

                  Дженерики a.k.a. шаблоны
                  Есть в psalm — psalm.dev/docs/annotating_code/templated_annotations

                  Больше возможностей для типизации, например, возможность указать, что аргумент является массивам с элементами такого-то типа
                  Также есть в psalm — psalm.dev/docs/annotating_code/type_syntax/array_types
                  От тайпхинтов самих по себе без каких-либо статических гарантий пользы нет +- никакой
                    0

                    yield и await — это не асинхронность, а синтаксический сахар. От языка нужен неблокирующий IO и единый цикл событий.


                    Статистический анализатор это хорошо, но хотелось бы видеть такой анализ в самом языке.

                  +12

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

                    +3

                    Если вы почитаете чем они это мотивируют, то станет понятно почему — основная причина это не потому что "так лучше и удобнее" а потому что "нам так проще". К сожалению, многие вещи в PHP за прошедшие годы именно по этому принципу и создавались...

                    +2

                    Завезли бы сокеты в SPL, но скорее всего не будет, как пример SPLFileObject

                      0

                      Не знал что в пхп можно указывать тип return. В нем даже стандартные функции возвращают что попало.


                      В своё время недоумевал почему код не работает, пока не узнал что половина функций вертают либо какую-то дату, либо молчаливый скромный false без всяких эксепшенов.

                        0

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

                          0

                          Я столкнулся с пхп и её проблемами на 7й версии. Если в мире пхп это давно, то питон со своей v3 — это вечность.

                        0
                        Пункт 9 про Stringable — явная ошибка дизайна…
                          0

                          Тоже так подумал.
                          Лучше бы явно запретили преобразование объекта в строку, если он не реализует интерфейс Stringable.
                          Который обязывает создать функцию __toString().
                          Но это поломает кучу легаси кода.

                            0
                            Лучше просто считать, что любая сущность конвертируется в строку, как это сделано во многих других языках.
                            +1
                            Ошибка дизайна — это declare(strict_types=1), а Stringable — просто порожденный ею костыль. Первый из многих…
                            +5

                            Тип mixed предназначен для явного указания, что параметр или свойство может быть любого типа. Отличается от НЕуказания типа только своей явностью (я не забыл указать тип, просто хочу обрабатывать здесь любые типы). Mixed включает в себя NULL, указать mixed? (mixed nullable) не получится.


                            Просто сэкономил кому-то минутку гугления ;-)

                              0
                              Может в будущем станет обязательным указание как минимум mixed-а
                                +1
                                Лет через 10 с PHP 15.
                              –2
                              } catch (MySpecialException) {

                              Это для тех кому лень прописать $e? Или что?
                              Зачем тогда оставлять (MySpecialException)?
                                0
                                Потому что catch может быть много?
                                  –1

                                  Мне непонятна выгода от написания: catch (MySpecialException) вместо просто catch () или же catch (MySpecialException $e).
                                  Может подразумевается что может быть N ошибок на которые не следует реагировать и K на которые следует? Ну тоже такое себе решение.

                                    +3
                                    Ну потому что например на определенный эксепшен мы захотим сделать ретрай, на другой — просто проигнорируем, а вот у третьего нам уже нужен будет объект, чтобы его залогировать. Как минимум в C# это есть, используется и не является code smell.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое