The Nullsafe Operator ?->
Call a method or read a property on something that might be null, and get null back instead of a fatal error.
Before PHP 8 you wrote defensive pyramids to walk a chain of maybe-null objects. The nullsafe operator ?-> collapses all of that: if the left side is null, the whole expression short-circuits to null and the rest of the chain is skipped.
Easy example
<?php
$country = $session?->getUser()?->getAddress()?->country;
// if $session is null (or any link is null), $country is null
// no 'method call on null' error, no isset() ladder
var_dump($country);It works for both method calls and property fetches. One caveat: it makes a read safe, but you can't assign through it — $a?->b = 1 is a parse error, which keeps intentions clear.
Example
When to use it
- Safely call getAddress()->getCity() on a user that may not have an address without nested null checks.
- Chain repository method calls in a single expression that returns null if any step produces null.
- Read a nested config object property with ?-> and fall back to a default using ?? at the end of the chain.
More examples
Basic nullsafe chain
?-> returns null when the left side is null instead of throwing a fatal error, making optional chains safe.
<?php
class Address {
public function __construct(public string $city) {}
}
class User {
public ?Address $address = null;
}
$user = new User();
// Without nullsafe: fatal error
// $city = $user->address->city;
$city = $user?->address?->city ?? 'Unknown';
echo $city; // UnknownNullsafe method calls
If getInvoice() returns null, getPdf() is never called; the whole chain short-circuits to null.
<?php
class Order {
public function getInvoice(): ?Invoice { return null; }
}
class Invoice {
public function getPdf(): string { return 'invoice.pdf'; }
}
$order = new Order();
$pdf = $order->getInvoice()?->getPdf() ?? 'No invoice';
echo $pdf; // No invoiceCombining with ?? and ->
?-> combined with ?? provides a complete pattern: try the chain, fall back to a default if anything is null.
<?php
function getUser(?int $id): ?object {
if ($id === null) return null;
return (object)['name' => 'Alice', 'role' => 'admin'];
}
$role = getUser(null)?->role ?? 'guest';
echo $role; // guest
$role = getUser(1)?->role ?? 'guest';
echo $role; // admin
Discussion