Null Coalescing Operator (??)
debt(d3/e1/b3/t5)
Closest to 'default linter catches the common case' (d3). The detection_hints list phpcs, rector, and php-cs-fixer — all standard PHP linting/fixers — which can catch the isset() ternary pattern not using ?? and flag ?? vs ?: confusion. This is a common-case catch by default tooling.
Closest to 'one-line patch or single-call swap' (e1). The quick_fix explicitly states replacing isset($a) ? $a : $default with $a ?? $default — a single-expression substitution. Rector can automate this transformation across the codebase, making it trivial per occurrence.
Closest to 'localised tax' (b3). The operator applies broadly across web, cli, and queue-worker contexts, but misuse (confusing ?? with ?:) is confined to the specific expression site. It imposes a per-developer learning cost but doesn't structurally shape the codebase or slow down many work streams.
Closest to 'notable trap' (t5). The misconception field explicitly identifies that ?? and ?: are NOT interchangeable: falsy non-null values like 0, '', or false behave differently between the two operators. This is a documented gotcha (common_mistakes confirms it as the top mistake) that most PHP developers eventually learn, but it's not obvious from the operator's name or appearance.
Also Known As
TL;DR
Explanation
$value = $_GET['key'] ?? 'default' is equivalent to isset($_GET['key']) ? $_GET['key'] : 'default'. The ?? operator suppresses undefined index notices and handles null in one step. The ??= assignment operator (PHP 7.4) sets a variable only if it is null: $config['debug'] ??= false. Prefer ?? over empty() when you want to distinguish between a missing value and a legitimately empty/zero value — empty() treats 0, "0", [], and "" as falsy, which is often not what you want.
Common Misconception
Why It Matters
Common Mistakes
- Confusing ?? with ?: (Elvis) — ?? checks for null/undefined, ?: checks for falsiness (0, '', false also trigger it).
- Chaining too many ?? operators in a line — more than two or three becomes unreadable, extract a variable.
- Using ??= and expecting it to work on non-null falsy values — it only assigns when the left side is null.
- Relying on ?? to suppress all errors — it only handles null and undefined keys, not type errors or exceptions.
Code Examples
// Verbose null check:
$name = isset($user['name']) ? $user['name'] : 'Guest';
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
// Null coalescing — concise:
$name = $user['name'] ?? 'Guest';
$page = (int)($_GET['page'] ?? 1);
// Null coalescing assignment (PHP 7.4):
$config['timeout'] ??= 30; // Only assigns if null or not set
// ?? — returns left side if not null, otherwise right side
$name = $user['name'] ?? 'Guest';
// Chains — first non-null wins
$locale = $user->locale ?? $account->locale ?? config('app.locale');
// ??= — assign if null (PHP 7.4)
$_SESSION['cart'] ??= [];
$_SESSION['cart'][] = $item;
// Nested arrays — no need for isset() checks
$city = $data['user']['address']['city'] ?? 'Unknown';