Может ли кто-нибудь объяснить разницу между сокращением тернарного оператора («?:») и оператором коалесценции нуля («??») в PHP?
Когда они ведут себя по-разному, а когда одинаково (если такое вообще бывает)?
$a ?: $b
или
$a ?? $b
Ответ 1
Когда ваш первый аргумент равен null, они практически одинаковы, за исключением того, что коалесценция null не выводит E_NOTICE, когда у вас есть неопределенная переменная. В документации по миграции PHP 7.0 говорится следующее:
Оператор null coalescing («??») был добавлен в качестве синтаксического сахара для распространенного случая, когда необходимо использовать тернарный оператор в сочетании с isset(). Он возвращает свой первый операнд, если он существует и не является NULL; в противном случае он возвращает свой второй операнд.
Вот пример кода, демонстрирующий это:
<?php
$a = null;
print $a ?? 'b'; // b
print "\n";
print $a ?: 'b'; // b
print "\n";
print $c ?? 'a'; // a
print "\n";
print $c ?: 'a'; // уведомление: Неопределенная переменная
print "\n";
$b = array('a' => null);
print $b['a'] ?? 'd'; // d
print "\n";
print $b['a'] ?: 'd'; // d
print "\n";
print $b['c'] ?? 'e'; // e
print "\n";
print $b['c'] ?: 'e'; // уведомление: Неопределенный индекс
print "\n";
В тех строках, где появляется уведомление, я использую сокращенный тернарный оператор, а не оператор коалесценции нуля. Однако даже с уведомлением PHP выдаст тот же ответ.
Конечно, не всегда предполагается, что первый аргумент равен null. Как только он перестает быть нулевым, появляются различия в результате операции. Так, оператор «??» всегда возвращает первый аргумент, а сокращение «?:» – только если первый аргумент истинный, а это зависит от того, как PHP будет приводить тип к логическому.
Итак:
$a = false ?? 'f'; // false
$b = false ?: 'g'; // 'g'
Ответ 2
Я выполнил приведенные ниже действия в интерактивном режиме php (php -a в терминале). Комментарий в каждой строке показывает результат.
var_export (false ?? 'value2'); // false
var_export (true ?? 'value2'); // true
var_export (null ?? 'value2'); // value2
var_export ('' ?? 'value2'); // ""
var_export (0 ?? 'value2'); // 0
var_export (false ?: 'value2'); // value2
var_export (true ?: 'value2'); // true
var_export (null ?: 'value2'); // value2
var_export ('' ?: 'value2'); // value2
var_export (0 ?: 'value2'); // value2
Null Coalescing Operator «??»
«??» – это как «ворота», которые пропускают только NULL.
Таким образом, он всегда возвращает первый параметр, если только первый параметр не является NULL.
Это означает, что «??» – это то же самое, что ( !isset() || is_null() ).
Использование «??»
Сокращает проверку !isset() || is_null().
Например, $object = $object ?? new objClassName();.
В итоге
$v = $x ?? $y ?? $z;
// Замена этой последовательности "SET && NOT NULL":
if( $x && !is_null($x) ){
return $x;
} else if( $y && !is_null($y) ){
return $y;
} else {
return $z;
}
Тернарный оператор «?:»
«?:» – это как «ворота», которые пропускают все фальшивое, включая NULL.
Все фальшивое: 0, пустая строка, NULL, false, !isset(), empty().
То же самое, что и старый тернарный оператор: X ? Y : Z.
Примечание: «?:» будет бросать PHP NOTICE на неопределенные (unset или !isset()) переменные.
Использование «?:»
Проверка empty(), !isset(), is_null() и т.д.
Сокращение тернарной операции, например, !empty($x) ? $x : $y до $x ?: $y .
Сокращение для if(!$x) { echo $x; } else { echo $y; } до echo $x ?: $y .
В итоге
echo 0 ?: 1 ?: 2 ?: 3; //1
echo 1 ?: 0 ?: 3 ?: 2; //1
echo 2 ?: 1 ?: 0 ?: 3; //2
echo 3 ?: 2 ?: 1 ?: 0; //3
echo 0 ?: 1 ?: 2 ?: 3; //1
echo 0 ?: 0 ?: 2 ?: 3; //2
echo 0 ?: 0 ?: 0 ?: 3; //3
// Для замены этой последовательности:
if( truthy ) {}
else if(truthy ) {}
else if(truthy ) {}
..
else {}
Складывая оба варианта:
if( isset($_GET['name']) && !is_null($_GET['name'])) {
$name = $_GET['name'];
} else if( !empty($user_name) ) {
$name = $user_name;
} else {
$name = 'anonymous';
}
К этому:
$name = $_GET['name'] ?? $user_name ?: 'anonymous';
Ответ 3
Если вы используете сокращенный тернарный оператор, как показано здесь, это вызовет уведомление, если $_GET['username'] не установлен:
$val = $_GET['username'] ?: 'default';
Поэтому вместо этого вы должны сделать что-то вроде этого:
$val = isset($_GET['username']) ? $_GET['username'] : 'default';
Оператор null coalescing эквивалентен приведенному выше утверждению и вернет 'default', если $_GET['username'] не установлен или равен null:
$val = $_GET['username'] ?? 'default';
Обратите внимание, что он не проверяет истинность. Он проверяет только, установлено ли значение и не является ли оно null. Вы также можете сделать это, и будет возвращено первое определенное (установленное и не null) значение:
$val = $input1 ?? $input2 ?? $input3 ?? 'default';
Вот это и есть правильный оператор коалесценции.
Ответ 4
Оператор коалесценции нулевых значений («??»)
Все истинно, кроме нулевых и неопределенных значений (переменная/индекс массива/атрибуты объекта).
$array = [];
$object = new stdClass();
var_export (false ?? 'второй'); # false
var_export (true ?? 'второй'); # true
var_export (null ?? 'второй'); # "второй"
var_export ('' ?? 'второй'); # ""
var_export ('некоторый текст' ?? 'второй'); # "некоторый текст"
var_export (0 ?? 'second'); # 0
var_export ($undefinedVarible ?? 'второй'); # "второй"
var_export ($array['undefined_index'] ?? 'второй'); # "второй"
var_export ($object->undefinedAttribute ?? 'второй'); # "второй"
Этот код проверяет, существует ли переменная (индекс массива, атрибут объекта... и т.д.) и не является ли она нулевой, аналогично функции isset.
Тернарный оператор («?:»)
Все ложные операции (false, null, 0, пустая строка) выдаются как false, но если это неопределенное значение, то оно также выдается как false, и будет выдано предупреждение.
$array = [];
$object = new stdClass();
var_export (false ?: 'второй'); # "второй"
var_export (true ?: 'второй'); # true
var_export (null ?: 'второй'); # "второй"
var_export ('' ?: 'второй'); # "второй"
var_export ('некоторый текст' ?? 'второй'); # "некоторый текст"
var_export (0 ?: 'второй'); # "второй"
var_export ($undefinedVarible ?: 'второй'); # "второй" предупреждение: Неопределенная переменная
var_export ($array['undefined_index'] ?: 'второй'); # "второй" предупреждение: Неопределенный индекс
var_export ($object->undefinedAttribute ?: 'второй'); # "предупреждение: Неопределенный индекс
Ответ 5
Есть плюсы и минусы в использовании «??» или «?:». Плюсом использования «?:» является то, что он оценивает false, null и "" одинаково. Минус в том, что он выдает сообщение E_NOTICE, если предыдущий аргумент равен null. В случае с «??» плюсом является отсутствие E_NOTICE, но минусом является то, что false и null не оцениваются одинаково. По своему опыту я видел, как люди начинают использовать null и false как взаимозаменяемые, но затем они в конечном итоге прибегают к модификации своего кода, чтобы быть последовательными в использовании либо null, либо false, но не обоих. Альтернативой является создание более сложного тернарного условия:
(isset($something) or !$something) ? $something : $something_else.
Ниже приведен пример разницы в использовании оператора «??» при использовании как null, так и false:
$false = null;
$var = $false ?? "true";
echo $var . "---<br>";//returns: true---
$false = false;
$var = $false ?? "true";
echo $var . "---<br>"; //returns: ---
Однако, усовершенствовав тернарный оператор, мы можем заставить ложную или пустую строку "" вести себя так, как если бы она была нулем, не вызывая при этом e_notice:
$false = null;
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: ---
$false = false;
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: ---
$false = "";
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: ---
$false = true;
$var = (isset($false) or !$false) ? $false : "true";
echo $var . "---<br>";//returns: 1---
Лично я думаю, что было бы очень хорошо, если бы в будущей версии PHP появился еще один новый оператор «:?», который заменил бы приведенный выше синтаксис, т.е.:
// $var = $false :? "true";
Такой синтаксис будет одинаково оценивать null, false и "" и не будет выдавать E_NOTICE...
Web