Null & Null-Safe Operators

Reviewed & published by Brayan K

Missing data is everywhere — absent array keys, unset properties, optional objects. PHP gives you three sharp tools for handling null gracefully: ?? for defaults, ??= for lazy assignment, and ?-> for safe object chains.

Part of the free PHP course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ What null Really Is

null is the type that means "no value". A variable is null when you assign null, when a function returns nothing, or when you read an array key or property that doesn't exist. Test for it with === null or is_null() — and don't confuse "is null" with "is falsy": 0 and "" are falsy but are not null.

<?php
// null means "no value at all". A variable is null when:
$a = null;                 // 1) explicitly assigned null

$arr = ['name' => 'Ada'];
// $arr['email'] is unset -> reading it warns AND yields null

// is_null() and === null both test for null.
var_dump(is_null($a));     // bool(true)
var_dump($a === null);     // bool(true)

// null is falsy, but DON'T confuse "is null" with "is falsy".
var_dump($a == false);     // bool(true)  — loose: both falsy
var_dump(0 === null);      // bool(false) — 0 is NOT null
var_dump("" === null);     // bool(false) — "" is NOT null
?>

2️⃣ The ?? Operator

The null coalescing operator ?? returns the left side unless it's null or undefined, in which case it returns the right side — and it never emits an "undefined key" warning. It also chains, walking left to right until it finds a non-null value.

<?php
// ?? — the null coalescing operator: "use the left side, but if it's
// null OR undefined, use the right side instead". No warning, ever.

$config = ['theme' => 'dark'];

$theme = $config['theme'] ?? 'light';   // 'dark'  (key exists)
$lang  = $config['lang']  ?? 'en';      // 'en'    (key missing, no warning)

echo "Theme: $theme\n";   // Theme: dark
echo "Lang: $lang\n";     // Lang: en

// Crucial difference from isset()-less access: ?? is null-AND-undefined safe.
// It also chains left to right, stopping at the first non-null value.
$pick = null ?? null ?? 'third' ?? 'fourth';
echo "$pick\n";           // third
?>

3️⃣ The ??= Operator

The null coalescing assignment operator ??= writes a value only if the target is currently null or unset. It's the tidy shorthand for "set this default if nothing's there yet", and it's great for lazy initialisation and caching.

<?php
// ??= — the null coalescing ASSIGNMENT operator.
// "Assign the right side ONLY if the left side is currently null/unset."

$settings = [];

$settings['timeout'] ??= 30;   // unset -> becomes 30
$settings['timeout'] ??= 99;   // already 30 -> unchanged

echo $settings['timeout'] . "\n";  // 30

// Handy for lazy defaults / caching:
$cache = null;
$cache ??= "expensive result";     // computed once
$cache ??= "ignored";              // skipped, $cache already set
echo $cache . "\n";                // expensive result
?>

4️⃣ ?? vs ?: — Null vs Falsy

This is the distinction that trips people up. ?? only reacts to null; the short ternary ?: (the "Elvis" operator) reacts to anything falsy. For a value of 0 or "", the two behave completely differently — choose deliberately.

<?php
// ?? checks for NULL. The "Elvis" ?: checks for FALSY. They differ!
$count = 0;

echo ($count ?? 'none') . "\n";   // 0     — 0 is not null, so ?? keeps it
echo ($count ?: 'none') . "\n";   // none  — 0 IS falsy, so ?: replaces it

$name = "";
echo ($name ?? 'guest') . "\n";   // (empty) — "" is not null
echo ($name ?: 'guest') . "\n";   // guest   — "" is falsy

// Rule of thumb: use ?? for "missing/null", use ?: for "empty/falsy".
?>

5️⃣ The Nullsafe Operator ?->

Added in PHP 8, the nullsafe operator ?-> reads a property or calls a method only if the object isn't null; if it is, the whole chain evaluates to null instead of throwing. It replaces piles of nested isset() checks for optional object graphs, and pairs perfectly with ?? for a final default.

<?php
// ?-> — the nullsafe operator (PHP 8). Call a method or read a property
// only if the object is NOT null; otherwise the whole chain is null —
// no "method on null" error.

class Address { public ?string $city = null; }
class User {
    public ?Address $address = null;
    public function __construct(?Address $a = null) { $this->address = $a; }
}

$withCity = new User(new Address());
$withCity->address->city = "Paris";
$noAddress = new User();   // address is null

// Before PHP 8 you'd need nested isset() checks. Now:
echo ($withCity->address?->city ?? 'unknown') . "\n";  // Paris
echo ($noAddress->address?->city ?? 'unknown') . "\n"; // unknown (no error!)
?>

Now you try. Fill the ___ blanks with ?? or ??=.

<?php
// 🎯 YOUR TURN — fill each ___ to read settings safely.
$prefs = ['volume' => 0];

$volume = $prefs['volume'] ___ 50;   // 👉 use ?? (keeps 0, it's not null)
$bright = $prefs['bright'] ___ 80;   // 👉 use ?? (key missing -> 80)
$prefs['mode'] ___ 'auto';           // 👉 use ??= to set a default mode

echo $volume . "\n";        // 0
echo $bright . "\n";        // 80
echo $prefs['mode'] . "\n"; // auto

// ✅ Expected output:
//    0
//    80
//    auto
?>

🧩 Reorder Challenge

These scrambled lines should pick a display name and default a role. Put them in the order that prints Sam (member).

Create the array first, then derive $name and $role from it, then echo. The chain $user['nick'] ?? $user['name'] skips the null nickname and lands on "Sam", and the missing role key falls back to "member". Output: Sam (member).

🧠 Quick Recall

Predict the output before revealing each answer.

Prints 0. ?? only replaces null, and 0 is not null. With ?: instead, it would print none.

Prints 5. The first ??= sets $x to 5 because it was null; the second is skipped because $x is no longer null.

Evaluates to n/a. The ?-> short-circuits the null object to null (no error), then ?? supplies the default.

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Null Operators

OperatorReacts toExample → result
??null / undefined$x ?? 'd'
??=null / undefined$x ??= 'd'
?:any falsy value$x ?: 'd'
?->null object$o?->m()
=== nullexactly null$x === null
is_null()exactly nullis_null($x)

Mini-Challenge: Build a Greeting

Write it yourself, run it on onecompiler.com/php or your own machine, then check against the expected output in the comments.

<?php
// 🎯 MINI-CHALLENGE: build a user greeting from a messy profile.
// No code filled in — follow the steps, then run and compare.
//
// Given: $profile = ['name' => 'Sam', 'nickname' => null];
//
// 1. $display = the nickname if it's set and non-null, else the name,
//    else 'Anonymous'. Hint: $profile['nickname'] ?? $profile['name'] ?? 'Anonymous'.
// 2. $role = $profile['role'] ?? 'member'   (key is missing).
// 3. echo "Welcome <display> (<role>)".
//
// ✅ Expected output:
//    Welcome Sam (member)

// your code here
?>

🎉 Lesson Complete!

Practice quiz

What does echo $count ?? 'none'; print when $count = 0?

  • none
  • (empty string)
  • 0
  • null

Answer: 0. ?? only replaces null or undefined. 0 is not null, so it is kept.

What does echo $count ?: 'none'; print when $count = 0?

  • none
  • 0
  • false
  • null

Answer: none. The ?: (Elvis) operator reacts to any falsy value, and 0 is falsy, so it is replaced.

After $x = null; $x ??= 5; $x ??= 9; what is $x?

  • 9
  • null
  • 14
  • 5

Answer: 5. The first ??= sets $x to 5 because it was null; the second is skipped because $x is no longer null.

What is the result of null ?? null ?? 'third' ?? 'fourth'?

  • 'fourth'
  • 'third'
  • null
  • An error

Answer: 'third'. ?? chains left to right and stops at the first non-null value, which is 'third'.

If $user->address is null, what is $user->address?->city ?? 'n/a'?

  • 'n/a'
  • null
  • An 'attempt to read property on null' error
  • 'city'

Answer: 'n/a'. ?-> short-circuits the null object to null without error, then ?? supplies the default 'n/a'.

Which expression is true?

  • 0 === null
  • "" === null
  • null === null
  • "0" === null

Answer: null === null. null === null is true. 0 and "" are falsy but are NOT null.

What does the ??= operator do?

  • Always assigns the right side
  • Assigns the right side only if the left side is null or unset
  • Assigns only if the left side is falsy
  • Compares two values for null

Answer: Assigns the right side only if the left side is null or unset. ??= is null coalescing assignment: $x ??= $d is shorthand for $x = $x ?? $d.

What does $config['lang'] ?? 'en' do when the 'lang' key is missing?

  • Returns 'en' but emits an 'Undefined array key' warning
  • Throws a TypeError
  • Returns null
  • Returns 'en' with no warning

Answer: Returns 'en' with no warning. ?? is null-and-undefined safe: it returns the default and never emits the undefined-key warning.

Which statement about the nullsafe operator ?-> is true?

  • You can write $obj?->prop = 1 to assign safely
  • It is read-only — you cannot assign through it
  • It throws when the object is null
  • It only works on arrays

Answer: It is read-only — you cannot assign through it. Nullsafe is read-only; $obj?->prop = 1 is not allowed. It safely reads or calls when the object is non-null.

When should you reach for ?? rather than ?:?

  • When you mean 'empty or falsy'
  • When comparing strings
  • When you mean 'missing or null'
  • When the value is always 0

Answer: When you mean 'missing or null'. Rule of thumb: ?? for 'missing/null', ?: for 'empty/falsy'. They differ around values like 0 and "".

Continue this course

Frequently asked questions

What is the null coalescing operator ??

?? returns its left operand if that operand exists and is not null, otherwise it returns the right operand. Crucially it suppresses the 'Undefined variable/array key' warning, so $_GET['page'] ?? 1 is safe even when the key is absent. It also chains: $a ?? $b ?? 'default' walks left to right and stops at the first value that isn't null. Use it whenever you want a fallback for a possibly-missing or null value.

What is the difference between ?? and the ?: (Elvis) operator?

?? tests only for null (and undefined), while ?: tests for falsiness. That makes them behave differently for values like 0, "", "0", and []. For example $count ?? 'none' keeps 0 because 0 is not null, but $count ?: 'none' replaces it because 0 is falsy. The rule of thumb: reach for ?? when you mean 'missing or null', and ?: when you mean 'empty or falsy'. Mixing them up is a classic source of bugs around zero values.

What does the ??= operator do?

??= is the null coalescing assignment operator. $x ??= $default assigns $default to $x only if $x is currently null or undefined; if $x already holds a non-null value it's left untouched. It's the concise form of $x = $x ?? $default and is perfect for setting defaults on array keys or for lazy initialisation, where you compute an expensive value once and skip it on later passes.

What is the nullsafe operator ?-> and when should I use it?

?-> (PHP 8) lets you read a property or call a method on an object that might be null without crashing. In $user->address?->city, if $address is null the whole expression short-circuits to null instead of throwing an 'attempt to read property on null' error. It replaces deeply nested isset() checks for optional object chains. Pair it with ?? to supply a final default: $user->address?->city ?? 'unknown'.