Потрійний оператор PHP порівняно з нульовим оператором злиття


342

Чи може хтось пояснити відмінності між потрійним оператором стенограмою ( ?:) та нульовим оператором злиття ( ??) в PHP?

Коли вони поводяться по-різному і коли однаково (якщо це взагалі трапляється)?

$a ?: $b

В.С.

$a ?? $b

Відповіді:


345

Коли ваш перший аргумент є недійсним, вони в основному однакові, за винятком того, що злиття нуля не виведе a, 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. Як тільки це більше не буде нульовим, тоді ви закінчуєтесь відмінностями в тому, що ??оператор завжди повертає перший аргумент під час?: стенограма буде тільки в тому випадку, якщо перший аргумент є правдоподібним, і це спирається на те, як PHP вводить речі в булеві .

Тому:

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

Тоді було $aб рівним falseі $bрівним 'g'.


8
Порада: якщо ви використовували ?? замість ?: але тоді вам доведеться зробити свій код сумісним з версіями PHP, старшими 7 (для плагіна для колишніх), тоді ви можете замінити ?? з isset ($ щось)? $ something: $ something_else скрізь у коді. Ви можете легко зробити це за допомогою Notepad ++ або nedit (та інших редакторів) також за допомогою інструменту пошуку / заміни, вибравши параметр регулярного виразу та вставивши у поле пошуку: "\ s * (\ S +) \ s * \? \?" і в полі заміни: "isset ($ 1)? $ 1:" без лапок (nedit використовує \ 1 замість $ 1). Потім замініть всі.
Даміан Грін

14
Це правильна відповідь, проте перевірка правдивості є головною різницею, і її слід підкреслити більше.
mancze

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 Coalescing Operator - ??:

  • ?? це як "ворота", які пропускають лише 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. Ternary Operator можна скласти ...

    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. Null Coalese Operator можна скласти ...

    $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';

    Класно, правда? :-)


3
На сьогодні найкраща відповідь
Faizan Anwer Ali Rupani

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';

Тепер це належний оператор злиття.


42

Основна різниця в тому

  1. Троичной Оператор вираз expr1 ?: expr3повертає , expr1якщо має expr1значення , TRUEале з іншого боку Null Коалесцентного Оператором вираз (expr1) ?? (expr2) приймає значення , expr1якщо expr1є НЕ NULL

  2. Троичной Оператор expr1 ?: expr3 випускає повідомлення , якщо сторона значення лівого (expr1) не існує , але з іншого боку Null Коалесцентного оператором (expr1) ?? (expr2) Зокрема, не виділяє повідомлення , якщо сторона значення лівого (expr1) не існує, так само , як isset().

  3. TernaryOperator залишається асоціативним

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

    Null Coalescing Operator є правильним асоціативом

    ($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);

Null Coalescing Operator (??)

$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);

Ось таблиця, яка пояснює різницю та подібність між '??'і?:

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

Спеціальна примітка: оператор з’єднання нуля та потрійний оператор - це вираз, і він оцінює не змінну, а результат виразу. Це важливо знати, чи потрібно повернути змінну за посиланням. Виписка return $ foo ?? $ бар; і повернути $ var == 42? $ a: $ b; в тому, що функція зворотного посилання не працює, і попередження видається.


15

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

Якщо змінна порожня (''), нульове злиття вважатиме цю змінну справжньою, але короткочасний потрійний оператор не буде. І це є на увазі.

$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 оцінюється як ІСТИНА .

??короткий для 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; інакше він повертає свій другий операнд.

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


1
Будь ласка, не вважайте, що це стосується ланцюга ... так важко читати / розуміти, як прикуті тернари
Марк Бейкер

7
@MarkBaker Приковані тернари важко зрозуміти, оскільки PHP порушив потрійну асоціативність. Це не стосується оператора коалесценції, і імхо ланцюг з'єднань цілком зрозумілий.
NikiC

7
Я не погоджуюсь. Зв'язування нульового злиття є чудовою особливістю, і це не ускладнює читання, якщо ви розумієте оператора. Він зазвичай використовується в JavaScript, і як тільки люди отримують комфорт з ним у PHP, цей виклик не використовувати ланцюжок повинен припинитися Прив'язування тернарів дуже важко читати, але нульовий злиття легко. Коли ви читаєте зліва направо, він просто перераховує, яке значення слід використовувати далі.
earl3s

2
Це дуже схоже на загальну a || b || cсхему в JS, за винятком того, що PHP можна використовувати для булевих ( false || 2у JS - 2; false ?? 2у PHP - помилково)
fregante

1
Я не погоджуюся з вами та іншими щодо того, щоб не використовувати ланцюжок. Це як сказати, що ніколи не використовуйте для циклів, тому що вони можуть не розуміти їх. Розробники / кодери абсолютно вільні у використанні стандартів та методів кодування, які вони розуміють, навіть якщо інші цього не роблять. Особисто я вважаю ланцюговий злиття як дуже схожий на переключення тверджень. Він повертає перше значення, яке знайдено (встановлено), і останнє значення, якщо нічого не знайдено.
kurdtpage

3

Інші відповіді заглиблюються і дають чудові пояснення. Для тих, хто шукає швидкої відповіді,

$a ?: 'fallback' є $a ? $a : 'fallback'

поки

$a ?? 'fallback' є $a = isset($a) ? $a : 'fallback'


Основна відмінність полягала б у тому, що лівий оператор:

  • Falsy значення , яке не дорівнює нулю ( 0, '', false,[] , ...)
  • Невизначена змінна

У $a =вищезгаданому розширенні не повинно бути ніяких ??. $a ?? 'fallback' не встановлює і не змінює значення $ a. (Це просто повертає значення).
Зробіть

2

Здається, є плюси і мінуси використання будь-якого ??або ?:. Професіонал використання ?:полягає в тому, що він оцінює "false" та "null" та "" те саме. Суть полягає в тому, що він повідомляє E_NOTICE, якщо попередній аргумент є нульовим. З ??профі - це те, що E_NOTICE не існує, але суть полягає в тому, що він не оцінює помилкове і недійсне те саме. На моєму досвіді я бачив, як люди починають використовувати нульову та помилкову взаємозамінність, але згодом вони вдаються до зміни свого коду, щоб відповідати використанню або 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 ...


3
ви можете використовувати $ var = $ false ?? null?: "Рядок порожній / хибний / null / undefined";
RedSparr0w

Ого ... ?? null ?:справа дуже приголомшлива, дякую г-н. розумний хлопець.
Blaine Lafreniere

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'])у першому прикладі все одно зайвий
Ярон У.

0

Використовуючи суперглобали, такі як $ _GET або $ _REQUEST, ви повинні знати, що вони можуть бути порожнім рядком. У цьому розмовному випадку цей приклад

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

не вдасться, оскільки значення $ ім'я користувача зараз є порожнім рядком.

Тож, використовуючи $ _GET або навіть $ _REQUEST, вам слід скористатись потрійним оператором замість цього:

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

Тепер значення $ ім'я користувача - "ніхто", як очікувалося.


Хороший улов. Крім того, оператор коалесценції також вийде з ладу у випадку порожнього рядка.
Choxx
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.