PHP 8.1 выйдет через несколько месяцев и я в восторге от множества нововведений! Хочу поделиться реальным влиянием PHP 8.1 на мой собственный код.

Перечисления

Долгожданный функционал, перечисления скоро будут доступны!

О них можно сказать не так много, кроме того, что я с нетерпением жду, когда мне больше не придётся использовать spatie/enum или myclabs/php-enum. Спасибо за все годы поддержки этих enum-пакетов, но они будут первыми, от которых я откажусь, когда появится PHP 8.1 и когда я изменю это:

/**
 * @method static self draft()
 * @method static self published()
 * @method static self archived()
 */
class StatusEnum extends Enum
{
}

На это:

enum Status
{
    case draft;
    case published;
    case archived;
}

Распаковка массивов со строковыми ключами

Может показаться незначительным, но меня это не раз беспокоило: до PHP 8.1 можно было распаковывать только массивы с числовыми ключами:

$a = [1, 2, 3];
$b = [4, 5, 6];

// Доступно, начиная с PHP 7.4
$new = [...$a, ...$b];

В то время как массивы со строковыми ключами распаковать таким образом было нельзя:

$a = ['a' => 1, 'b' => 2, 'c' => 3];
$b = ['d' => 4, 'e' => 5, 'f' => 6];

$new = [...$a, ...$b]; 

// В этом случае необходимо использовать array_merge
$new = array_merge($a, $b);

Итак, одна из особенностей PHP 8.1, которая облегчит мне жизнь, заключается в том, что теперь можно также распаковывать массивы со строковыми ключами!

$a = ['a' => 1, 'b' => 2, 'c' => 3];
$b = ['d' => 4, 'e' => 5, 'f' => 6];

// :)
$new = [...$a, ...$b];

Свойства класса: инициализаторы и readonly

Ещё одно замечательное улучшение, которое я ждал годами: аргументы по умолчанию в параметрах функции. Представьте, что вы хотите установить класс состояния по умолчанию для объекта BlogData. До PHP 8.1 вам необходимо было объявить его допускающим значение null и установить в конструкторе:

class BlogData
{
    public function __construct(
        public string $title,
        public ?BlogState $state = null,
    ) {
        $this->state ??= new Draft();
    }
}

PHP 8.1 позволяет вызов new непосредственно в определении функции. Это потрясающе:

class BlogData
{
    public function __construct(
        public string $title,
        public BlogState $state = new Draft(),
    ) {
    }
}

Говоря о грандиозных нововведениях, я уже упоминал, что readonly-свойства теперь стали явью?!?

class BlogData
{
    public function __construct(
        public readonly string $title,
        public readonly BlogState $state = new Draft(),
    ) {
    }
}

Да, кстати, не беспокойтесь о клонировании, я о вас позаботился.

First-class callable

Как будто всего этого было недостаточно, теперь также доступен синтаксис First-class callable, который позволяет использовать более чистый код для создания замыканий из вызываемых объектов.

Раньше приходилось писать что-то вроде этого:

$strlen = Closure::fromCallable('strlen');
$callback = Closure::fromCallable([$object, 'method']);

В PHP 8.1 вы можете написать… так:

$strlen = strlen(...);
$callback = $object->method(...);

В PHP 8.1 есть ещё много нового, но это то, что меня волнует больше всего.

Комментарии (42)


  1. OleksiyT
    26.07.2021 15:06
    -5

    Станет не хуже, а чуть лучше? Ну ок.

    Волновать? Ну уж нет, слишком мелко.


  1. marvin255
    26.07.2021 15:39
    +3

    Никогда не понимал преимуществ readonly свойств и указания свойств в конструкторе. Язык уже позволяет реализовать аналог с помощью private + getter. В чем преимущество, кроме сокращения числа строк кода?

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

    public ?BlogState $state = null все же имеет иной синтаксический смысл. Такая запись говорит о том, что автор допускает, что объект может быть не предоставлен и тогда класс будет вести себя иначе. Если же такой объект требуется всегда, то я бы его и запросил явно без альтернативы по умолчанию.


    1. oxidmod
      26.07.2021 15:56
      +1

      Зато можно так
      public LoggerInterface $logger = new NullLogger()

      Раньше это было 3 мусорные строки в конструкторе
      Потом стало чуть лучше
      $this->logger = $logger ?? new NullLogger();

      А теперь стало еще короче и это радует.


      1. Fafhrd
        26.07.2021 16:28
        -1

        можно вообще код не писать, так будет ещё короче :)


        1. oxidmod
          26.07.2021 16:31
          +2

          Ну так то, лучший код тот, который не написан. Чем меньше LoC, тем меньше шансов словить баги.
          Ну и так, зачем писать код, который не несет смысловой нагрузки, если можно его не писать. Вам же не за количество строк платят? Ведь так?


          1. Anamelash
            26.07.2021 16:58

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


            1. oxidmod
              26.07.2021 18:38
              +2

              Конкретно в этом случае, чем стало хуже?


      1. vitiok78
        27.07.2021 05:11
        +1

        Есть еще один жирный плюс этого, который автоматом идет в комплекте: вложенные атрибуты

        #[Assert\All(new Assert\NotNull, new Assert\Length(max: 6))]


    1. toratoda
      26.07.2021 21:33

      Указание свойств в конструкторе мне тоже не по душе (потому что для меня это выглядит не очевидным что это объявлены свойства объекта).

      Скажу в защиту readonly, на сколько я понимаю, ключевое слово постулирует о том что php будет проверять что свойство было заполнено единожды и более изменяться не будет. В то время как в вашем варианте "неродивый" разработчик мог добавить в ваши объекты публичный сеттерт то с readonly это ему не поможет.


      1. marvin255
        26.07.2021 23:33

        А вот тут интересный момент. Если в readonly будет ссылка на объект, то защищена будет только сама ссылка на объект. При этом я спокойно, если я правильно все понял, смогу сделать $object->readonlyObject->property = 10, что, в общем-то, не так хорошо, как хотелось бы.

        Опять же, сеттер он сможет добавить только через наследование, но в PHP и тут есть защита в виде private области видимости, которую наследник не сможет переопределить. В крайнем случае, есть ключевое слово final.

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

        То есть остается совсем небольшой, на мой взгляд, процент случаев, при котором readonly будет удобно и оправданно использовать.


        1. a-tk
          27.07.2021 09:58

          Вы смешали в кучу два различных понятия: заменить целиком (readonly) и изменить содержимое (immutable).


          1. marvin255
            27.07.2021 10:24

            Как раз не смешал, а пытаюсь понять как правильно использовать readonly, рассматривая разные варианты.

            Я вижу только один случай использования: синтаксический сахар для DTO, который позволяет избавиться от геттеров.


            1. a-tk
              27.07.2021 10:26

              Ещё защита от возможности замены значений полей после инициализации на уровне синтаксиса, а не соглашений и внимательности разработчика.

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


    1. vitiok78
      26.07.2021 23:33

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

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

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

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

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


    1. zorn_v
      27.07.2021 21:54
      -1

      и указания свойств в конструкторе

      Ну хз, на таком например можно полэкрана скрола сэкономить (плюс еще полэкрана+ на объявление) :)

      И главное не затупить при копипасте в двух местах, когда еще один сервис надо будет заинжектить. Про God классы лекции не нужны - этот класс делает именно то, для чего был создан. Он не виноват что ему много чего нужно )


      1. a-tk
        28.07.2021 13:35
        -1

        Обилие сервисов какбэ намекает на альтернативную красоту архитектуры.


      1. MuKPo6
        28.07.2021 13:43

        При передаче трех и более аргументов лучше упаковывать их в какой-нибудь отдельный объект.


        1. zorn_v
          28.07.2021 14:00

          А в чем смысл, если напрямую этот класс все равно не создается и вручную не надо ничего передавать ?


          1. a-tk
            28.07.2021 14:25

            Пускай DI разбирается с этим.


          1. BoShurik
            29.07.2021 14:10

            Я бы посмотрел на тесты этого класса


            1. zorn_v
              29.07.2021 15:39

              Ждал этого коммента )

              На самом деле тесты этого класса бессмысленны, потому что основная его работа - взаимодействие с различными, порой неизвестными (задаются пользователями) внешними API и он первый претендент на мок.

              Да и вообще во всем проекте (плагин для nextcloud) половину классов надо замокать для тестов, а значит тесты не особо имеют смысл.

              Максимум что можно протестировать - это то что сервисы самого nextcloud отрабатывают правильно (читай проверять 2+2=4).

              В общем мне проще по старинке - протыкал кнопки и убедился что все работает )


  1. psycho-coder
    26.07.2021 18:23
    +2

    First-class callable что-то вообще не понял, про что тут. Может кто объяснить?


    1. iliazeus
      26.07.2021 22:05

      В PHP, по историческим причинам, не всегда синтаксически просто использовать функции как значения. Например, присваивать их переменным:

      php > $foo = strlen;
      PHP Warning:  Use of undefined constant strlen - assumed 'strlen' (this will throw an Error in a future version of PHP) in php shell code on line 1
      php > echo $foo;
      strlen

      В PHP 8.1 для такого ввели специальный синтаксис.


      1. psycho-coder
        27.07.2021 11:26

        Да это известная штука, однако я им пользовался всегда как

        $func = 'strlen';
        echo $func('ololo');
        // 5

        Поэтому для меня стало удивлением что это еще оборачивают вто что-то дополнительно.

        А по поводу введение, думаю да, теперь проще станет такое делать.


        1. a-tk
          28.07.2021 13:36
          +2

          Автодополнение в IDE, рефакторинги и т.п. чувствуют себя не слишком уверенно со строками.


          1. psycho-coder
            28.07.2021 13:45

            Есть такое


          1. toratoda
            30.07.2021 19:07

            Интересно как иде будет чувствовать себя с

            [$obj, 'doIt']

            Почему бы не ввести сразу какой то синтаксический сахар для имени метода объекта (а за одно и свойства) типа:

            echo $obj->>doIt;
            // doIt


            1. a-tk
              30.07.2021 20:42
              -1

              А со свободными функциями как делать?


              1. toratoda
                02.08.2021 07:13

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

                А если бы внутри вводилась бы какая то псевдо типизация то как бы хорошо это отразилось на активрекорд, когда мы сейчас пишем свойство так:

                $moo->where('foo', 1)

                Можно было писать:

                $moo->where(Moo::class->>foo, 1)

                А внутри была бы проверка псевдотипа:

                public function where(Moo::property, $value)


    1. DAGpro
      26.07.2021 23:32

      Под этот RFC https://wiki.php.net/rfc/partial_function_application первый шаг был.


      1. psycho-coder
        27.07.2021 11:27

        Спасибо, ознакомлюсь


  1. OlegPatron92
    27.07.2021 00:22
    -3

    Весь этот "синтаксический сахаръ" - это конечно хорошо, но когда уже php превратится в альтернативу таких мощных языков как C/C++ для тех кто ничего кроме PHP не знает, для того чтобы на PHP можно было бы писать всё, как "вечно живущие" серверы для игр, так и полноценные десктопные приложения и программы?! (сарказм)


    1. malferov
      27.07.2021 03:19
      +1

      Может, ответить на ваш вопрос поможет расшифровка аббревиатуры PHP? Или все-таки стоит надеяться, что песок будем возить на спортивных автомобилях, а гоняться на самосвалах?


      1. OlegPatron92
        27.07.2021 11:10

        На КАМАЗах уже давно как гоняются, да и на люксовых автомобилях возят картошку на рынок. Почему нет?!

        Мало того PHP уже давно перестал делать то (или только то), для чего изначально создавался.


    1. psycho-coder
      27.07.2021 11:27
      +2

      Никто не мешает вам демонизировать php


    1. SerafimArts
      27.07.2021 19:28
      +1

      Боюсь, вы этот момент чуть проспали. Давно можно.


  1. wolfandman
    27.07.2021 08:40

    Всё правильно. Язык должен эволюционировать, чтобы угождать всем и конкурировать. Молоцы!


    1. godzie
      27.07.2021 10:52
      +1

      Если угождать всем и каждому - получится свалка из конструкций и концепций. В итоге php придет к тому с чего начинал - str_rot13 и вот это вот все.

      Может, конечно, три года в go так влияют, но релизы чуть ли не полностью состоящие из syntax sugar воспринимаются как то искусственно что ли. Как будто: "мы не знали что делать с языком (но делать что то надо) поэтому вот вам гора сахара - будете экономить минуту в день при написании кода". И как то люди забывают, что код надо не только писать, но и читать, и увеличение когнитивной нагрузки при чтении это не то чтобы плюс.


  1. kadirov
    31.07.2021 14:06
    -1

    Мне кажется в место readonly правильнее было бы дать возможность указать типы констант.

    class Foo
    {
        const string bar = 'baz';
    }


    1. oxidmod
      02.08.2021 07:19

      Я могу ошибаться, но классовые константы привязаны к классу, а значит значение будет у всех объектов одинаковое


      1. kadirov
        02.08.2021 09:04
        -1

        В джаве к примеру это реализовано таким способом, что можешь константам не задавать значения, но присвоить значение в конструкторе. Да, наверное для этого в ядре много чего изменить придется, но const и readonly получается почти одно и тоже. По моему не очень хорошо плодить такие вещи


        1. oxidmod
          02.08.2021 12:41
          +1

          Это не одно и тоже, константа это ближе к static readonly.