Кто такой Элвис и при чём тут PHP?
Если вы хоть раз писали на PHP, вы наверняка видели странные конструкции в чужом коде: $name ?: 'Гость' или $name ?? 'Гость'. На первый взгляд — одно и то же. Но на самом деле за этими двумя операторами скрываются совершенно разные механики, которые могут как спасти ваш код, так и подложить свинью.
На Stack Overflow вопрос "PHP short-ternary ("Elvis") operator vs null coalescing operator" собрал десятки тысяч просмотров. И это не случайно. Разработчики путаются, ошибаются, а потом долго ищут баги. Давайте раз и навсегда разберёмся, кто есть кто, и перестанем гадать.
В этой статье мы не просто перечислим различия. Мы заглянем под капот PHP, посмотрим на реальные кейсы из продакшена и поймём, когда какой оператор использовать. Поехали!
Краткий ликбез: что это вообще такое?
Тернарный оператор (классический)
Для начала вспомним базу. Тернарный оператор — это сокращённая форма if-else:
$age = 20;
$status = ($age >= 18) ? 'Взрослый' : 'Ребёнок';
// $status = 'Взрослый'
Всё просто: если условие истинно — берём первое значение, иначе — второе.
Short-ternary (он же Elvis operator) — ?:
Это сокращение для случая, когда вы проверяете переменную на "истинность" и хотите использовать её же значение:
$name = 'Алексей';
$greeting = $name ?: 'Гость';
// $greeting = 'Алексей'
$name = '';
$greeting = $name ?: 'Гость';
// $greeting = 'Гость'
Работает так: если $name приводится к true, берём $name. Иначе — "Гость".
Null coalescing operator — ??
Появился в PHP 7.0 и сразу стал любимчиком разработчиков:
$name = 'Алексей';
$greeting = $name ?? 'Гость';
// $greeting = 'Алексей'
$name = null;
$greeting = $name ?? 'Гость';
// $greeting = 'Гость'
Отличие от Элвиса: проверка идёт только на null, а не на "ложность" в целом.
Главное отличие: что считается "пустым"?
Вот здесь начинается самое интересное. Давайте посмотрим на таблицу сравнения для разных значений $value:
$value = false;
echo $value ?: 'default'; // 'default' (false — это "ложь")
echo $value ?? 'default'; // false (false — это НЕ null)
$value = 0;
echo $value ?: 'default'; // 'default' (0 — это "ложь")
echo $value ?? 'default'; // 0 (0 — это НЕ null)
$value = '';
echo $value ?: 'default'; // 'default' (пустая строка — это "ложь")
echo $value ?? 'default'; // '' (пустая строка — это НЕ null)
$value = [];
echo $value ?: 'default'; // 'default' (пустой массив — это "ложь")
echo $value ?? 'default'; // [] (пустой массив — это НЕ null)
$value = '0';
echo $value ?: 'default'; // 'default' (строка "0" — это "ложь"!)
echo $value ?? 'default'; // '0' (строка "0" — это НЕ null)
Видите разницу? Элвис работает как проверка на "истинность" (truthy/falsy), а null coalescing — это строгая проверка на существование и отличие от null.
Под капотом: как это работает на уровне движка
Давайте заглянем в исходники PHP (ну, почти). Если вы когда-нибудь компилировали PHP из исходников, то знаете, что оба оператора обрабатываются на этапе парсинга.
Short-ternary (?):
Фактически, это синтаксический сахар. $a ?: $b превращается в $a ? $a : $b. Но есть нюанс: PHP не вычисляет $a дважды. Он сохраняет результат в временную переменную. Это важно для функций с побочными эффектами:
function getData() {
echo "Функция вызвана!\n";
return rand(0, 1) ? 'data' : '';
}
// Элвис вызовет функцию ОДИН раз
$result = getData() ?: 'default';
// "Функция вызвана!" — один раз
Если бы PHP вычислял $a дважды, функция getData() могла бы вернуть разные значения. Но разработчики PHP умные — такого не происходит.
Null coalescing (??):
Этот оператор появился позже и работает ещё хитрее. Он проверяет, существует ли переменная и не равна ли она null. Если переменная не определена — не будет ошибки E_WARNING или E_NOTICE:
// Переменная $undefined не существует
echo $undefined ?: 'default';
// Warning: Undefined variable $undefined
// Выведет 'default'
echo $undefined ?? 'default';
// Без ошибок! Выведет 'default'
Это killer feature null coalescing. Он делает код безопаснее при работе с необязательными данными: массивами, $_GET, $_POST и т.д.
Реальные сценарии: когда что выбирать?
Сценарий 1: Проверка на существование ключа в массиве
Это самый частый кейс. Допустим, у вас есть форма:
$data = [
'name' => 'Анна',
'age' => 0, // Да, возраст может быть 0
];
// ❌ Элвис — плохо!
$age = $data['age'] ?: 18;
// $age = 18! А мы хотели 0!
// ✅ Null coalescing — хорошо
$age = $data['age'] ?? 18;
// $age = 0. Всё верно.
Если вы используете Элвис, то 0, пустая строка, false будут заменены на значение по умолчанию. А это почти всегда баг.
Сценарий 2: Логины, email, статусы
А вот здесь Элвис может быть уместен:
$userName = $input['nickname'] ?: 'Аноним';
// Если nickname пустой, '0' или false — покажем 'Аноним'
// Это логично для отображаемого имени
Но будьте осторожны! Если nickname может быть валидной строкой "0" (да, такое бывает в некоторых системах), Элвис её проигнорирует.
Сценарий 3: Цепочки вызовов
Начиная с PHP 7.4, null coalescing можно использовать в цепочках. А вот Элвис — нет:
// Null coalescing — можно
$city = $user->getAddress() ?? $user->getDefaultCity() ?? 'Неизвестно';
// Элвис — не сработает так красиво
// $city = $user->get