Elvis vs Null Coalescing: в чём подвох? (Разбор под капотом PHP)
Привет, коллега! Если ты хоть раз писал на PHP, то наверняка сталкивался с ситуацией, когда нужно проверить, существует ли переменная, и если да — использовать её значение, а если нет — подставить что-то по умолчанию. Вроде бы простая задача, но именно здесь новички (и не только) часто спотыкаются о два очень похожих, но принципиально разных оператора: short-ternary (Elvis operator) ?: и null coalescing operator ??.
Это один из самых популярных вопросов на Stack Overflow (тысячи просмотров и голосов). И не зря: разница между ними — не просто академический интерес. Ошибка в выборе оператора может привести к багам, которые вы будете отлавливать часами. Давай раз и навсегда разберёмся, что у них «под капотом» и когда что использовать.
Короткое введение: зачем нам вообще эти операторы?
Представь, что ты пишешь код для отображения имени пользователя. Если имени нет — показываем «Гость». Классический способ — if:
if (isset($userName)) {
$displayName = $userName;
} else {
$displayName = 'Гость';
}
Работает, но громоздко. PHP предлагает элегантные сокращения. И тут мы подходим к главной дилемме: ?: или ??? На первый взгляд — одно и то же. Но это только на первый взгляд.
1. Elvis operator (?:) — «Король булевых значений»
Оператор Elvis (прозванный так за сходство с причёской короля рок-н-ролла: ?: — глаза, нос и чёлка) появился в PHP ещё в версии 5.3. Это сокращённая форма тернарного оператора.
Синтаксис:
$result = $expression ?: $default;
Что это значит? Интерпретатор смотрит на $expression. Если он преобразуется в true (то есть не является «ложным» значением), то возвращается $expression. Если $expression — «ложное» (false, 0, пустая строка, null, пустой массив и т.д.), то возвращается $default.
Важный нюанс: Elvis operator не проверяет, существует ли переменная. Он проверяет её истинность. Если переменная не определена, вы получите Notice: Undefined index/variable.
Пример с подвохом:
$count = 0;
$result = $count ?: 10;
echo $result; // Выведет 10, а не 0!
Почему? Потому что 0 — это «ложное» значение (falsy). Elvis посчитал, что переменная «пустая», и отдал запасной вариант. Для многих это становится сюрпризом.
2. Null Coalescing Operator (??) — «Спаситель от Notice»
Оператор null coalescing появился в PHP 7.0 и был встречен с восторгом. Его главная суперсила — он безопасно работает с несуществующими переменными, не вызывая ошибок.
Синтаксис:
$result = $expression ?? $default;
Что это значит? Оператор проверяет, существует ли $expression и не равен ли он null. Если да — возвращает $expression. В противном случае — $default.
Ключевое отличие: ?? игнорирует все «ложные» значения, кроме null и неопределённости (undefined). 0, "", false — всё это считается валидными значениями и будет возвращено.
Пример:
$count = 0;
$result = $count ?? 10;
echo $result; // Выведет 0! Потому что переменная существует и не null.
Видишь разницу? Это и есть главный «подкапотный» механизм.
3. Сравнение «под капотом»: таблица истинности
Давай сведём всё в наглядную таблицу, чтобы закрепить. Пусть у нас есть переменная $x с разными значениями.
| Значение $x | $x ?: 'default' | $x ?? 'default' |
|---|---|---|
| Не определена | Notice + 'default' | 'default' (без Notice) |
| null | 'default' (null — falsy) | 'default' |
| false | 'default' | false |
| 0 | 'default' | 0 |
| '' (пустая строка) | 'default' | '' |
| 'Hello' | 'Hello' | 'Hello' |
Вывод из таблицы: ?? — это оператор «существует и не null». ?: — это оператор «является истинным». Они решают разные задачи, хотя внешне похожи.
4. Цепочки вызовов: где магия становится очевидной
Оба оператора поддерживают chaining (цепочки). Но ведут они себя по-разному.
Цепочка с ?? (Null Coalescing):
$name = $_GET['name'] ?? $_POST['name'] ?? 'Гость';
Это работает как «найди первое существующее и не null значение». Идеально для форм.
Цепочка с ?: (Elvis):
$name = $_GET['name'] ?: $_POST['name'] ?: 'Гость';
Это будет работать только если переменные определены (иначе Notice). И вернёт первое «истинное» значение. Если имя пользователя — пустая строка, цепочка пойдёт дальше.
На практике цепочки с ?? встречаются гораздо чаще, так как они безопаснее.
5. Практический пример из жизни: настройки и конфиги
Представь, что ты читаешь настройки приложения из массива. Ключ может отсутствовать, а значение может быть 0 или false (например, флаг «отключить кеш»).
$config = [
'cache_enabled' => false, // Кеш выключен