Тернарный оператор PHP против оператора объединения нулей

342

Может кто-нибудь объяснить разницу между кратким оператором shorthand ( ?:) и оператором объединения нулей ( ??) в PHP?

Когда они ведут себя по-разному и когда одинаково (если это вообще происходит)?

$a ?: $b

VS.

$a ?? $b
balping
источник

Ответы:

345

Когда ваш первый аргумент равен нулю, они в основном совпадают, за исключением того, что объединение с нулем не будет выводить, E_NOTICEкогда у вас есть неопределенная переменная. Документация по миграции на PHP 7.0 имеет следующее:

Нулевой оператор объединения (??) был добавлен в качестве синтаксического сахара для общего случая необходимости использования троичного в сочетании с 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'; // Notice: Undefined variable: c in /in/apAIb on line 14
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'; // Notice: Undefined index: c in /in/apAIb on line 33
print "\n";

Линии, которые имеют уведомление, - это те, где я использую сокращенный тернарный оператор, а не оператор объединения нулей. Тем не менее, даже с уведомлением, PHP даст тот же ответ обратно.

Выполните код: https://3v4l.org/McavC

Конечно, это всегда предполагает первый аргумент null. Как только он перестанет иметь значение null, вы получите различия в том, что ??оператор всегда будет возвращать первый аргумент, тогда как ?:сокращение будет только в том случае, если первый аргумент верен, и это зависит от того, как PHP будет приводить типы к булевому значению .

Так:

$a = false ?? 'f'; // false
$b = false ?: 'g'; // 'g'

тогда $aбудет равно falseи $bравно 'g'.

MasterOdin
источник
8
Совет: если вы использовали ?? вместо?: но затем вам нужно, чтобы ваш код был совместим с версиями PHP старше 7 (для плагина для ex), тогда вы можете поменять ?? с isset ($ что-то)? где-то в вашем коде. Вы можете легко сделать это с помощью Notepad ++ или nedit (и других редакторов), используя инструмент поиска / замены, выбрав опцию регулярного выражения и вставив в поле поиска: "\ s * (\ S +) \ s * \? \?" и в поле замены: «isset ($ 1)? $ 1:» без кавычек (nedit использует \ 1 вместо $ 1). Тогда замени все.
Дамиан Грин,
14
Это правильный ответ, однако проверка достоверности является основным отличием и должна быть более подчеркнута.
17
2
@MasterOdin Не удовлетворен вашим ответом. Оба не одинаковы. Иметь другой результат.
Любопытно
1
Стоит отметить, что вы можете использовать ?? с цепочкой. Например: $b = []; var_dump($b['a']['b']['c'] ?? 'default');или с объектами$b = new Foo; var_dump($b->a()->b()->c() ?? 'default');
Джек Б
Пожалуйста, имейте в виду, что поведение также отличается с $a = [];. См .: 3v4l.org/iCCa0
Soullivaneuh
75

Запустил ниже на php интерактивный режим ( php -aна терминале). Комментарий в каждой строке показывает результат.

var_dump (false ?? 'value2');   # bool(false)
var_dump (true  ?? 'value2');   # bool(true)
var_dump (null  ?? 'value2');   # string(6) "value2"
var_dump (''    ?? 'value2');   # string(0) ""
var_dump (0     ?? 'value2');   # int(0)

var_dump (false ?: 'value2');   # string(6) "value2"
var_dump (true  ?: 'value2');   # bool(true)
var_dump (null  ?: 'value2');   # string(6) "value2"
var_dump (''    ?: 'value2');   # string(6) "value2"
var_dump (0     ?: 'value2');   # string(6) "value2"

Итак, это моя интерпретация:

1. Нулевой оператор слияния - ??:

  • ??это как "ворота", которые пропускают только NULL .
  • Таким образом, он всегда возвращает первый параметр , если только первый параметр не былNULL .
  • Это значит так ??же, как( !isset() || is_null() )

2. Тернарный оператор - ?:

  • ?:это как ворота, которые пропускают anything falsy- в том числеNULL
  • 0, empty string, NULL, false, !isset(), empty().. все , что пахнет falsy
  • Так же, как классический троичный оператор: echo ($x ? $x : false)
  • ПРИМЕЧАНИЕ: ?:будет выбрасывать PHP NOTICEнеопределенные ( unsetили !isset()) переменные

3. Так доктор, когда я использую ??и ?:..

  • Я только шучу - я не врач, и это просто интерпретация
  • Я бы использовал ?:когда
    • делать empty($x)проверки
    • Классическая троичная операция вроде !empty($x) ? $x : $yможет быть сокращена до$x ?: $y
    • if(!$x) { fn($x); } else { fn($y); } можно сократить до fn(($x ?: $y))
  • Я бы использовал ??когда
    • Я хочу сделать !isset() || is_null()проверку
    • например, проверить, существует ли объект - $object = $object ?? new objClassName();

4. Штабелирование операторов ...

  1. Тройной Оператор может быть сложен ...

    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 {}
    
  2. Нулевой Coalese Оператор может быть сложен ...

    $v = $x ?? $y ?? $z; 

    Это последовательность:

    if(!isset($x) || is_null($x) ) {} 
    else if(!isset($y) || is_null($y) ) {}
    else {}
    
  3. Используя укладку, я могу сократить это:

    if(!isset($_GET['name'])){
       if($user_name){
          $name = $user_name;
       }else {
          $name = 'anonymous';
       }
    } else { 
       $name = $_GET['name'];
    }
    

    К этому:

    $name = $_GET['name'] ?? $user_name ?: 'anonymous';

    Круто, верно? :-)

a20
источник
3
Безусловно лучший ответ
Файзан Анвер Али Рупани
69

Если вы используете ярлык тернарный оператор, как это, это вызовет уведомление, если $_GET['username']не установлен:

$val = $_GET['username'] ?: 'default';

Поэтому вместо этого вы должны сделать что-то вроде этого:

$val = isset($_GET['username']) ? $_GET['username'] : 'default';

Оператор нулевой коалесцирующий эквивалентно выше заявление, и будет возвращать « по умолчанию» , если $_GET['username']не установлен или null:

$val = $_GET['username'] ?? 'default';

Обратите внимание, что это не проверяет правдивость . Он проверяет, только если он установлен, а не ноль.

Вы также можете сделать это, и первое определенное (установленное и не null) значение будет возвращено:

$val = $input1 ?? $input2 ?? $input3 ?? 'default';

Теперь это правильный оператор объединения.

Андрей
источник
43

Основное отличие состоит в том, что

  1. Троичной Оператор выражение expr1 ?: expr3возвращает , expr1если имеет expr1значение , TRUEно с другой стороны Null Коалесцентного Оператором выражение (expr1) ?? (expr2) принимает значение , expr1если expr1является не NULL

  2. Тернарный оператор expr1 ?: expr3 выдает уведомление, если левое значение (expr1) не существует, но, с другой стороны, Null Coalescing Operator (expr1) ?? (expr2) В частности, не выдает уведомление, если левое значение (expr1) не существует, как, например isset().

  3. TernaryOperator остается ассоциативным

    ((true ? 'true' : false) ? 't' : 'f');

    Нулевой оператор слияния является ассоциативным справа

    ($a ?? ($b ?? $c));

Теперь давайте объясним разницу между примерами:

Троичный оператор (?:)

$x='';
$value=($x)?:'default';
var_dump($value);

// The above is identical to this if/else statement
if($x){
  $value=$x;
}
else{
  $value='default';
}
var_dump($value);

Нулевой оператор объединения (??)

$value=($x)??'default';
var_dump($value);

// The above is identical to this if/else statement
if(isset($x)){
  $value=$x;
}
else{
  $value='default';
}
var_dump($value);

Вот таблица, которая объясняет разницу и сходство между '??'и?:

введите описание изображения здесь

Специальное примечание: оператор объединения нулей и троичный оператор - это выражение, и оно вычисляется не по переменной, а по результату выражения. Это важно знать, если вы хотите вернуть переменную по ссылке. Утверждение возврата $ foo ?? $ Бар; и вернуть $ var == 42? $ a: $ b; поэтому функция возврата по ссылке не будет работать и выдается предупреждение.

Дхаирья Лахера
источник
15

Оба они ведут себя по-разному, когда дело доходит до динамической обработки данных.

Если переменная пуста (''), объединение с нулем будет обрабатывать переменную как true, но сокращенный троичный оператор не будет. И это то, что нужно иметь в виду.

$a = NULL;
$c = '';

print $a ?? '1b';
print "\n";

print $a ?: '2b';
print "\n";

print $c ?? '1d';
print "\n";

print $c ?: '2d';
print "\n";

print $e ?? '1f';
print "\n";

print $e ?: '2f';

И вывод:

1b
2b

2d
1f

Notice: Undefined variable: e in /in/ZBAa1 on line 21
2f

Ссылка: https://3v4l.org/ZBAa1

Чейзи Чаз
источник
Это явно противоречит интуиции для PHP, где пустая строка обычно считается ложной. И все же это четко указано в документах для ??: It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.
Саймон
12

Оба являются сокращениями для более длинных выражений.

?:это сокращение от $a ? $a : $b. Это выражение будет иметь значение $ a, если $ a равно TRUE .

??это сокращение от isset($a) ? $a : $b. Это выражение будет иметь значение $ a, если $ a установлено и не равно нулю.

Их варианты использования перекрываются, когда $ a не определено или равно нулю. Если $ a ??не определено, E_NOTICE не будет получен, но результаты будут такими же. Когда $ a равно нулю, результат тот же.

Декан или
источник
5

Для начинающих:

Нулевой оператор объединения (??)

Все верно, кроме nullзначений и неопределенных (переменная / индекс массива / атрибуты объекта)

например:

$array = [];
$object = new stdClass();

var_export (false ?? 'second');                           # false
var_export (true  ?? 'second');                           # true
var_export (null  ?? 'second');                           # 'second'
var_export (''    ?? 'second');                           # ""
var_export ('some text'    ?? 'second');                  # "some text"
var_export (0     ?? 'second');                           # 0
var_export ($undefinedVarible ?? 'second');               # "second"
var_export ($array['undefined_index'] ?? 'second');       # "second"
var_export ($object->undefinedAttribute ?? 'second');     # "second"

это в основном проверка, что переменная (индекс массива, атрибут объекта и т. д.) существует, а не существует null. похоже на issetфункцию

Сокращенное обозначение троичного оператора (? :)

каждые ложные вещи ( false, null, 0, пустая строка) пришли как ложные, но если это не определено также приходят как ложные , но Noticeвыбросит

бывший

$array = [];
$object = new stdClass();

var_export (false ?: 'second');                           # "second"
var_export (true  ?: 'second');                           # true
var_export (null  ?: 'second');                           # "second"
var_export (''    ?: 'second');                           # "second"
var_export ('some text'    ?? 'second');                  # "some text"
var_export (0     ?: 'second');                           # "second"
var_export ($undefinedVarible ?: 'second');               # "second" Notice: Undefined variable: ..
var_export ($array['undefined_index'] ?: 'second');       # "second" Notice: Undefined index: ..
var_export ($object->undefinedAttribute ?: 'second');     # "Notice: Undefined index: ..

Надеюсь это поможет

Супун Пранит
источник
4

Прокрутите вниз по этой ссылке и просмотрите раздел, он дает вам сравнительный пример, как показано ниже:

<?php
/** Fetches the value of $_GET['user'] and returns 'nobody' if it does not exist. **/
$username = $_GET['user'] ?? 'nobody';
/** This is equivalent to: **/
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

/** Coalescing can be chained: this will return the first defined value out of $_GET['user'], $_POST['user'], and 'nobody'. **/
$username = $_GET['user'] ?? $_POST['user'] ?? 'nobody';
?>

Однако не рекомендуется объединять операторы в цепочку, так как это усложняет понимание кода при последующем его чтении.

Нулевой оператор объединения (??) был добавлен в качестве синтаксического сахара для общего случая необходимости использования троичного в сочетании с isset (). Он возвращает свой первый операнд, если он существует и не равен NULL; в противном случае он возвращает свой второй операнд.

По сути, использование оператора объединения делает его автоматической проверкой на нуль в отличие от троичного оператора.

Script47
источник
1
Пожалуйста, не рассматривайте цепочки ... это так же трудно читать / понимать, как цепные троицы
Марк Бейкер
7
@MarkBaker Тройные цепочки трудно понять, потому что PHP нарушил троичную ассоциативность. Это не относится к оператору объединения, и объединение imho-цепочки вполне понятно.
NikiC
7
Я не согласен. Цепочка объединения нулей - это отличная функция, и ее не сложно прочитать, если вы понимаете оператор. Он обычно используется в javascript, и как только люди освоятся с ним в PHP, этот вызов не использовать цепочки должен прекратиться. Тройные цепочки очень трудно читать, но объединение нулей легко. Как вы читаете слева направо, он просто показывает, какое значение следует использовать дальше.
Earl3s
2
Это очень похоже на общий a || b || cшаблон в JS, за исключением того, что PHP можно использовать для логических значений ( false || 2в JS - 2; false ?? 2в PHP - false)
fregante
1
Я не согласен с вами и другими в том, что вы не используете цепочку. Это все равно, что говорить, что вы никогда не используете циклы, потому что можете их не понимать. Разработчики / программисты могут свободно использовать стандарты и практики кодирования, которые они понимают, даже если другие этого не делают. Лично я считаю сцепление цепями очень похожим на операторы switch. Возвращает первое найденное (установленное) значение и последнее значение, если ничего не найдено.
kurdtpage
3

Другие ответы идут глубоко и дают большие объяснения. Для тех, кто ищет быстрый ответ,

$a ?: 'fallback' является $a ? $a : 'fallback'

пока

$a ?? 'fallback' является $a = isset($a) ? $a : 'fallback'


Основное отличие будет в том случае, если левый оператор либо:

  • Falsy значение , которое не равно нулю ( 0, '', false, [], ...)
  • Неопределенная переменная
Ярон У.
источник
Там не должно быть $a =в приведенном выше расширении ??. $a ?? 'fallback' не устанавливает и не изменяет значение $ a. (Он просто возвращает значение).
Doin
2

Кажется , есть свои плюсы и минусы использования либо ??или ?:. Преимущество использования ?:состоит в том, что он оценивает false и null и "" то же самое. Дело в том, что он сообщает E_NOTICE, если предыдущий аргумент равен нулю. С ??профи, что нет E_NOTICE, но мошенник в том , что она не оценивает ложные и нуль то же самое. Исходя из моего опыта, я видел, как люди начинают взаимозаменяемо использовать 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";тот синтаксис будет оценивать нуль, ложь и "" одинаково и не выбрасывать E_NOTICE ...

Дамиан Грин
источник
3
вы можете использовать $ var = $ false ?? null?: "Строка пуста / false / null / undefined";
RedSparr0w
Вау ... ?? null ?:вещь довольно крутая, спасибо, мистер. умный парень
Блейн Лафреньер
1
class a
{
    public $a = 'aaa';
}

$a = new a();

echo $a->a;  // Writes 'aaa'
echo $a->b;  // Notice: Undefined property: a::$b

echo $a->a ?? '$a->a does not exists';  // Writes 'aaa'

// Does not throw an error although $a->b does not exist.
echo $a->b ?? '$a->b does not exist.';  // Writes $a->b does not exist.

// Does not throw an error although $a->b and also $a->b->c does not exist.
echo $a->b->c ?? '$a->b->c does not exist.';  // Writes $a->b->c does not exist.
Камо
источник
0

Null Coalescing operatorвыполняет только две задачи: проверяет whether the variable is setи whether it is null. Посмотрите на следующий пример:

<?php
# case 1:
$greeting = 'Hola';
echo $greeting ?? 'Hi There'; # outputs: 'Hola'

# case 2:
$greeting = null;
echo $greeting ?? 'Hi There'; # outputs: 'Hi There'

# case 3:
unset($greeting);
echo $greeting ?? 'Hi There'; # outputs: 'Hi There'

В приведенном выше примере кода указывается, что Null Coalescing operatorобрабатывает несуществующую переменную и переменную, для которой установлено NULLодинаковое значение.

Null Coalescing operatorэто улучшение по сравнению с ternary operator. Взгляните на следующий фрагмент кода, сравнивая их:

<?php /* example: checking for the $_POST field that goes by the name of 'fullname'*/
# in ternary operator
echo "Welcome ", (isset($_POST['fullname']) && !is_null($_POST['fullname']) ? $_POST['fullname'] : 'Mr. Whosoever.'); # outputs: Welcome Mr. Whosoever.
# in null coalecing operator
echo "Welcome ", ($_POST['fullname'] ?? 'Mr. Whosoever.'); # outputs: Welcome Mr. Whosoever.

Таким образом, разница между ними заключается в том, что Null Coalescing operatorоператор предназначен для обработки неопределенных переменных лучше, чем ternary operator. Принимая во внимание, что ternary operatorэто сокращение для if-else.

Null Coalescing operatorне предназначен для замены ternary operator, но в некоторых случаях, например, в приведенном выше примере, он позволяет писать чистый код с меньшими хлопотами.

Кредиты: http://dwellupper.io/post/6/php7-null-coalescing-operator-usage-and-examples

Пранав Рана
источник
isset($_POST['fullname'])уже проверяет NULLзначения - так что && !is_null($_POST['fullname'])в первом примере все равно избыточно
Yaron U.
0

При использовании суперглобальных объектов, таких как $ _GET или $ _REQUEST, вы должны знать, что они могут быть пустой строкой. В этом конкретном случае этот пример

$username = $_GET['user'] ?? 'nobody';

потерпит неудачу, потому что значение $ username теперь является пустой строкой.

Поэтому при использовании $ _GET или даже $ _REQUEST вы должны использовать вместо этого троичный оператор, например:

$username = (!empty($_GET['user'])?$_GET['user']:'nobody';

Теперь значение $ username равно «nobody», как и ожидалось.

Александр Белинг
источник
Хороший улов. Кроме того, оператор coalescing также не будет работать в случае пустой строки.
Choxx