Если вы уже использовали оператор объединения с null (null coalescing operator), вы, вероятно, замечали его недостатки: он не работает с вызовами методов. Вместо этого приходится делать промежуточные проверки или полагаться на хелперы вроде optional , которые есть в некоторых фреймворках:
$startDate = $booking->getStartDate(); $dateAsString = $startDate ? $startDate->asDateTimeString() : null;
Оператор nullsafe предоставляет схожую с null coalescing функциональность, но при этом поддерживает вызовы методов. Вместо того чтобы писать так:
$country = null;
if ($session !== null) {
$user = $session->user;
if ($user !== null) {
$address = $user->getAddress();
if ($address !== null) {
$country = $address->country;
}
}
}В PHP 8 можно написать следующее:
$country = $session?->user?->getAddress()?->country;
Давайте посмотрим, на что способен этот новый оператор, а на что — нет!
Оператор nullsafe в деталях
Давайте начнём с самого главного вопроса: в чём же именно разница между оператором объединения с null и оператором nullsafe?
Взглянем на этот пример:
class Order
{
public ?Invoice $invoice = null;
}
$order = new Order();Здесь у нас есть объект Order , который имеет опциональную связь с объектом Invoice . Теперь представьте, что мы хотим получить номер счёта (если он не null). Это можно сделать как с помощью оператора объединения с null, так и с помощью оператора nullsafe:
var_dump($order->invoice?->number); var_dump($order->invoice->number ?? null);
Так в чём же разница? Хотя в этом примере оба оператора дают одинаковый результат, у каждого из них есть свои сценарии использования. Например, оператор объединения с null можно использовать с ключами массивов, тогда как оператор nullsafe с ними работать не умеет:
$array = []; var_dump($array['key']->foo ?? null);var_dump($array['key']?->foo); Warning: Undefined array key "key"
С другой стороны, оператор nullsafe может работать с вызовами методов, в отличие от оператора объединения с null. Представьте себе такой объект Invoice :
class Invoice
{
public function getDate(): ?DateTime { /* … */ }
// …
}
$invoice = new Invoice();Вы можете использовать оператор nullsafe, чтобы вызвать format для даты счёта, даже если она равна null :
var_dump($invoice->getDate()?->format('Y-m-d'));
// nullТогда как оператор объединения с null завершится ошибкой:
var_dump($invoice->getDate()->format('Y-m-d') ?? null);
Fatal error: Uncaught Error: Call to a member function format() on null
Короткое замыкание (short-circuiting)
Иногда можно использовать любой из двух операторов, а иногда подойдёт только один конкретный. Разница в том, что оператор nullsafe использует форму короткого замыкания (short-circuiting): при записи ?-> PHP проверяет левую часть выражения, и если она равна null , правая часть попросту отбрасывается. Оператор объединения с null на самом деле представляет собой замаскированный вызов isset для левого операнда и не поддерживает короткое замыкание.
Короткое замыкание также означает, что при записи такого вида:
$foo?->bar(expensive_function());
expensive_function выполнится только в том случае, если $foo действительно не равен null .
Вложенные операторы nullsafe
Вызовы с оператором nullsafe можно выстраивать в цепочки:
$foo?->bar?->baz()?->boo?->baa();
Только для чтения данных
Оператор nullsafe нельзя использовать для записи данных в объекты:
$offer?->invoice?->date = new DateTime();
Оператор nullsafe — это определённо недостающий элемент пазла, который наконец-то появился в PHP. Учитывая динамическую природу языка, приятно иметь удобный способ работы с null . Разница и пересечения между оператором nullsafe и оператором объединения с null поначалу могут сбивать с толку, но я уверен, что мы к этому быстро привыкнем.
Комментарии (0)
Пока нет комментариев — будьте первым.