Ternary operator, null coalescing, and short-circuit evaluation
Concept
PHP provides several compact conditional and logical operators that, when used correctly, make code more readable. Misused, they hide bugs.
Ternary ($a ? $b : $c): if $a is truthy, evaluates to $b, otherwise $c. Short ternary (Elvis operator, $a ?: $c): if $a is truthy, returns $a; otherwise $c. Used for "use value if truthy, otherwise fallback". Null coalescing ($a ?? $b): if $a exists and is not null, returns $a; otherwise $b. Does not trigger isset warnings on undefined variables — safer than Elvis for array keys and properties.
Null coalescing assignment (??=, PHP 7.4+): $a ??= $b is equivalent to $a = $a ?? $b. Use to lazily initialize values.
Short-circuit evaluation: && (and) stops evaluating at the first false operand. || (or) stops at the first true operand. This is not just an optimization — it prevents errors: if ($obj !== null && $obj->method()) is safe because $obj->method() is never called if $obj is null.
Operator precedence trap: and and or have lower precedence than =. $a = true or false assigns true to $a (the assignment happens first). $a = true || false correctly evaluates the || first. Always use && and ||, not and and or, except in specific idiomatic patterns.
Code Example
<?php
declare(strict_types=1);
// Ternary — simple conditional
$age = 20;
$label = $age >= 18 ? 'adult' : 'minor';
// Short ternary (Elvis) — use value if truthy, else fallback
$name = $_GET['name'] ?: 'Anonymous'; // '' or missing → 'Anonymous'
$port = $config['port'] ?: 3306; // 0 or false → 3306 (careful with 0!)
// Null coalescing — safe undefined key/property access
$username = $_SESSION['user']['name'] ?? 'Guest'; // no E_NOTICE even if unset
$ttl = $config['cache']['ttl'] ?? 3600;
$value = $obj?->getProp() ?? 'default'; // nullsafe + null coalescing
// Null coalescing assignment — lazy initialization
$cache ??= new Cache(); // only creates if $cache is null
$request['count'] ??= 0; // initialize array key only if unset
$request['count']++;
// Elvis vs Null coalescing — the key difference
$arr = ['value' => 0];
$a = $arr['value'] ?: 'default'; // 'default' — 0 is falsy!
$b = $arr['value'] ?? 'default'; // 0 — null coalescing only checks null/undefined
// Short-circuit evaluation — prevents errors
function expensive(): bool { echo "expensive called\n"; return true; }
$fast = false;
$result = $fast && expensive(); // expensive() never called — short-circuit
// Null check chain
class Order { public ?Customer $customer = null; }
class Customer { public ?Address $address = null; }
class Address { public string $city = ''; }
$order = new Order();
// Safe chaining with short-circuit
$city = ($order->customer !== null && $order->customer->address !== null)
? $order->customer->address->city
: 'Unknown';
// Cleaner with nullsafe operator (PHP 8.0)
$city = $order->customer?->address?->city ?? 'Unknown';
// Precedence trap
$flag1 = true or false; // $flag1 = true (assignment before `or`)
$flag2 = true || false; // $flag2 = true (correct, || evaluated first)
var_dump($flag1, $flag2); // both true, but for different reasons