Nullsafe Operator
Reading a chain of properties or method calls — $order->customer->address->city
— is convenient right up until one link in that chain might be
null. In real applications, that happens constantly: an order
might not have a customer yet, a customer might not have an
address on file. Before PHP 8.0, safely reading a deep chain like
that meant either wrapping it in a pile of isset() calls or
writing a nested sequence of if statements checking each link
before touching the next one. PHP 8.0's nullsafe operator, ?->,
replaces all of that with a single, readable expression.
The old way: nested null checks
To safely read $order->customer->address->city without risking
a fatal error on a null property, the pre-8.0 approach looked
something like this.
Verbose null-checking before PHP 8.0
<?php
if ($order !== null) {
$customer = $order->customer;
if ($customer !== null) {
$address = $customer->address;
if ($address !== null) {
$city = $address->city;
}
}
}
$city ??= 'Unknown';
echo $city;This works, but the actual logic — "get the city, or fall back to Unknown" — is buried under three levels of nested if statements that exist purely for null-safety, not for anything the reader actually cares about.
The nullsafe operator
?-> replaces -> at any point in a chain. If the value on the
left of ?-> is null, PHP stops evaluating immediately and the
entire expression evaluates to null — none of the rest of the
chain is touched, so no fatal error can occur from trying to read
a property off of null.
The same logic with the nullsafe operator
<?php $city = $order?->customer?->address?->city ?? 'Unknown'; echo $city;
Unknown
One line replaces the entire nested block above, and it reads as close to plain English as PHP syntax gets: "the order's customer's address's city, if all of those exist, otherwise null" — then the ?? operator supplies the fallback.
Short-circuiting the whole chain
The important behavior to understand is that a single null link short-circuits everything after it — PHP does not attempt to read further and does not throw a warning or error. This holds even when a method call is involved, not just property access.
A null anywhere in the chain stops evaluation
<?php
class Address {
public function __construct(public string $city) {}
}
class Customer {
public ?Address $address = null;
}
class Order {
public ?Customer $customer = null;
}
$order = new Order();
// $order->customer is null, so PHP never attempts to read ->address
$city = $order->customer?->address?->city;
var_dump($city);NULL
Notice that only the first ?-> in that chain was strictly
necessary to prevent an error, but every link after a nullable one
should also use ?->, since any of them could be the one that's
actually null depending on the data.
Nullsafe method calls
?-> works identically for method calls — if the object is
null, the method is never invoked, and the whole expression is
null.
Nullsafe method calls
<?php
class Logger {
public function warn(string $message): void {
echo "WARN: {$message}\n";
}
}
class Service {
public ?Logger $logger = null;
}
$service = new Service();
// No error, even though $service->logger is null — warn() is simply never called
$service->logger?->warn('Something happened');
echo "Done\n";Done
Nullsafe operator vs. isset()
isset() and ?-> solve related but different problems. isset()
checks whether a variable or array key exists and is non-null,
and it's still the right tool for arrays and for checking whether
a variable itself is defined. ?-> specifically short-circuits an
object property/method access chain and produces a usable value
(typically combined with ??) in one expression, rather than
just a boolean.
?->replaces->and short-circuits the entire remaining chain the moment any link evaluates tonull.No warning or fatal error is raised when a nullsafe access hits
null— the whole expression simply evaluates tonull.Combine
?->with??to supply a fallback value in a single readable line, replacing nestedisset()/ifchains.?->works for both property access and method calls.?->cannot appear on the left-hand side of an assignment — it is a read-only safety mechanism.