Himanshu SharmaSoftware engineer
Let’s talk

PHP · Lesson 17 of 54

Ternary, null coalescing and nullsafe operators

Write short conditional expressions with the ternary operator, supply defaults with ?? and ??=, and read through null objects with ?->.

10 minControl flowUpdated 22 October 2026

Awaiting technical review. This lesson is readable but not yet listed in search engines.

By the end of this lessonUse ? :, ??, ??= and ?-> to handle choices and missing values in one expression.

Choosing between two values

Often you just need one value or another: "in stock" or "sold out", "item" or "items". Writing a full if / else for that is a lot of lines. The ternary operator does it in one expression:

php
<?php
$stock = 0;
$label = $stock > 0 ? "In stock" : "Sold out";
echo $label . "\n";

$count = 1;
echo "You have $count " . ($count === 1 ? "item" : "items") . "\n";
result
Sold out
You have 1 item

The form is condition ? valueIfTrue : valueIfFalse. It is an expression, so you can assign it, print it or pass it to a function. Put brackets around it when it sits inside a longer expression, as in the second example; without them, the . concatenation would bind to the condition first.

Do not nest ternaries. $a ? $b : ($c ? $d : $e) is legal with brackets but hard to read, and PHP 8 refuses the unbracketed form altogether. Use match or if instead.

The short ternary ?:

Leaving out the middle part, a ?: b, gives "a if it is truthy, otherwise b". It is sometimes called the Elvis operator:

php
<?php
$nickname = "";
echo ($nickname ?: "Guest") . "\n";

$discount = 0;
echo ($discount ?: "no discount") . "\n";
result
Guest
no discount

That is handy for empty strings, but notice the second line: a discount of 0 is treated as "nothing", because 0 is falsy. When 0 is a valid value, you want the next operator.

Null coalescing ??

a ?? b returns a if it exists and is not null, otherwise b. Two things make it special. It only looks at null, not at other falsy values. And it does not complain when a is an undefined variable or a missing array key, which makes it perfect for optional input:

php
<?php
$query = ['sort' => 'price'];   // imagine this came from the URL

$sort = $query['sort'] ?? 'name';
$page = $query['page'] ?? 1;
$discount = 0;

echo "sort=$sort page=$page\n";
var_dump($discount ?? 5);
var_dump($missingVariable ?? "default");
result
sort=price page=1
int(0)
string(7) "default"

Compare that with reading $query['page'] directly, which would raise an "Undefined array key" warning. You will see $_GET['x'] ?? '' and $_POST['x'] ?? '' in almost every form handler.

?? can be chained, and the first value that is set wins:

php
<?php
$config = ['currency' => null];
$userCurrency = null;

echo ($userCurrency ?? $config['currency'] ?? 'INR') . "\n";
result
INR

Assign a default with ??=

??= assigns only if the variable or key is missing or null. It is a short way to fill in defaults:

php
<?php
$options = ['limit' => 20];
$options['limit'] ??= 10;
$options['order'] ??= 'newest';

print_r($options);
result
Array
(
    [limit] => 20
    [order] => newest
)

The existing limit was kept; the missing order was added.

The nullsafe operator ?->

Objects (covered in the object-oriented section) are reached with ->. If the object might be null, calling -> on it throws an error. The nullsafe operator ?->, added in PHP 8, returns null instead and skips the rest of the chain:

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

class Customer
{
    public function __construct(public ?Address $address = null) {}
}

$withAddress = new Customer(new Address("Mumbai"));
$withoutAddress = new Customer();
$nobody = null;

var_dump($withAddress->address?->city);
var_dump($withoutAddress->address?->city);
var_dump($nobody?->address?->city);
echo ($withoutAddress->address?->city ?? "No city on file") . "\n";
result
string(6) "Mumbai"
NULL
NULL
No city on file

Combining ?-> with ??, as in the last line, gives a fallback when anything along the chain is missing.

Which one when

You want Use
One of two values based on a condition cond ? a : b
A fallback when a value is empty or falsy a ?: b
A fallback when a value is missing or null a ?? b
To set a default only if nothing is there $x ??= value
To read through something that might be null $a?->b

Common mistakes

  • Using ?: where 0 or "0" are valid values, so they are replaced by the default. Use ??.
  • Using ?? and expecting it to catch empty strings. "" ?? "x" is "", because an empty string is not null.
  • Nesting ternaries instead of using match or if.
  • Forgetting brackets around a ternary inside a string concatenation.

Exercise

A search page receives optional parameters in an array $query. Read q (default empty string), page (default 1, but keep 0 if sent), and perPage (default 20 if missing or if it is empty or 0). Test with $query = ['q' => 'mug', 'page' => 0, 'perPage' => ''].

Show one possible answer
php
<?php
$query = ['q' => 'mug', 'page' => 0, 'perPage' => ''];

$q = $query['q'] ?? '';
$page = $query['page'] ?? 1;
$perPage = ($query['perPage'] ?? null) ?: 20;

echo "q=$q page=$page perPage=$perPage\n";
result
q=mug page=0 perPage=20

?? keeps the page number 0 because it is not null, while ?: replaces the empty perPage with 20 because an empty string is falsy.

Key points

  • condition ? a : b returns a when the condition is true and b otherwise.
  • a ?: b (the Elvis operator) returns a if it is truthy, otherwise b.
  • a ?? b returns a if it exists and is not null, otherwise b, without any warning for a missing key or variable.
  • $object?->method() returns null instead of failing when $object is null.

Check yourself

1. What is $_GET['page'] ?? 1 when the page parameter was not sent?
2. What does 0 ?: 10 return?
3. What does 0 ?? 10 return?
4. What does $user?->address?->city return when $user is null?