Web

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

Может ли кто-нибудь объяснить разницу между сокращением тернарного оператора («?:») и оператором коалесценции нуля («??») в 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 «??»

  1. «??» это как «ворота», которые пропускают только NULL.

  2. Таким образом, он всегда возвращает первый параметр, если только первый параметр не является NULL.

  3. Это означает, что «??» это то же самое, что ( !isset() || is_null() ).

Использование «??»

  1. Сокращает проверку !isset() || is_null().

  2. Например, $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; 

        }

Тернарный оператор «?:»

  1. «?:» это как «ворота», которые пропускают все фальшивое, включая NULL.

  2. Все фальшивое: 0, пустая строка, NULL, false, !isset(), empty().

  3. То же самое, что и старый тернарный оператор: X ? Y : Z.

  4. Примечание: «?:»  будет бросать PHP NOTICE на неопределенные (unset или !isset()) переменные.

Использование «?:»

  1. Проверка empty(), !isset(), is_null() и т.д.

  2. Сокращение тернарной операции, например, !empty($x) ? $x : $y до $x ?: $y .

  3. Сокращение для 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

Как выполнить запрос «INSERT IF NOT EXIST» в MySQL?

Web

Функции PHP для идентификации вредоносного кода

Web

Почему $_FILES может быть пустым при загрузке файлов в PHP?

Web

Есть ли способ обновить расширение через composer и обойти ошибку ограничения памяти в Magento2