Nullsafe Operator

Ma Mariya Vinciya Updated 21 Oct 2026
5 min read ·Lesson 166 of 174
In this post

What Is the Nullsafe Operator? In simple terms: When you follow a chain of objects — an order's customer's address's city — any link in the chain might be missing. Normally, calling a method or readin

What Is the Nullsafe Operator?

In simple terms: When you follow a chain of objects — an order's customer's address's city — any link in the chain might be missing. Normally, calling a method or reading a property on null causes an error, so you must check every step. The nullsafe operator ?-> does the checking for you: if the value on its left is null, the whole chain simply results in null.

Technically: The nullsafe operator (PHP 8.0+) works like the object operator ->, but if the left-hand side evaluates to null, it short-circuits: the rest of the chain is skipped — including method calls and the evaluation of their arguments — and the expression evaluates to null. It can be used for reading properties and calling methods, not for writing.

Before and After

// Before PHP 8.0
$city = null;
if ($order !== null) {
    $customer = $order->getCustomer();
    if ($customer !== null) {
        $address = $customer->getAddress();
        if ($address !== null) {
            $city = $address->city;
        }
    }
}

// PHP 8.0+
$city = $order?->getCustomer()?->getAddress()?->city;

 


A First Example

<?php
final class Address
{
    public function __construct(public string $city) {}
}

final class Customer
{
    public function __construct(public string $name, private ?Address $address = null) {}

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

final class Order
{
    public function __construct(public int $id, private ?Customer $customer = null) {}

    public function getCustomer(): ?Customer
    {
        return $this->customer;
    }
}

$orders = [
    new Order(501, new Customer('Asha', new Address('Chennai'))),
    new Order(502, new Customer('Bala')),
    new Order(503),
];

foreach ($orders as $order) {
    $city = $order->getCustomer()?->getAddress()?->city;
    echo "Order {$order->id}: " . ($city ?? 'no city on file') . "\n";
}

 

Output

Order 501: Chennai
Order 502: no city on file
Order 503: no city on file

 

How It Works

  1. Order 501 has a customer with an address, so the full chain runs and returns Chennai.
  2. Order 502's customer has no address: getAddress() returns null, so ?->city short-circuits to null.
  3. Order 503 has no customer: getCustomer() returns null, so everything after it is skipped.
  4. The null coalescing operator ?? turns null into a readable fallback text.

Short-Circuiting Skips the Entire Chain

When ?-> finds null, it skips all remaining parts of the chain — even those written with the normal -> operator — and does not evaluate method arguments:

<?php
function expensiveLookup(): string
{
    echo "expensiveLookup() was called\n";
    return 'data';
}

final class Report
{
    public function render(string $data): self
    {
        echo "Rendering $data\n";
        return $this;
    }

    public function save(): string
    {
        return 'saved';
    }
}

$report = null;
var_dump($report?->render(expensiveLookup())->save());

$report = new Report();
var_dump($report?->render(expensiveLookup())->save());

 

Output

NULL
expensiveLookup() was called
Rendering data
string(5) "saved"

 

  • With $report = null, the whole expression ->render(expensiveLookup())->save() is skipped. expensiveLookup() is never called, and ->save() does not cause an error even though it uses the normal -> operator.
  • With a real object, the chain runs normally.

This means you only need ?-> at the points where null can actually occur; the rest of the chain is protected automatically.


?-> vs ??

The two operators are often used together, but they do different things:

OperatorChecksUse For
?->Whether the object on the left is nullContinuing a chain only if the object exists
??Whether the value on the left is null or undefinedProviding a default value

 

$name = $user?->profile?->displayName ?? 'Guest';

 

?-> safely navigates the chain; ?? supplies the fallback.

There is one more difference: ?? also suppresses warnings for undefined variables, array keys, and properties. ?-> does not: it only reacts to null values. Accessing an undefined variable with $undefined?->name still produces an "Undefined variable" warning.


Where ?-> Cannot Be Used

ContextExampleResult
Assignment$order?->status = 'paid';Compile error: "Can't use nullsafe operator in write context"
unset()unset($order?->notes);Compile error
References$ref = &$order?->items;Compile error
Static callsClassName?::method()Not valid syntax

 

Writing to a property of an object that may be null is a decision your code must make explicitly:

if ($order !== null) {
    $order->markAsPaid();
}

 


Practical Example: Building a Delivery Label

<?php
final class Address
{
    public function __construct(
        public string $line1,
        public string $city,
        public ?string $pincode = null,
    ) {}
}

final class Customer
{
    public function __construct(public string $name, public ?string $phone = null, public ?Address $address = null) {}
}

function deliveryLabel(?Customer $customer): string
{
    $name = $customer?->name ?? 'UNKNOWN RECIPIENT';
    $line1 = $customer?->address?->line1 ?? '(address missing)';
    $city = $customer?->address?->city ?? '';
    $pincode = $customer?->address?->pincode ?? '------';
    $phone = $customer?->phone ?? 'no phone';

    return sprintf("%s\n%s\n%s %s\nPhone: %s", $name, $line1, $city, $pincode, $phone);
}

echo deliveryLabel(new Customer('Asha', '98765 43210', new Address('12 Lake Road', 'Chennai', '600041'))) . "\n\n";
echo deliveryLabel(new Customer('Bala')) . "\n";

 

Output

Asha
12 Lake Road
Chennai 600041
Phone: 98765 43210

Bala
(address missing)
 ------
Phone: no phone

 

The label function handles missing customers, addresses, pincodes, and phone numbers without a single if statement. Each value falls back to a placeholder that warehouse staff will notice.

(In a real application, an order without a delivery address should not reach the label printer at all — validate required data earlier, and use ?-> for genuinely optional information.)


Common Mistakes

Overusing ?->

Effect: Writing ?-> everywhere hides bugs where a value should never be null.

Fix: Use it only where null is a legitimate, expected state; let errors surface elsewhere.

Expecting ?-> to Handle Undefined Values

Effect: Warnings for undefined variables or properties.

Fix: Ensure variables exist; use ?? for possibly undefined array keys.

Trying to Assign Through ?->

Effect: Compile error.

Fix: Check for null explicitly before writing.

Forgetting the Fallback

Effect: null flows further into the code (for example, printed as an empty string).

Fix: Combine with ?? to provide meaningful defaults.


Best Practices

  • Use ?-> to navigate chains of optional objects concisely.
  • Place it only at the links that can actually be null.
  • Combine it with ?? to provide defaults.
  • Keep write operations explicit with if checks.
  • Prefer designs that avoid null where possible (required data, Null Objects).

Official reference: https\://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.nullsafe

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else User-defined Functions Slicing Strings PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning MySQL Database MySQL Connect MySQL Create DB MySQL Create Table MySQL Insert Data MySQL Get Last ID MySQL Insert Multiple MySQL Prepared MySQL Select Data MySQL Where Debugging PHP Project: Image Upload Gallery $_SESSION $_ENV PHP RegEx RegEx Patterns Heredoc and Nowdoc RegEx Modifiers preg_match preg_replace preg_split PHP RegEx Functions Nullable Types Mixed and Never Types Nullsafe Operator PHP Attributes PHP Fibers Readonly Classes Property Hooks (PHP 8.4) Asymmetric Visibility (PHP 8.4) Reflection API SPL Data Structures Composer
Ask about this post
AI Ask about this post

Ask questions about Nullsafe Operator and get answers drawn from it.

Signed-in readers only.