Operador ternario PHP vs operador de fusión nula

341

¿Alguien puede explicar las diferencias entre el operador ternario shorthand ( ?:) y el operador de fusión nulo ( ??) en PHP?

¿Cuándo se comportan de manera diferente y de la misma manera (si eso sucede)?

$a ?: $b

VS.

$a ?? $b
balping
fuente

Respuestas:

344

Cuando su primer argumento es nulo, son básicamente los mismos, excepto que la fusión nula no dará salida E_NOTICEcuando tenga una variable indefinida. Los documentos de migración de PHP 7.0 tienen esto que decir:

El operador de fusión nulo (??) se ha agregado como azúcar sintáctico para el caso común de necesitar usar un ternario junto con isset (). Devuelve su primer operando si existe y no es NULL; de lo contrario, devuelve su segundo operando.

Aquí hay un código de ejemplo para demostrar esto:

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

Las líneas que tienen el aviso son aquellas en las que estoy usando el operador ternario abreviado en lugar del operador de fusión nula. Sin embargo, incluso con el aviso, PHP devolverá la misma respuesta.

Ejecute el código: https://3v4l.org/McavC

Por supuesto, esto siempre supone que el primer argumento es null. Una vez que ya no es nulo, termina con diferencias en que el ??operador siempre devolverá el primer argumento, mientras que la ?:taquigrafía solo lo haría si el primer argumento fuera verdadero, y eso se basa en cómo PHP convertiría las cosas en un booleano .

Entonces:

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

entonces debería $aser igual falsee $bigual a 'g'.

MasterOdin
fuente
8
Consejo: si has estado usando ?? en lugar de?: pero luego necesita hacer que su código sea compatible con versiones de PHP anteriores a 7 (para un complemento para ex), entonces es posible que desee cambiar el ?? con isset ($ algo)? $ something: $ something_else en todas partes en su código. Puede hacerlo fácilmente con Notepad ++ o nedit (y otros editores también) utilizando la herramienta de búsqueda / reemplazo, seleccionando la opción de expresión regular e insertando en el campo de búsqueda: "\ s * (\ S +) \ s * \? \?" y en el campo de reemplazo: "isset ($ 1)? $ 1:" sin las comillas (nedit usa \ 1 en lugar de $ 1). Luego reemplace todo.
Damian Green
14
Esta es la respuesta correcta, sin embargo, la verificación de la veracidad es la principal diferencia y debe enfatizarse más.
mancze
2
@MasterOdin No está satisfecho con su respuesta. Ambos no son lo mismo. Tener un resultado diferente.
Curioso
1
Vale la pena señalar que puedes usar? Con encadenamiento. Por ejemplo: $b = []; var_dump($b['a']['b']['c'] ?? 'default');o con objetos$b = new Foo; var_dump($b->a()->b()->c() ?? 'default');
Jack B el
Tenga en cuenta que el comportamiento también es diferente con $a = [];. Ver: 3v4l.org/iCCa0
Soullivaneuh
75

Ejecuté el siguiente en modo interactivo php ( php -aen terminal). El comentario en cada línea muestra el 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"

Entonces esta es mi interpretación:

1. El operador de fusión nula - ??:

  • ??es como una "puerta" que solo deja pasar NULL .
  • Por lo tanto, siempre devuelve el primer parámetro , a menos que el primer parámetro seaNULL .
  • Esto significa que ??es lo mismo que( !isset() || is_null() )

2. El operador ternario - ?:

  • ?:es como una puerta que deja anything falsypasar, incluyendoNULL
  • 0, empty string, NULL, false, !isset(), empty().. todo lo que huele Falsy
  • Al igual que el operador ternario clásico: echo ($x ? $x : false)
  • NOTA: ?:arrojará variables PHP NOTICEindefinidas ( unseto !isset())

3. Entonces doctor, ¿cuándo uso ??y ?:...

  • Solo estoy bromeando, no soy médico y esto es solo una interpretación
  • Usaría ?:cuando
    • haciendo empty($x)controles
    • La operación ternaria clásica como !empty($x) ? $x : $yse puede acortar a$x ?: $y
    • if(!$x) { fn($x); } else { fn($y); } se puede acortar a fn(($x ?: $y))
  • Usaría ??cuando
    • Quiero hacer un !isset() || is_null()cheque
    • Por ejemplo, compruebe si existe un objeto. $object = $object ?? new objClassName();

4. Operadores de apilamiento ...

  1. El operador ternario se puede apilar ...

    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

    Fuente y crédito para este código

    Esto es básicamente una secuencia de:

    if( truthy ) {}
    else if(truthy ) {}
    else if(truthy ) {}
    ..
    else {}
  2. El operador nulo coalese se puede apilar ...

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

    Esta es una secuencia de:

    if(!isset($x) || is_null($x) ) {} 
    else if(!isset($y) || is_null($y) ) {}
    else {}
  3. Usando el apilamiento, puedo acortar esto:

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

    A esto:

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

    ¿Guay, verdad? :-)

a20
fuente
3
Con mucho, la mejor respuesta
Faizan Anwer Ali Rupani
69

Si utiliza el operador ternario de acceso directo de esta manera, generará un aviso si $_GET['username']no está configurado:

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

Entonces, en cambio, tienes que hacer algo como esto:

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

El operador de fusión nulo es equivalente a la declaración anterior y devolverá 'default' si $_GET['username']no está configurado o es null:

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

Tenga en cuenta que no verifica la veracidad . Comprueba solo si está configurado y no es nulo.

También puede hacer esto, y se devolverá el primer valor definido (establecido y no null):

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

Ahora que es un operador de fusión apropiado.

Andrés
fuente
42

La principal diferencia es que

  1. La expresión del operador ternarioexpr1 ?: expr3 devuelve expr1si se expr1evalúa TRUEpero, por otro lado, la expresión del operador de fusión nula se(expr1) ?? (expr2) evalúa expr1si noexpr1 es NULL

  2. El Operador ternario expr1 ?: expr3 emite un aviso si el valor del lado izquierdo (expr1) no existe pero, por otro lado, el Operador de fusión nula (expr1) ?? (expr2) En particular, no emite un aviso si el valor del lado izquierdo (expr1) no existe, al igual que isset().

  3. TernaryOperator se deja asociativo

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

    El operador de fusión nula es asociativo correcto

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

Ahora vamos a explicar la diferencia entre por ejemplo:

Operador ternario (?:)

$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 fusión nula (??)

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

Aquí está la tabla que explica la diferencia y similitud entre '??'y?:

ingrese la descripción de la imagen aquí

Nota especial: el operador de fusión nulo y el operador ternario es una expresión, y que no evalúa una variable, sino el resultado de una expresión. Esto es importante para saber si desea devolver una variable por referencia. La declaración devuelve $ foo ?? $ bar; y devolver $ var == 42? $ a: $ b; en una función de retorno por referencia, por lo tanto, no funcionará y se emitirá una advertencia.

Dhairya Lakhera
fuente
15

Ambos se comportan de manera diferente cuando se trata del manejo dinámico de datos.

Si la variable está vacía (''), la fusión nula tratará la variable como verdadera pero el operador ternario abreviado no lo hará. Y eso es algo a tener en cuenta.

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

Y la salida:

1b
2b

2d
1f

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

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

Chazy Chaz
fuente
Esto es claramente contra intuitivo para PHP, donde una cadena vacía generalmente se considera falsa. Sin embargo, está claramente indicado en los documentos para ??: It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.
Simon
12

Ambos son shorthands para expresiones más largas.

?:es la abreviatura de $a ? $a : $b. Esta expresión se evaluará a $ a si $ a se evalúa como VERDADERO .

??es la abreviatura de isset($a) ? $a : $b. Esta expresión se evaluará a $ a si $ a está establecido y no es nulo.

Sus casos de uso se superponen cuando $ a no está definido o es nulo. Cuando $ a no está definido ??, no se producirá un E_NOTICE, pero los resultados son los mismos. Cuando $ a es nulo, el resultado es el mismo.

Dean Or
fuente
5

Para los principiantes:

Operador de fusión nula (??)

Todo es verdadero excepto los nullvalores y los indefinidos (variable / índice de matriz / atributos de 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"

esto es básicamente verificar que la variable (índice de matriz, atributo de objeto, etc.) exista y no exista null. Similar aisset función

Taquigrafía del operador ternario (? :)

cada falsas cosas ( false, null, 0, cadena vacía) se vienen como falsa, pero si se trata de un indefinido sino que también vienen como falsa, pero Noticetirarán

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 esto ayude

Supun Praneeth
fuente
4

Desplácese hacia abajo en este enlace y vea la sección, le da un ejemplo comparativo como se ve a continuación:

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

Sin embargo, no se recomienda encadenar a los operadores ya que dificulta la comprensión del código al leerlo más adelante.

El operador de fusión nulo (??) se ha agregado como azúcar sintáctico para el caso común de necesitar usar un ternario junto con isset (). Devuelve su primer operando si existe y no es NULL; de lo contrario, devuelve su segundo operando.

Esencialmente, el uso del operador coalescente hará que la comprobación automática sea nula, a diferencia del operador ternario.

Script47
fuente
1
Por favor, no considere encadenar ... es tan difícil de leer / entender como los terrarios encadenados
Mark Baker
77
@MarkBaker Los terrarios encadenados son difíciles de entender porque PHP ha roto la asociatividad ternaria. Esto no se aplica al operador de fusión y la fusión integrada es perfectamente comprensible.
NikiC
77
Estoy en desacuerdo. Encadenar la fusión nula es una gran característica, y no hace que sea difícil de leer si entiendes al operador. Se usa comúnmente en JavaScript y una vez que la gente se sienta cómoda con él en PHP, esta llamada para no usar el encadenamiento debería detenerse. Encadenar ternaries es muy difícil de leer, pero la fusión nula es fácil. A medida que lea de izquierda a derecha, solo enumera qué valor debe usarse a continuación.
earl3s
2
Esto se parece mucho al a || b || cpatrón común en JS, excepto que PHP puede usarse para booleanos ( false || 2en JS es 2; false ?? 2en PHP es falso)
Fregante
1
No estoy de acuerdo con usted y otros con respecto a no usar el encadenamiento. Es como decir que nunca lo uses para bucles porque puede que no los entiendas. Los desarrolladores / codificadores son perfectamente libres de usar estándares y prácticas de codificación que entienden, incluso si otros no lo hacen. Personalmente, veo la fusión encadenada como muy similar a las declaraciones de cambio. Devuelve el primer valor que se encuentra (conjunto) y el último valor si no se encuentra nada.
kurdtpage
3

Las otras respuestas son profundas y dan grandes explicaciones. Para aquellos que buscan una respuesta rápida,

$a ?: 'fallback' es $a ? $a : 'fallback'

mientras

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


La principal diferencia sería cuando el operador izquierdo es:

  • Un valor Falsy que no es nulo ( 0, '', false, [], ...)
  • Una variable indefinida
Yaron U.
fuente
No debería haber $a =en la expansión anterior de ??. $a ?? 'fallback' no establece ni cambia el valor de $ a. (Simplemente devuelve un valor).
Doin
2

Parece que hay ventajas y desventajas de usar cualquiera ??o ?:. La ventaja de usar ?:es que evalúa falso y nulo y "" lo mismo. La desventaja es que informa un E_NOTICE si el argumento anterior es nulo. Con ??el pro es que no hay E_NOTICE, pero la desventaja es que no evalúa falso y nulo. En mi experiencia, he visto personas que comienzan a usar nulo y falso de manera intercambiable, pero luego eventualmente recurren a modificar su código para que sea consistente con el uso de nulo o falso, pero no ambos. Una alternativa es crear una condición ternaria más elaborada:(isset($something) or !$something) ? $something : $something_else .

El siguiente es un ejemplo de la diferencia de usar el ??operador usando nulo y falso:

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

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

Sin embargo, al elaborar el operador ternario, podemos hacer que una cadena "" falsa o vacía "se comporte como si fuera nula sin arrojar un aviso_e:

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

Personalmente, creo que sería realmente bueno si una futura revisión de PHP incluyera otro operador nuevo: :?que reemplazara la sintaxis anterior. es decir: // $var = $false :? "true";esa sintaxis evaluaría nulo, falso e "" por igual y no arrojaría una E_NOTICE ...

Damian Green
fuente
3
puedes usar $ var = $ false ?? null?: "La cadena está vacía / falsa / nula / indefinida";
RedSparr0w
Whoa ... la ?? null ?:cosa es increíble, gracias señor. chico 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
fuente
0

Null Coalescing operatorrealiza solo dos tareas: comprueba whether the variable is sety whether it is null. Eche un vistazo al siguiente ejemplo:

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

El ejemplo de código anterior establece que Null Coalescing operatortrata una variable no existente y una variable que se establece de NULLla misma manera.

Null Coalescing operatores una mejora sobre el ternary operator. Eche un vistazo al siguiente fragmento de código que compara los dos:

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

Entonces, la diferencia entre los dos es que el Null Coalescing operatoroperador está diseñado para manejar variables indefinidas mejor que el ternary operator. Mientras que, ternary operatores una abreviatura de if-else.

Null Coalescing operatorno está destinado a reemplazar ternary operator, pero en algunos casos de uso, como en el ejemplo anterior, le permite escribir código limpio con menos problemas.

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

Pranav Rana
fuente
isset($_POST['fullname'])ya comprueba los NULLvalores, por lo que && !is_null($_POST['fullname'])el primer ejemplo es redundante de todos modos
Yaron U.
0

Cuando utilice las superglobales como $ _GET o $ _REQUEST, debe tener en cuenta que podrían ser una cadena vacía. En este caso especial este ejemplo

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

fallará porque el valor de $ username ahora es una cadena vacía.

Por lo tanto, cuando use $ _GET o incluso $ _REQUEST, debe usar el operador ternario como este:

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

Ahora el valor de $ username es 'nobody' como se esperaba.

Alexander Behling
fuente
Buena atrapada. Además, el operador de fusión también fallará en caso de una cadena vacía.
Choxx