?? returns the value on its left if it exists and is not null, otherwise the value on its right: $name = $_GET['name'] ?? 'Guest';. Unlike reading the key directly, it never warns when the variable or array key is missing.
On the first run there is no query string, so both defaults are used. Click a link in the Page tab and the script runs again with $_GET filled in. Reading $_GET["name"] without ?? on that first run would print Warning: Undefined array key "name" on a normal PHP setup. More about request data is on the _POST page.
?? is a short isset() check
$a ?? $b means exactly isset($a) ? $a : $b. It works on variables, array keys at any depth, and object properties, and it stops at the first missing piece without complaint.
The check is the same one isset does, so a key that exists with the value null also takes the default.
Chain ?? to try several sources
?? is right-associative and you can chain as many as you like. PHP returns the first operand that is set and not null.
??= assigns only when null
$a ??= $b stores $b in $a only when $a is unset or null. It is the shortest way to fill in defaults or to compute something once and reuse it.
The right side of ??= is only evaluated when the assignment happens, which is why computing 4 appears once.
?? vs ?: (short ternary)
?? tests for null. ?: tests for truthiness. With values like 0, "" and "0" they give different answers:
A quantity of "0" is real input: ?? keeps it, ?: replaces it. ?: on its own would warn about the missing key, which is why the block wraps it as ($inputs[$key] ?? null) ?: .... The ternary operator page covers ?: itself.
The nullsafe operator ?->
?-> is the object version of the same idea, added in PHP 8. If the object on the left is null, the whole chain stops and returns null instead of throwing an Error such as Call to a member function getAddress() on null. Put ?? at the end for a default.
User 2 exists but has no address, and user 3 does not exist at all. Both reach the ?? "unknown" default without an error.
Common mistake: ?? has low precedence
?? binds more loosely than . and the arithmetic operators. So in "Count: " . $count ?? 0, PHP first builds the string "Count: " . $count, which is never null, and the default is never used.
Wrap the ?? expression in parentheses whenever it sits inside a larger expression.
Frequently Asked Questions
What does ?? mean in PHP?
?? is the null coalescing operator. $a ?? $b returns $a if it exists and is not null, otherwise $b. It is short for isset($a) ? $a : $b and does not raise a warning when $a or an array key is undefined.
What is the difference between ?? and ?: in PHP?
?? falls back only when the left side is null or not set, so 0 ?? 5 is 0 and '' ?? 'x' is ''. ?: falls back whenever the left side is falsy, so 0 ?: 5 is 5. ?: also warns about undefined variables and keys, ?? does not.
What does ??= do in PHP?
$a ??= $b assigns $b to $a only if $a is null or not set; otherwise it leaves $a alone. It is short for $a = $a ?? $b; and was added in PHP 7.4.
What is the nullsafe operator ?-> in PHP?
$obj?->method() calls the method only when $obj is not null; if it is null, the whole chain returns null instead of throwing an error. Combine it with ?? for a default: $user?->getAddress()?->city ?? 'unknown'. It was added in PHP 8.0.