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)
marvin255
26.07.2021 15:39+3Никогда не понимал преимуществ readonly свойств и указания свойств в конструкторе. Язык уже позволяет реализовать аналог с помощью private + getter. В чем преимущество, кроме сокращения числа строк кода?
Я смотрю на свой проект, в нем около 50 объектов, которые были написаны год-два назад. При новом синтаксисе я бы выиграл часа три при их создании в рамках всей двухлетней истории проекта. При этом в интерфейс эти свойства не добавить, в наследовании не переопределить. Разве что это поможет случайно не изменить значение атрибута примитивного типа внутри методов самого класса, но насколько это вообще нужно, если учесть, что большая часть таких ошибок все равно ловится тестами.
public ?BlogState $state = null
все же имеет иной синтаксический смысл. Такая запись говорит о том, что автор допускает, что объект может быть не предоставлен и тогда класс будет вести себя иначе. Если же такой объект требуется всегда, то я бы его и запросил явно без альтернативы по умолчанию.oxidmod
26.07.2021 15:56+1Зато можно так
public LoggerInterface $logger = new NullLogger()
Раньше это было 3 мусорные строки в конструкторе
Потом стало чуть лучше$this->logger = $logger ?? new NullLogger();
А теперь стало еще короче и это радует.Fafhrd
26.07.2021 16:28-1можно вообще код не писать, так будет ещё короче :)
oxidmod
26.07.2021 16:31+2Ну так то, лучший код тот, который не написан. Чем меньше LoC, тем меньше шансов словить баги.
Ну и так, зачем писать код, который не несет смысловой нагрузки, если можно его не писать. Вам же не за количество строк платят? Ведь так?
vitiok78
27.07.2021 05:11+1Есть еще один жирный плюс этого, который автоматом идет в комплекте: вложенные атрибуты
#[Assert\All(new Assert\NotNull, new Assert\Length(max: 6))]
toratoda
26.07.2021 21:33Указание свойств в конструкторе мне тоже не по душе (потому что для меня это выглядит не очевидным что это объявлены свойства объекта).
Скажу в защиту readonly, на сколько я понимаю, ключевое слово постулирует о том что php будет проверять что свойство было заполнено единожды и более изменяться не будет. В то время как в вашем варианте "неродивый" разработчик мог добавить в ваши объекты публичный сеттерт то с readonly это ему не поможет.
marvin255
26.07.2021 23:33А вот тут интересный момент. Если в readonly будет ссылка на объект, то защищена будет только сама ссылка на объект. При этом я спокойно, если я правильно все понял, смогу сделать
$object->readonlyObject->property = 10
, что, в общем-то, не так хорошо, как хотелось бы.Опять же, сеттер он сможет добавить только через наследование, но в PHP и тут есть защита в виде private области видимости, которую наследник не сможет переопределить. В крайнем случае, есть ключевое слово final.
При этом, если я хочу защитить атрибут с примитивным типом, то мне, вероятно, стоит подумать про константу (у которых, кстати, тоже есть области видимости).
То есть остается совсем небольшой, на мой взгляд, процент случаев, при котором readonly будет удобно и оправданно использовать.
a-tk
27.07.2021 09:58Вы смешали в кучу два различных понятия: заменить целиком (readonly) и изменить содержимое (immutable).
marvin255
27.07.2021 10:24Как раз не смешал, а пытаюсь понять как правильно использовать readonly, рассматривая разные варианты.
Я вижу только один случай использования: синтаксический сахар для DTO, который позволяет избавиться от геттеров.
a-tk
27.07.2021 10:26Ещё защита от возможности замены значений полей после инициализации на уровне синтаксиса, а не соглашений и внимательности разработчика.
Возможно также, что JIT сможет на основании этого генерировать более оптимальный код, но это не точно.
vitiok78
26.07.2021 23:33Весь вот этот вот церемониальный мусор в конструкторе, который был у нас до PHP 8 - это крайне раздражающий фактор лично для меня, потому что надо было в трёх местах писать абсолютно идиотский и никому не нужный инициализационный код.
Эти секунды, которые нужно тратить на пояски с инициализационным бубном, складываются в весьма неплохой кусок времени к концу дня.
Получается, что не язык обслуживает наши интересы, а мы обслуживаем язык.
Программист - это не человек, который знает языки программирования, а человек, который решает бизнес-задачи. Устранение бестолковых церемоний, мешающих этому, можно только приветствовать.
Подумайте, почему мы используем фреймворки? Для того, чтобы не писать заново повторяющийся код, который всегда будет одним и тем же.
zorn_v
27.07.2021 21:54-1и указания свойств в конструкторе
Ну хз, на таком например можно полэкрана скрола сэкономить (плюс еще полэкрана+ на объявление) :)
И главное не затупить при копипасте в двух местах, когда еще один сервис надо будет заинжектить. Про God классы лекции не нужны - этот класс делает именно то, для чего был создан. Он не виноват что ему много чего нужно )
MuKPo6
28.07.2021 13:43При передаче трех и более аргументов лучше упаковывать их в какой-нибудь отдельный объект.
zorn_v
28.07.2021 14:00А в чем смысл, если напрямую этот класс все равно не создается и вручную не надо ничего передавать ?
BoShurik
29.07.2021 14:10Я бы посмотрел на тесты этого класса
zorn_v
29.07.2021 15:39Ждал этого коммента )
На самом деле тесты этого класса бессмысленны, потому что основная его работа - взаимодействие с различными, порой неизвестными (задаются пользователями) внешними API и он первый претендент на мок.
Да и вообще во всем проекте (плагин для nextcloud) половину классов надо замокать для тестов, а значит тесты не особо имеют смысл.
Максимум что можно протестировать - это то что сервисы самого nextcloud отрабатывают правильно (читай проверять 2+2=4).
В общем мне проще по старинке - протыкал кнопки и убедился что все работает )
psycho-coder
26.07.2021 18:23+2First-class callable что-то вообще не понял, про что тут. Может кто объяснить?
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 для такого ввели специальный синтаксис.
psycho-coder
27.07.2021 11:26Да это известная штука, однако я им пользовался всегда как
$func = 'strlen'; echo $func('ololo'); // 5
Поэтому для меня стало удивлением что это еще оборачивают вто что-то дополнительно.
А по поводу введение, думаю да, теперь проще станет такое делать.
a-tk
28.07.2021 13:36+2Автодополнение в IDE, рефакторинги и т.п. чувствуют себя не слишком уверенно со строками.
toratoda
30.07.2021 19:07Интересно как иде будет чувствовать себя с
[$obj, 'doIt']
Почему бы не ввести сразу какой то синтаксический сахар для имени метода объекта (а за одно и свойства) типа:
echo $obj->>doIt; // doIt
a-tk
30.07.2021 20:42-1А со свободными функциями как делать?
toratoda
02.08.2021 07:13Можно придумать, главное что у нас появится способ указать для иде что мы сейчас хотим написать и на после последней > она будет подсказывать методы.
А если бы внутри вводилась бы какая то псевдо типизация то как бы хорошо это отразилось на активрекорд, когда мы сейчас пишем свойство так:
$moo->where('foo', 1)
Можно было писать:
$moo->where(Moo::class->>foo, 1)
А внутри была бы проверка псевдотипа:
public function where(Moo::property, $value)
DAGpro
26.07.2021 23:32Под этот RFC https://wiki.php.net/rfc/partial_function_application первый шаг был.
OlegPatron92
27.07.2021 00:22-3Весь этот "синтаксический сахаръ" - это конечно хорошо, но когда уже php превратится в альтернативу таких мощных языков как C/C++ для тех кто ничего кроме PHP не знает, для того чтобы на PHP можно было бы писать всё, как "вечно живущие" серверы для игр, так и полноценные десктопные приложения и программы?! (сарказм)
malferov
27.07.2021 03:19+1Может, ответить на ваш вопрос поможет расшифровка аббревиатуры PHP? Или все-таки стоит надеяться, что песок будем возить на спортивных автомобилях, а гоняться на самосвалах?
OlegPatron92
27.07.2021 11:10На КАМАЗах уже давно как гоняются, да и на люксовых автомобилях возят картошку на рынок. Почему нет?!
Мало того PHP уже давно перестал делать то (или только то), для чего изначально создавался.
wolfandman
27.07.2021 08:40Всё правильно. Язык должен эволюционировать, чтобы угождать всем и конкурировать. Молоцы!
godzie
27.07.2021 10:52+1Если угождать всем и каждому - получится свалка из конструкций и концепций. В итоге php придет к тому с чего начинал - str_rot13 и вот это вот все.
Может, конечно, три года в go так влияют, но релизы чуть ли не полностью состоящие из syntax sugar воспринимаются как то искусственно что ли. Как будто: "мы не знали что делать с языком (но делать что то надо) поэтому вот вам гора сахара - будете экономить минуту в день при написании кода". И как то люди забывают, что код надо не только писать, но и читать, и увеличение когнитивной нагрузки при чтении это не то чтобы плюс.
kadirov
31.07.2021 14:06-1Мне кажется в место readonly правильнее было бы дать возможность указать типы констант.
class Foo { const string bar = 'baz'; }
oxidmod
02.08.2021 07:19Я могу ошибаться, но классовые константы привязаны к классу, а значит значение будет у всех объектов одинаковое
kadirov
02.08.2021 09:04-1В джаве к примеру это реализовано таким способом, что можешь константам не задавать значения, но присвоить значение в конструкторе. Да, наверное для этого в ядре много чего изменить придется, но const и readonly получается почти одно и тоже. По моему не очень хорошо плодить такие вещи
OleksiyT
Станет не хуже, а чуть лучше? Ну ок.
Волновать? Ну уж нет, слишком мелко.