Avatar ·

Оператор Элвис (?:) и оператор объединения с null (??) в PHP: в чем разница?

Всем привет. Я начинающий PHP-разработчик, и никак не могу понять разницу между двумя операторами: коротким тернарным оператором (его еще называют "Элвис") `?:` и оператором объединения с null `??`. В документации написано, что они похожи, но я постоянно путаюсь, какой из них использовать. У меня есть такой код. Я получаю данные из формы или из базы данных, и хочу вывести имя пользователя, если оно есть, или "Гость", если нет: ```php $username = $_POST['username'] ?: 'Гость'; ``` Это работает, но иногда я вижу, что другие разработчики пишут так: ```php $username = $_POST['username'] ?? 'Гость'; ``` Оба варианта вроде бы делают одно и то же. Но я заметил, что если в `$_POST['username']` приходит пустая строка `""`, то первый вариант (с `?:`) выводит "Гость", а второй (с `??`) выводит пустую строку. Почему так? И какой вариант правильнее использовать, когда я хочу проверить, что переменная вообще существует и не равна `null`? Еще один пример. У меня есть массив с настройками: ```php $config = [ 'timeout' => 0, 'debug' => false ]; $timeout = $config['timeout'] ?: 30; // Результат: 30 $debug = $config['debug'] ?? true; // Результат: false ``` Почему `$timeout` стал 30, хотя я явно указал 0? И почему `$debug` остался `false`, хотя я ожидал, что он станет `true`? Объясните, пожалуйста, на пальцах, как работают эти операторы и в каких случаях их применять. Заранее спасибо! <<>>

Главное отличие: проверка на "истинность" vs проверка на существование и null

Вы столкнулись с классической путаницей. Давайте раз и навсегда разберемся. Ключевое отличие в том, что именно проверяет каждый оператор.

  • Оператор Элвис ?: проверяет, является ли левое выражение "истинным" (truthy) в логическом контексте. Если да — возвращает его, если нет — возвращает правое значение.
  • Оператор объединения с null ?? проверяет, существует ли переменная и не равна ли она null. Если да — возвращает левое значение, если переменная не определена или равна null — возвращает правое.

Разбор ваших примеров

Пример 1: Пустая строка

$username = $_POST['username'] ?: 'Гость'; // Пустая строка -> 'Гость'
$username = $_POST['username'] ?? 'Гость'; // Пустая строка -> '' (пустая строка)

Пустая строка "" в PHP считается ложным (falsy) значением. Поэтому оператор Элвис видит: "Ага, слева ложь, значит, беру правое значение — 'Гость'". Оператор ?? же проверяет только на null и существование. Пустая строка — это не null, она существует. Поэтому он возвращает её как есть.

Пример 2: Число 0 и false

$timeout = $config['timeout'] ?: 30; // 0 -> 30
$debug = $config['debug'] ?? true;   // false -> false

Число 0 — это falsy значение. Элвис заменяет его на 30. А false — это хоть и falsy, но не null. Оператор ?? видит, что значение существует и не равно null, и возвращает его (false).

Полный список "falsy" значений (для Элвиса)

Оператор ?: считает ложными и заменяет следующие значения:

  • null
  • false
  • 0 и 0.0
  • "" (пустая строка) и "0"
  • [] (пустой массив)

Когда что использовать?

  • Используйте ?? когда:
    • Вы хотите проверить, определена ли переменная (ключ массива) и не равна ли она null.
    • Вам нужно безопасно получить значение, которое может отсутствовать (например, из $_GET, $_POST, результата запроса к БД).
    • Нормальные значения включают 0, false, пустую строку.
  • Используйте ?: когда:
    • Вы хотите предоставить запасное значение для любого "пустого" или "ложного" результата.
    • Например, при выводе счётчика: echo $count ?: 'Нет записей'; (если 0 — покажем сообщение).
    • Вы уверены, что 0, false или пустая строка не являются допустимыми результатами.

Продвинутый совет: цепочка операторов

Их можно комбинировать. Например, получить значение из запроса, а если его нет — из кэша, а если и там пусто — значение по умолчанию:

$value = $_GET['id'] 
    ?? $cache->get('last_id') 
    ?: 1; 
// Сначала проверим $_GET, потом кэш (если null), потом применим Элвиса к результату из кэша

Но будьте осторожны: ?? имеет более высокий приоритет, чем ?:, поэтому скобки тут расставлены правильно слева направо.

Запомните простое правило: Элвис проверяет "есть ли что-то"? (truthy), а null-коалесцент проверяет "существует ли что-то"? (not null).

<<>>

Дополнение: синтаксический сахар и вложенность

Коллега выше всё верно объяснил. Хочу добавить пару важных моментов, которые часто упускают новички.

1. Оператор ?? можно "цепочкой"

Это его уникальная фишка. Вы можете проверить несколько переменных подряд, и оператор вернёт первую существующую не-null:

$name = $firstName ?? $nickname ?? 'Безымянный';

С Элвисом так не получится — он будет проверять на truthy каждое значение, что может дать неожиданный результат, если одно из них равно 0 или false.

2. Проблема с isset()

Оператор ?? внутри себя использует isset(). Это значит, что он не вызовет ошибку (Notice), если переменная или ключ массива не существует. Элвис же ?: сначала попытается прочитать значение, и если его нет — вы получите Warning: Undefined array key. Поэтому для работы с внешними данными ($_GET, $_POST) ?? гораздо безопаснее.

3. Когда Элвис всё же лучше

Представьте, что вы пишете функцию для форматирования числа:

function formatCount($count) {
    return $count ?: '0 (нет данных)';
}

Здесь 0 — это действительно "нет данных", и Элвис отработает идеально. Если бы вы использовали ??, то при передаче 0 функция вернула бы "0", что вам не нужно.

Итог: ?? — для безопасного доступа к данным, ?: — для логических подстановок "по умолчанию".