PHP 8.0: Null-safe operator
Null-safe operator is a new syntax in PHP 8.0, that provides optional chaining feature to PHP.
The null-safe operator allows reading the value of property and method return value chaining, where the null-safe operator short-circuits the retrieval if the value is null
, without causing any errors.
The syntax is similar to the property/method access operator (->
), and following the nullable type pattern, the null-safe operator is ?->
.
$foo?->bar?->baz;
Null safe operator silently returns null
if the expression to the left side evaluates to null
.
class Customer {
public function getAddress(): ?Address {}
}
class Address {
public function getCountry(): string {}
}
$country = $customer->getAddress()->getCountry();
In the snippet above, the Customer::getAddress()
method return value is nullable; It can return a null
value, or an object of Address
class.
The $customer->getAddress()->getCountry()
chain is not "null safe", because the return value of getAddress
can be null
, and PHP throws an error when it tries to call getCountry()
method:
Fatal error: Uncaught Error: Call to a member function getCountry() on null in ...:...
To safely access the address, it was necessary to check the null
return values before further accessing the return value.
$address = $customer->getAddress();
$country = $address ? $address->getCountry() : null;
$address = $customer->getAddress();
if ($address) {
$country = $address->getCountry();
}
else {
$country = null;
}
The null-safe operator solves this by short-circuiting the property/method access, and returning null
immediately if the left side of the operator is null
, without executing the rest of the expression.
- $address = $customer->getAddress();
- $country = $address ? $address->getCountry() : null;
+ $country = $customer->getAddress()?->getCountry();
The ?->
null-safe operator can help reduce excessive isset()
and ternary checks.
Null-safe operator was added to PHP 8.0 between alpha 3 release and first beta release, right around the time PHP 8.0 reached its feature-freeze.
Read-Only
The null-safe operator is read-only. It cannot write/assign values from it.
class Customer {
private ?Address $address;
public function getAddress(): ?Address {
return $this->address;
}
}
class Address {
public string $country;
}
$customer->getAddress()?->country = 'NL';
This snippet attempts to write to the $country
property of the Address
object. This is not allowed with the null-safe operator.
Fatal error: Can't use nullsafe operator in write context in ... on line ...
Chaining
The null-safe operator can be chained, and the expression as a whole will be short-circuited from the first null-safe operator that encounters null.
$customer->getAddress()?->getCoordinates()->getLongitude()->format();
This chain of calls are null-safe. PHP will stop and immediately return null
if any of the null-safe return operators evaluate to null
.
{code-ok} Null-safe operator must be present at every step the chain can be short-circuited. Using the
?->
operator will not make the whole chain null-safe.
Evaluated from left to right
The null-safe operator is evaluated from left to right. Most importantly, it does not prioritize other function calls or other access patterns such as array-access.
<?php
class Customer {
public function getAddress(): ?Address {}
}
class Address {
public function setCountry(string $country_code): void {}
}
$customer->getAddress()?->setCountry(GeoIP::getCountry());
If the Customer::getAddress
method returns a valid Address
object, the GeoIP::getCountry()
will be executed, and passed to the Address::setCountry()
method call. Because the null-safe operators are evaluated from left to right, the GetIP::getCountry()
method will never be called if the Customer::getAddress()
method returns null
.
Note that using braces will make the expression inside the braces execute altogether, as opposed to the left-to-write pattern.
$customer->getAddress()?->setCountry((GeoIP::getAddress()?->getCountry()))
If Customer::getAddress()
returns a value other than null
, the (GeoIP::getAddress()?->getCountry())
chain will be executed, and the return value will be passed to the Address::setCountry()
call.
No References
The null-safe operator cannot be used with references.
$country = &$customer->getAddress()?->getCountry();
Fatal error: Cannot take reference of a nullsafe chain in ... on line ...
Backwards Compatibility Impact
Null-safe operator is a new syntax, and cannot be back-ported to older PHP versions.
Running code that uses null-safe operator will raise a parse error:
Parse error: syntax error, unexpected '->' (T_OBJECT_OPERATOR) in ... on line ...