Nullsafe-оператор — это новый синтаксис в PHP 8.0, добавляющий в PHP возможность опциональной цепочки вызовов (optional chaining).

Nullsafe-оператор позволяет  читать значения свойств и  выстраивать цепочки возвращаемых методов значений, при этом он  прерывает вычисление выражения (short-circuiting), если значение равно  null ,  не вызывая при этом никаких ошибок. 

Синтаксис похож на оператор доступа к свойствам/методам ( -> ), и по аналогии с nullable-типами nullsafe-оператор записывается как  ?-> . 

$foo?->bar?->baz; 

Nullsafe-оператор  тихо возвращает  null , если выражение в левой части вычисляется как  null . 

class Customer {
    public function getAddress(): ?Address {}
}
class Address {
    public function getCountry(): string {}
}

$country = $customer->getAddress()->getCountry(); 

В приведенном выше фрагменте метод  Customer::getAddress()  может возвращать null; он возвращает значение  null  или объект класса  Address . 

Цепочка  $customer->getAddress()->getCountry()  не является безопасной к null, поскольку возвращаемое значение  getAddress  может быть  null , и PHP выдаст ошибку при попытке вызвать метод  getCountry() :

Fatal error: Uncaught Error: Call to a member function getCountry() on null in ...:... 

Чтобы безопасно получить адрес, раньше приходилось проверять возвращаемые значения  null  перед дальнейшим обращением к ним. 

$address = $customer->getAddress();
$country = $address ? $address->getCountry() : null; $address = $customer->getAddress();
if ($address) {
    $country = $address->getCountry();
}
else {
    $country = null;
} 

Nullsafe-оператор решает эту проблему путем  краткого вычисления (short-circuiting) доступа к свойству/методу, немедленно возвращая  null , если выражение слева от оператора равно  null , и не выполняя оставшуюся часть выражения. 

- $address = $customer->getAddress();
- $country = $address ? $address->getCountry() : null;
+ $country = $customer->getAddress()?->getCountry();  

Nullsafe-оператор  ?->  помогает избавиться от избыточных проверок с помощью  isset()  и тернарного оператора.

Nullsafe-оператор был добавлен в PHP 8.0 в промежутке между релизом alpha 3 и первой бета-версией, как раз тогда, когда PHP 8.0 достиг стадии заморозки функционала (feature freeze).

Только для чтения 

Nullsafe-оператор предназначен только для чтения. С его помощью нельзя записывать или присваивать значения. 

class Customer {
    private ?Address $address;

    public function getAddress(): ?Address {
        return $this->address;
    }
}

class Address {
    public string $country;
}

$customer->getAddress()?->country = 'NL'; 

В этом примере делается попытка записи в свойство  $country  объекта  Address . При использовании nullsafe-оператора это запрещено. 

Fatal error: Can't use nullsafe operator in write context in ... on line ... 

Цепочки вызовов 

Nullsafe-операторы можно объединять в цепочки, и выполнение всего выражения будет прервано на первом же nullsafe-операторе, который встретит null. 

$customer->getAddress()?->getCoordinates()->getLongitude()->format(); 

Такая цепочка вызовов безопасна к null. PHP прервет выполнение и немедленно вернет  null , если какой-либо из nullsafe-операторов вернет  null .

{code-ok} Nullsafe-оператор  должен присутствовать на каждом шаге, где цепочка может быть прервана. Использование оператора  ?->  не делает всю цепочку безопасной к null автоматически.

Вычисление слева направо 

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

<?php

class Customer {
    public function getAddress(): ?Address {}
}

class Address {
    public function setCountry(string $country_code): void {}
}

$customer->getAddress()?->setCountry(GeoIP::getCountry()); 

Если метод  Customer::getAddress  возвращает валидный объект  Address , будет выполнена функция  GeoIP::getCountry() , а ее результат будет передан в вызов метода  Address::setCountry() . Поскольку nullsafe-операторы вычисляются слева направо, метод  GetIP::getCountry()  никогда не будет вызван, если метод  Customer::getAddress()  вернет  null . 

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

$customer->getAddress()?->setCountry((GeoIP::getAddress()?->getCountry())) 

Если  Customer::getAddress()  возвращает значение, отличное от  null , цепочка  (GeoIP::getAddress()?->getCountry())  будет выполнена, а возвращаемое значение будет передано в вызов  Address::setCountry() .

Без ссылок 

Nullsafe-оператор нельзя использовать со ссылками. 

$country = &$customer->getAddress()?->getCountry(); Fatal error: Cannot take reference of a nullsafe chain in ... on line ... 

Влияние на обратную совместимость

Nullsafe-оператор представляет собой новый синтаксис и не может быть портирован на старые версии PHP. 

Запуск кода, использующего nullsafe-оператор, приведет к ошибке синтаксического анализа (parse error):

Parse error: syntax error, unexpected '->' (T_OBJECT_OPERATOR) in ... on line ... 

RFC Обсуждение Реализация