Avatar ·

Что означает двойной вопросительный знак (??) в PHP? Оператор объединения с null

Всем привет! Я новичок в PHP и наткнулся на странный синтаксис в чужом коде. Там было написано что-то вроде: ```php $username = $_GET['user'] ?? 'гость'; ``` Я понимаю, что такое `$_GET['user']` и что такое присваивание, но вот эти два вопросительных знака `??` меня сбили с толку. Я знаю, что один вопросительный знак `?` используется в тернарном операторе, например `$a ? $b : $c`. Но что делает двойной `??`? Я пытался загуглить, но нашёл только общие объяснения на английском. Мне нужно понять на русском, как это работает и зачем это нужно. В каких ситуациях это применяется? Можно ли обойтись без этого оператора, используя старые конструкции? И есть ли какие-то подводные камни? Особенно меня интересует, как это работает с массивами и формами. У меня часто бывает, что нужно проверить, пришло ли значение из POST-запроса, и если нет — подставить значение по умолчанию. Раньше я писал длинные конструкции с `isset()`, но может быть этот оператор упростит код? Буду благодарен за понятное объяснение с примерами! Желательно на русском, чтобы точно всё усвоить. <<>>

Что такое оператор объединения с null (??)

Двойной вопросительный знак ?? в PHP — это оператор объединения с null (null coalescing operator). Он появился в PHP 7.0 и стал настоящим спасением для тех, кто устал писать громоздкие проверки с isset().

Синтаксис и логика работы

Оператор выглядит так:

$result = $expression1 ?? $expression2;

Он работает следующим образом:

  • Если $expression1 существует и не равен null, то возвращается значение $expression1.
  • В противном случае возвращается $expression2.

Важнейшая особенность: оператор не вызывает предупреждение (warning), если переменная или ключ массива не существует. Он ведёт себя точно так же, как isset().

Простой пример

<?php
// Вместо длинной проверки:
$username = isset($_GET['user']) ? $_GET['user'] : 'гость';

// Теперь можно писать кратко:
$username = $_GET['user'] ?? 'гость';

echo "Привет, $username!";
?>

Если в URL передан параметр ?user=Иван, выведется "Привет, Иван!". Если параметра нет — "Привет, гость!". Никаких ошибок и предупреждений.

Чем отличается от тернарного оператора?

Многие путают ?? с сокращённым тернарным оператором ?: (один вопросительный знак). Разница критическая:

<?php
// Тернарный оператор ?: проверяет на "истинность" (truthy/falsy)
$value1 = 0 ?: 'default';  // Вернёт 'default', так как 0 считается false
$value2 = '' ?: 'default'; // Вернёт 'default', пустая строка — false

// Оператор ?? проверяет ТОЛЬКО на null и несуществование
$value3 = 0 ?? 'default';  // Вернёт 0, так как 0 — это не null
$value4 = '' ?? 'default'; // Вернёт '', пустая строка — не null
?>

Это очень важно: ?? не считает пустую строку, ноль или false "пустыми" значениями. Он реагирует только на null и отсутствие переменной.

Работа с массивами и вложенными данными

Особенно удобен оператор при работе с массивами, например, с данными из форм или API:

<?php
$data = [
    'name' => 'Анна',
    'age' => null,
    'city' => ''
];

// Обычная проверка:
$name = isset($data['name']) ? $data['name'] : 'Неизвестно';
$age = isset($data['age']) ? $data['age'] : 'Не указан';
$city = isset($data['city']) ? $data['city'] : 'Не указан';

// С оператором ??:
$name = $data['name'] ?? 'Неизвестно';  // 'Анна'
$age = $data['age'] ?? 'Не указан';     // 'Не указан' (потому что null)
$city = $data['city'] ?? 'Не указан';   // '' (пустая строка — не null!)
?>

Обратите внимание на $city: так как значение существует и не равно null, вернётся пустая строка. Если вам нужно проверить и на пустоту, используйте другие методы.

Цепочка операторов

Операторы можно объединять в цепочку для последовательной проверки нескольких значений:

<?php
// Поиск первого не-null значения
$config = $envConfig ?? $fileConfig ?? $defaultConfig ?? 'стандартные настройки';
?>

Это работает слева направо: если $envConfig не null — берём его, иначе проверяем $fileConfig и так далее.

Nullsafe-оператор (?->) — дополнение из PHP 8

В PHP 8 появился похожий оператор для цепочек вызовов методов — ?-> (nullsafe operator). Он позволяет безопасно обращаться к методам и свойствам объектов, которые могут быть null:

<?php
// Без nullsafe:
$country = null;
if ($user !== null) {
    $address = $user->getAddress();
    if ($address !== null) {
        $country = $address->getCountry();
    }
}

// С nullsafe (PHP 8+):
$country = $user?->getAddress()?->getCountry();
?>

Подводные камни

  • Не путайте с ?: Оператор ?? проверяет только на null/несуществование, а ?: — на "ложность" (false, 0, '', null, []).
  • Приоритет: Оператор ?? имеет низкий приоритет. Если используете его в сложных выражениях, ставьте скобки: ($a ?? $b) + 5.
  • Не заменяет isset() для множества переменных: Если нужно проверить сразу несколько вложенных ключей, isset() может быть удобнее.

Итог

Оператор ?? — это элегантный способ задать значение по умолчанию для переменных, которые могут быть неопределены или равны null. Он делает код короче, чище и безопаснее. Используйте его везде, где раньше писали isset($x) ? $x : $default.

<<>>

Практический пример: обработка формы с множеством полей

Допустим, у вас есть форма регистрации с 10 полями. Без оператора ?? код превращается в "простыню" из isset:

<?php
// Старый способ — ужасно
$name = isset($_POST['name']) ? trim($_POST['name']) : '';
$email = isset($_POST['email']) ? trim($_POST['email']) : '';
$phone = isset($_POST['phone']) ? trim($_POST['phone']) : '';
// ... ещё 7 полей
?>

С оператором ?? это становится лаконичным:

<?php
// Новый способ — красиво
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$phone = trim($_POST['phone'] ?? '');
// ... остальные поля
?>

Альтернативный подход: функция-обёртка

Если вам нужно часто задавать значения по умолчанию и при этом проверять на пустоту (а не только на null), можно написать свою функцию:

<?php
function getParam(array $data, string $key, $default = null) {
    return isset($data[$key]) && $data[$key] !== '' ? $data[$key] : $default;
}

// Использование:
$name = getParam($_POST, 'name', 'Неизвестно');
// Теперь пустая строка тоже будет заменена на значение по умолчанию
?>

Но для большинства случаев встроенный ?? — это именно то, что нужно. Он встроен в язык, работает быстро и не требует дополнительных функций.

Совет для новичков

Начните с замены всех конструкций вида isset($x) ?