Elvis жив: сравнение short-ternary и null coalescing в PHP

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Кто такой Элвис и при чём тут 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                

Блоги

Рекомендаций по книгам