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
$stock = 0;
$label = $stock > 0 ? "In stock" : "Sold out";
echo $label . "\n";
$count = 1;
echo "You have $count " . ($count === 1 ? "item" : "items") . "\n";Sold out
You have 1 itemThe 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
$nickname = "";
echo ($nickname ?: "Guest") . "\n";
$discount = 0;
echo ($discount ?: "no discount") . "\n";Guest
no discountThat 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
$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");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
$config = ['currency' => null];
$userCurrency = null;
echo ($userCurrency ?? $config['currency'] ?? 'INR') . "\n";INRAssign a default with ??=
??= assigns only if the variable or key is missing or null. It is a short way to fill in defaults:
<?php
$options = ['limit' => 20];
$options['limit'] ??= 10;
$options['order'] ??= 'newest';
print_r($options);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
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";string(6) "Mumbai"
NULL
NULL
No city on fileCombining ?-> 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
matchorif. - 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
$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";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.