Operador ternário PHP vs operador coalescente nulo

341

Alguém pode explicar as diferenças entre taquigrafia do operador ternário (?: ) e o operador coalescente nulo ( ??) no PHP?

Quando eles se comportam de maneira diferente e quando da mesma maneira (se isso acontecer)?

$a ?: $b

VS.

$a ?? $b
balping
fonte

Respostas:

344

Quando seu primeiro argumento é nulo, eles são basicamente os mesmos, exceto que a coalescência nula não produzirá um E_NOTICEquando você tiver uma variável indefinida. Os documentos de migração do PHP 7.0 têm a dizer:

O operador coalescente nulo (??) foi adicionado como açúcar sintático para o caso comum de precisar usar um ternário em conjunto com isset (). Retorna seu primeiro operando se existir e não for NULL; caso contrário, ele retornará seu segundo operando.

Aqui está um exemplo de código para demonstrar isso:

<?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";

As linhas que têm o aviso são aquelas em que estou usando o operador ternário de taquigrafia, em oposição ao operador coalescente nulo. No entanto, mesmo com o aviso, o PHP dará a mesma resposta.

Execute o código: https://3v4l.org/McavC

Claro, isso sempre está assumindo que o primeiro argumento é null. Uma vez que não é mais nulo, você termina com diferenças em que o ??operador sempre retornaria o primeiro argumento, enquanto a ?:abreviação retornaria apenas se o primeiro argumento fosse verdadeiro, e isso depende de como o PHP converteria as coisas em um valor booleano .

Assim:

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

seria então $aigual falsee $bigual a 'g'.

MasterOdin
fonte
8
Dica: se você estiver usando ?? em vez de?: mas, em seguida, você precisa tornar seu código compatível com versões do PHP anteriores a 7 (para um plug-in para ex), convém trocar o ?? com isset ($ algo)? $ something: $ something_else em qualquer lugar do seu código. Você pode fazer isso facilmente com o Notepad ++ ou o nedit (e outros editores também) usando a ferramenta Localizar / Substituir, selecionando a opção de expressão regular e inserindo no campo Localizar: "\ s * (\ S +) \ s * \? \?" e no campo substituir: "isset ($ 1)? $ 1:" sem as aspas (o nedit usa \ 1 em vez de $ 1). Depois substitua tudo.
Damian Green
14
Esta é a resposta certa, no entanto, a verificação da veracidade é a principal diferença e deve ser mais enfatizada.
mancze
2
@MasterOdin Não satisfeito com sua resposta. Ambos não são iguais. Tem resultado diferente.
Curioso
11
Vale a pena notar que você pode usar? com encadeamento. Por exemplo: $b = []; var_dump($b['a']['b']['c'] ?? 'default');ou com objetos$b = new Foo; var_dump($b->a()->b()->c() ?? 'default');
Jack B
Esteja ciente de que o comportamento também é diferente $a = [];. Veja: 3v4l.org/iCCa0
Soullivaneuh
75

Execute o abaixo no modo interativo php ( php -ano terminal). O comentário em cada linha mostra o resultado.

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"

Então esta é a minha interpretação:

1. O operador nulo de coalescência - ??:

  • ??é como um "portão" que apenas permite a passagem de NULL .
  • Portanto, ele sempre retorna o primeiro parâmetro , a menos que seja o primeiro parâmetroNULL .
  • Isso significa que ??é o mesmo que( !isset() || is_null() )

2. O operador ternário - ?:

  • ?:é como um portão que deixa anything falsypassar - incluindoNULL
  • 0, empty string, NULL, false, !isset(), empty().. qualquer coisa que cheira Falsas
  • Assim como o operador ternário clássico: echo ($x ? $x : false)
  • NOTA: ?:lançará variáveis PHP NOTICEindefinidas ( unsetou !isset())

3. Então doutor, quando eu uso ??e ?:..

  • Só estou brincando - não sou médico e isso é apenas uma interpretação
  • Eu usaria ?:quando
    • fazendo empty($x)cheques
    • A operação ternária clássica como !empty($x) ? $x : $ypode ser reduzida para$x ?: $y
    • if(!$x) { fn($x); } else { fn($y); } pode ser reduzido para fn(($x ?: $y))
  • Eu usaria ??quando
    • Eu quero fazer uma !isset() || is_null()verificação
    • por exemplo, verifique se existe um objeto - $object = $object ?? new objClassName();

4. Operadores de empilhamento ...

  1. O operador ternário pode ser empilhado ...

    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

    Origem e crédito para este código

    Esta é basicamente uma sequência de:

    if( truthy ) {}
    else if(truthy ) {}
    else if(truthy ) {}
    ..
    else {}
  2. O operador de carvão nulo pode ser empilhado ...

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

    Esta é uma sequência de:

    if(!isset($x) || is_null($x) ) {} 
    else if(!isset($y) || is_null($y) ) {}
    else {}
  3. Usando o empilhamento, posso encurtar isso:

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

    Para isso:

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

    Legal certo? :-)

a20
fonte
3
De longe a melhor resposta
Faizan Anwer Ali Rupani
69

Se você usar o operador ternário de atalho como este, ele causará um aviso se $_GET['username']não estiver definido:

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

Então, em vez disso, você deve fazer algo assim:

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

O operador coalescente nulo é equivalente à instrução acima e retornará 'padrão' se $_GET['username']não estiver definido ou estiver null:

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

Observe que ele não verifica a veracidade . Ele verifica apenas se está definido e não é nulo.

Você também pode fazer isso e o primeiro valor definido (definido e não null) será retornado:

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

Agora esse é um operador coalescente adequado.

Andrew
fonte
42

A principal diferença é que

  1. A expressão Ternary Operatorexpr1 ?: expr3 retorna expr1se for expr1avaliada como, TRUEmas por outro lado, a expressão Null Coalescing Operator(expr1) ?? (expr2) avalia expr1se expr1for não for NULL

  2. O operador ternário expr1 ?: expr3 emite um aviso se o valor do lado esquerdo (expr1) não existir, mas, por outro lado, operador nulo de coalescência (expr1) ?? (expr2) Em particular, não emite um aviso se o valor do lado esquerdo (expr1) não existir, assim como isset().

  3. TernaryOperator é deixado associativo

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

    O operador de coalescência nulo é associativo correto

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

Agora vamos explicar a diferença entre por exemplo:

Operador ternário (?:)

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

Operador de coalescência nulo (??)

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

Aqui está a tabela que explica a diferença e semelhança entre '??'e?:

insira a descrição da imagem aqui

Nota especial: operador coalescente nulo e operador ternário é uma expressão, e que não é avaliado para uma variável, mas para o resultado de uma expressão. É importante saber se você deseja retornar uma variável por referência. A declaração retorna $ foo ?? $ bar; e retornar $ var == 42? $ a: $ b; em uma função de retorno por referência, portanto, não funcionará e um aviso será emitido.

Dhairya Lakhera
fonte
15

Ambos se comportam de maneira diferente quando se trata de manipulação dinâmica de dados.

Se a variável estiver vazia (''), a coalescência nula tratará a variável como verdadeira, mas o operador ternário abreviado não. E isso é algo a ter em mente.

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

E a saída:

1b
2b

2d
1f

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

Link: https://3v4l.org/ZBAa1

Chazy Chaz
fonte
Isso é claramente contra-intuitivo para PHP, onde uma string vazia é geralmente considerada falsa. No entanto, é claramente indicado nos documentos para ??: It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.
19619 Simon
12

Ambos são atalhos para expressões mais longas.

?:é a abreviação de $a ? $a : $b. Essa expressão será avaliada como $ a se $ a for avaliada como TRUE .

??é a abreviação de isset($a) ? $a : $b. Essa expressão será avaliada como $ a se $ a estiver definido e não nulo.

Seus casos de uso se sobrepõem quando $ a é indefinido ou nulo. Quando $ a é indefinido ??, não produz um E_NOTICE, mas os resultados são os mesmos. Quando $ a é nulo, o resultado é o mesmo.

Dean Or
fonte
5

Para iniciantes:

Operador coalescente nulo (??)

Tudo é verdade, exceto nullvalores e indefinidos (atributos de índice de variável / matriz / objeto)

ex:

$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"

isso é basicamente verificar se a variável (índice do array, atributo do objeto etc.) existe e não existe null. semelhante à issetfunção

Taquigrafia do operador ternário (? :)

cada coisas falsas ( false, null, 0, string vazia) são vir como falsa, mas se é um indefinido também vir como falsa, mas Noticevai jogar

ex

$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: ..

Espero que isto ajude

Supun Praneeth
fonte
4

Role para baixo neste link e veja a seção, ele fornece um exemplo comparativo, como visto abaixo:

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

No entanto, não é aconselhável encadear os operadores, pois dificulta a compreensão do código ao lê-lo posteriormente.

O operador coalescente nulo (??) foi adicionado como açúcar sintático para o caso comum de precisar usar um ternário em conjunto com isset (). Retorna seu primeiro operando se existir e não for NULL; caso contrário, ele retornará seu segundo operando.

Essencialmente, o uso do operador coalescente tornará a verificação automática de nulo, diferente do operador ternário.

Script47
fonte
11
Por favor, não consideram encadeamento ... é tão difícil de ler / entender como ternários acorrentados
Mark Baker
7
Os ternários encadeados @MarkBaker são difíceis de entender porque o PHP quebrou a associatividade ternária. Isso não se aplica ao operador de coalescência e a coalescência encadeada é perfeitamente compreensível.
NikiC 3/16
7
Discordo. O encadeamento da coalescência nula é um ótimo recurso e não dificulta a leitura se você entender o operador. É comumente usado em javascript e, quando as pessoas se acostumam com ele no PHP, essa chamada para não usar encadeamento deve parar. O encadeamento de ternários é muito difícil de ler, mas a coalescência nula é fácil. Enquanto você lê da esquerda para a direita, apenas lista qual valor deve ser usado a seguir.
283:
2
Este é muito parecido com o comum a || b || cpadrão em JS, exceto PHP do pode ser usado para booleans ( false || 2em JS é 2; false ?? 2em PHP é false)
fregante
11
Discordo de você e de outras pessoas em relação a não usar encadeamento. É como dizer que nunca use para loops porque pode não entendê-los. Os desenvolvedores / codificadores são perfeitamente livres para usar padrões e práticas de codificação que eles entendem, mesmo que outros não. Pessoalmente, considero a coalescência encadeada muito semelhante às declarações de switch. Retorna o primeiro valor encontrado (definido) e o último valor se nada for encontrado.
precisa saber é o seguinte
3

As outras respostas são profundas e dão ótimas explicações. Para quem procura uma resposta rápida,

$a ?: 'fallback' é $a ? $a : 'fallback'

enquanto

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


A principal diferença seria quando o operador esquerdo é:

  • Um valor Falsas que não é nula ( 0, '', false, [], ...)
  • Uma variável indefinida
Yaron U.
fonte
Não deve haver $a =na expansão acima de ??. $a ?? 'fallback' não define nem altera o valor de $ a. (Ele apenas retorna um valor).
Doin
2

Parece que existem prós e contras no uso de um ??ou outro ?:. O profissional a usar ?:é que ele avalia falso e nulo e "" o mesmo. O engodo é que ele relata um E_NOTICE se o argumento anterior for nulo. Com ??o profissional, não há E_NOTICE, mas o contrário é que ele não avalia falso e nulo da mesma forma. Na minha experiência, vi pessoas começarem a usar nulo e falso de forma intercambiável, mas depois acabaram recorrendo a modificar seu código para ser consistente com o uso de nulo ou falso, mas não ambos. Uma alternativa é criar uma condição ternária mais elaborada: (isset($something) or !$something) ? $something : $something_else.

A seguir, é apresentado um exemplo da diferença de usar o ??operador usando null e false:

$false = null;
$var = $false ?? "true";
echo $var . "---<br>";//returns: true---

$false = false;
$var = $false ?? "true";
echo $var . "---<br>"; //returns: ---

Ao elaborar o operador ternário, no entanto, podemos fazer com que uma string "" falsa ou vazia se comporte como se fosse um nulo sem gerar um 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---

Pessoalmente, acho que seria muito bom se uma futura revisão do PHP incluísse outro novo operador: :?que substituísse a sintaxe acima. ou seja: // $var = $false :? "true";Essa sintaxe avaliaria nulo, falso e "" igualmente e não lançaria um E_NOTICE ...

Damian Green
fonte
3
você pode usar $ var = $ false ?? null?: "A string está vazia / falsa / nula / indefinida";
RedSparr0w 13/09/19
Whoa ... a ?? null ?:coisa é incrível, obrigado Sr. cara inteligente.
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.
Čamo
fonte
0

Null Coalescing operatorexecuta apenas duas tarefas: verifica whether the variable is sete whether it is null. Veja o seguinte exemplo:

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

O exemplo de código acima afirma que Null Coalescing operatortrata uma variável inexistente e uma variável que é definida NULLda mesma maneira.

Null Coalescing operatoré uma melhoria sobre o ternary operator. Dê uma olhada no seguinte trecho de código comparando os dois:

<?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.

Portanto, a diferença entre os dois é que o Null Coalescing operatoroperador foi projetado para lidar com variáveis ​​indefinidas melhor que o ternary operator. Visto que o ternary operatoré uma abreviação de if-else.

Null Coalescing operatornão pretende substituir ternary operator, mas em alguns casos de uso, como no exemplo acima, permite escrever código limpo com menos problemas.

Créditos: http://dwellupper.io/post/6/php7-null-coalescing-operator-usage-and-examples

Pranav Rana
fonte
isset($_POST['fullname'])já verifica NULLvalores - portanto, && !is_null($_POST['fullname'])no primeiro exemplo, é redundante de qualquer maneira #
Yaron U.
0

Ao usar superglobais como $ _GET ou $ _REQUEST, você deve estar ciente de que eles podem ser uma sequência vazia. Neste caso especal, este exemplo

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

falhará porque o valor de $ username agora é uma string vazia.

Portanto, ao usar $ _GET ou mesmo $ _REQUEST, você deve usar o operador ternário da seguinte maneira:

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

Agora, o valor de $ username é 'ninguém' conforme o esperado.

Alexander Behling
fonte
Boa pegada. Além disso, o operador coalescente também falhará no caso de uma string vazia.
Choxx