Opérateur ternaire PHP vs opérateur coalescent nul


342

Quelqu'un peut-il expliquer les différences entre l'opérateur ternaire raccourci ( ?:) et l'opérateur coalescent nul ( ??) en PHP?

Quand se comportent-ils différemment et de la même manière (si cela se produit même)?

$a ?: $b

CONTRE.

$a ?? $b

Réponses:


345

Lorsque votre premier argument est nul, ils sont fondamentalement les mêmes, sauf que la fusion nulle ne produira pas E_NOTICElorsque vous avez une variable non définie. La documentation de migration PHP 7.0 a ceci à dire:

L'opérateur de coalescence nul (??) a été ajouté en tant que sucre syntaxique dans le cas courant de la nécessité d'utiliser un ternaire en conjonction avec isset (). Il renvoie son premier opérande s'il existe et n'est pas NULL; sinon, elle renvoie son deuxième opérande.

Voici un exemple de code pour illustrer cela:

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

Les lignes qui ont l'avis sont celles où j'utilise l'opérateur ternaire raccourci par opposition à l'opérateur coalescent nul. Cependant, même avec l'avis, PHP donnera la même réponse.

Exécutez le code: https://3v4l.org/McavC

Bien sûr, cela suppose toujours que le premier argument est null. Une fois qu'il n'est plus nul, vous vous retrouvez avec des différences dans la mesure où l' ??opérateur renvoie toujours le premier argument tandis que la ?:sténographie ne le fait que si le premier argument est véridique, et cela dépend de la façon dont PHP transtyperait les choses en booléen .

Alors:

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

aurait alors $aété égal falseet $bégal à 'g'.


8
Astuce: si vous utilisez ?? au lieu de?: mais vous devez alors rendre votre code compatible avec les versions PHP antérieures à 7 (pour un plugin par exemple), alors vous voudrez peut-être échanger le ?? avec isset ($ quelque chose)? $ quelque chose: $ quelque chose_else partout dans votre code. Vous pouvez facilement le faire avec Notepad ++ ou nedit (et d'autres éditeurs également) en utilisant l'outil de recherche / remplacement, en sélectionnant l'option d'expression régulière et en insérant dans le champ de recherche: "\ s * (\ S +) \ s * \? \?" et dans le champ de remplacement: "isset ($ 1)? $ 1:" sans les guillemets (nedit utilise \ 1 au lieu de $ 1). Remplacez ensuite tout.
Damian Green

14
C'est la bonne réponse, mais la vérification de la véracité est la principale différence et devrait être davantage soulignée.
mancze

2
@MasterOdin Pas satisfait de votre réponse. Les deux ne sont pas identiques. Ont un résultat différent.
Curieux

1
Il convient de noter que vous pouvez utiliser ?? avec enchaînement. Par exemple: $b = []; var_dump($b['a']['b']['c'] ?? 'default');ou avec des objets$b = new Foo; var_dump($b->a()->b()->c() ?? 'default');
Jack B

Veuillez noter que le comportement est également différent avec $a = [];. Voir: 3v4l.org/iCCa0
Soullivaneuh

75

Exécuté ci-dessous en mode interactif php ( php -asur le terminal). Le commentaire sur chaque ligne montre le résultat.

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"

Voici donc mon interprétation:

1. L'opérateur de coalescence nul - ??:

  • ??est comme une "porte" qui ne laisse passer que NULL .
  • Ainsi, il retourne toujours le premier paramètre , sauf si le premier paramètre se trouve êtreNULL .
  • Cela signifie la ??même chose que( !isset() || is_null() )

2. L'opérateur ternaire - ?:

  • ?:est comme une porte qui laisse anything falsypasser - y comprisNULL
  • 0, empty string, NULL, false, !isset(), empty().. tout ce qui sent falsy
  • Tout comme l'opérateur ternaire classique: echo ($x ? $x : false)
  • REMARQUE: ?:lancera des variables PHP NOTICEnon définies ( unsetou !isset())

3. Alors docteur, quand dois-je utiliser ??et ?:..

  • Je plaisante - je ne suis pas médecin et ce n'est qu'une interprétation
  • J'utiliserais ?:quand
    • faire des empty($x)vérifications
    • Opération ternaire classique comme !empty($x) ? $x : $ypeut être raccourcie à$x ?: $y
    • if(!$x) { fn($x); } else { fn($y); } peut être raccourci en fn(($x ?: $y))
  • J'utiliserais ??quand
    • Je veux faire un !isset() || is_null()chèque
    • par exemple vérifier si un objet existe - $object = $object ?? new objClassName();

4. Opérateurs d'empilage ...

  1. L'opérateur ternaire peut être empilé ...

    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

    Source et crédit pour ce code

    Il s'agit essentiellement d'une séquence de:

    if( truthy ) {}
    else if(truthy ) {}
    else if(truthy ) {}
    ..
    else {}
  2. Null Coalese Operator peut être empilé ...

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

    Il s'agit d'une séquence de:

    if(!isset($x) || is_null($x) ) {} 
    else if(!isset($y) || is_null($y) ) {}
    else {}
  3. En utilisant l'empilement, je peux raccourcir ceci:

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

    Pour ça:

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

    Cool, non? :-)


3
De loin la meilleure réponse
Faizan Anwer Ali Rupani

69

Si vous utilisez l'opérateur de raccourci ternaire comme celui-ci, il provoquera un avis s'il $_GET['username']n'est pas défini:

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

Au lieu de cela, vous devez faire quelque chose comme ceci:

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

L' opérateur de coalescence nul est équivalent à l'instruction ci-dessus, et retournera 'par défaut' s'il $_GET['username']n'est pas défini ou est null:

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

Notez qu'il ne vérifie pas la véracité . Il vérifie uniquement s'il est défini et non nul.

Vous pouvez également le faire, et la première valeur définie (définie et non null) sera retournée:

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

Maintenant, c'est un bon opérateur de coalescence.


43

La principale différence est que

  1. L' expression d' opérateur ternaireexpr1 ?: expr3 retourne expr1si expr1évalue à TRUEmais d'autre part l' expression d' opérateur de coalescence nulle est(expr1) ?? (expr2) évaluée à expr1si expr1n'est pas NULL

  2. L'opérateur ternaire expr1 ?: expr3 émet une notification si la valeur du côté gauche (expr1) n'existe pas mais, d'autre part, l' opérateur de coalescence nulle (expr1) ?? (expr2) en particulier, n'émet pas de notification si la valeur du côté gauche (expr1) n'existe pas, tout comme isset().

  3. TernaryOperator est laissé associatif

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

    Null Coalescing Operator est à droite associative

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

Expliquons maintenant la différence entre par exemple:

Opérateur ternaire (?:)

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

Opérateur de coalescence nul (??)

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

Voici le tableau qui explique la différence et la similitude entre '??'et?:

entrez la description de l'image ici

Remarque spéciale: l'opérateur de coalescence nul et l'opérateur ternaire est une expression, et il n'évalue pas une variable, mais le résultat d'une expression. Il est important de savoir si vous souhaitez renvoyer une variable par référence. L'instruction renvoie $ foo ?? $ bar; et retourner $ var == 42? $ a: $ b; dans une fonction de retour par référence ne fonctionnera donc pas et un avertissement est émis.


15

Les deux se comportent différemment en ce qui concerne la gestion dynamique des données.

Si la variable est vide (''), la coalescence nulle traitera la variable comme vraie, mais l'opérateur ternaire abrégé ne le fera pas. Et c'est quelque chose à avoir en tête.

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

Et la sortie:

1b
2b

2d
1f

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

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


C'est clairement contre-intuitif pour PHP, où une chaîne vide est généralement considérée comme fausse. Pourtant, il est clairement indiqué dans les documents pour ??: It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.
Simon

12

Les deux sont des raccourcis pour des expressions plus longues.

?:est l'abréviation de $a ? $a : $b. Cette expression sera évaluée à $ a si $ a est évaluée à TRUE .

??est l'abréviation de isset($a) ? $a : $b. Cette expression sera évaluée à $ a si $ a est définie et non nulle.

Leurs cas d'utilisation se chevauchent lorsque $ a n'est pas défini ou est nul. Lorsque $ a n'est pas défini ??, aucun E_NOTICE ne sera produit, mais les résultats seront les mêmes. Lorsque $ a est nul, le résultat est le même.


5

Pour les débutants:

Opérateur coalescent nul (??)

Tout est vrai sauf les nullvaleurs et indéfinies (variable / index de tableau / attributs d'objet)

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"

il s'agit essentiellement de vérifier que la variable (index du tableau, attribut d'objet .. etc.) existe et non null. similaire à la issetfonction

Raccourci opérateur ternaire (? :)

toutes choses fausses ( false, null, 0, chaîne vide) sont venus comme faux, mais si elle est un non défini VIENS aussi faux , mais Noticejetteront

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

J'espère que cela t'aides


4

Faites défiler vers le bas sur ce lien et consultez la section, il vous donne un exemple comparatif comme indiqué ci-dessous:

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

Cependant, il n'est pas conseillé de chaîner les opérateurs car cela rend plus difficile la compréhension du code lors de sa lecture ultérieure.

L'opérateur de coalescence nul (??) a été ajouté en tant que sucre syntaxique dans le cas courant de la nécessité d'utiliser un ternaire en conjonction avec isset (). Il renvoie son premier opérande s'il existe et n'est pas NULL; sinon, elle renvoie son deuxième opérande.

Essentiellement, l'utilisation de l'opérateur de fusion fera en sorte qu'il vérifie automatiquement la valeur null contrairement à l'opérateur ternaire.


1
S'il vous plaît ne pensez pas à enchaîner ... c'est aussi difficile à lire / à comprendre que les ternaires enchaînés
Mark Baker

7
Les ternaires chaînés @MarkBaker sont difficiles à comprendre car PHP a rompu l'associativité ternaire. Cela ne s'applique pas à l'opérateur de coalescence et la coalescence imho enchaînée est parfaitement compréhensible.
NikiC

7
Je ne suis pas d'accord. Le chaînage de la fusion nulle est une fonctionnalité intéressante, et il n'est pas difficile à lire si vous comprenez l'opérateur. Il est couramment utilisé en javascript et une fois que les utilisateurs s'y seront mis à l'aise en PHP, cet appel à ne pas utiliser le chaînage devrait cesser. Le chaînage des ternaires est très difficile à lire, mais la fusion nulle est facile. Lorsque vous lisez de gauche à droite, il répertorie simplement la valeur à utiliser ensuite.
earl3s du

2
Cela ressemble beaucoup au a || b || cmodèle courant dans JS, sauf que PHP peut être utilisé pour les booléens ( false || 2dans JS est 2; false ?? 2dans PHP est faux)
fregante

1
Je suis en désaccord avec vous et les autres concernant la non-utilisation du chaînage. C'est comme dire de ne jamais utiliser de boucles, car elles pourraient ne pas les comprendre. Les développeurs / codeurs sont parfaitement libres d'utiliser des normes et des pratiques de codage qu'ils comprennent, même si d'autres ne le font pas. Personnellement, je considère la coalescence chaînée comme très similaire aux instructions switch. Il renvoie la première valeur trouvée (définie) et la dernière valeur si rien n'est trouvé.
kurdtpage

3

Les autres réponses vont en profondeur et donnent de grandes explications. Pour ceux qui recherchent une réponse rapide,

$a ?: 'fallback' est $a ? $a : 'fallback'

tandis que

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


La principale différence serait lorsque l'opérateur de gauche est:

  • Une valeur qui est falsy pas nulle ( 0, '', false, [], ...)
  • Une variable indéfinie

Il ne devrait pas y avoir d' $a =expansion ci-dessus ??. $a ?? 'fallback' ne définit ni ne modifie la valeur de $ a. (Il renvoie simplement une valeur).
Doin

2

Il semble qu'il y ait des avantages et des inconvénients à utiliser l'un ??ou l' autre ?:. Le pro de l 'utilisation ?:est qu'il évalue faux et nul et "" pareil. L'inconvénient est qu'il signale un E_NOTICE si l'argument précédent est nul. Avec ??le pro c'est qu'il n'y a pas d'E_NOTICE, mais le con c'est qu'il n'évalue pas faux et nul pareil. D'après mon expérience, j'ai vu des gens commencer à utiliser null et false de manière interchangeable, mais ils ont finalement recours à la modification de leur code pour être cohérent avec l'utilisation de null ou false, mais pas les deux. Une alternative est de créer une condition ternaire plus élaborée: (isset($something) or !$something) ? $something : $something_else.

Voici un exemple de la différence d'utilisation de l' ??opérateur en utilisant à la fois null et false:

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

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

Cependant, en développant l'opérateur ternaire, nous pouvons faire en sorte qu'une chaîne "" vide ou fausse se comporte comme si elle était nulle sans lancer un 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---

Personnellement, je pense que ce serait vraiment bien si une future version de PHP incluait un autre nouvel opérateur: :?qui remplacerait la syntaxe ci-dessus. ie: // $var = $false :? "true";Cette syntaxe évaluerait null, false et "" également et ne lancerait pas E_NOTICE ...


3
vous pouvez utiliser $ var = $ false ?? null?: "La chaîne est vide / fausse / nulle / non définie";
RedSparr0w

Whoa ... la ?? null ?:chose est assez génial, merci mr. gars intelligent.
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 operatoreffectue seulement deux tâches: il vérifie whether the variable is setet whether it is null. Jetez un œil à l'exemple suivant:

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

L'exemple de code ci-dessus indique que Null Coalescing operatortraite une variable non existante et une variable qui est définie sur NULLde la même manière.

Null Coalescing operatorest une amélioration par rapport à la ternary operator. Jetez un œil à l'extrait de code suivant comparant les deux:

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

Ainsi, la différence entre les deux est que l' Null Coalescing operatoropérateur est conçu pour gérer les variables non définies mieux que le ternary operator. Alors que, ternary operatorc'est un raccourci pour if-else.

Null Coalescing operatorn'est pas destiné à remplacer ternary operator, mais dans certains cas d'utilisation comme dans l'exemple ci-dessus, il vous permet d'écrire du code propre avec moins de tracas.

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


isset($_POST['fullname'])vérifie déjà les NULLvaleurs - donc le && !is_null($_POST['fullname'])premier exemple est de toute façon redondant
Yaron U.

0

Lorsque vous utilisez les superglobaux comme $ _GET ou $ _REQUEST, vous devez savoir qu'ils peuvent être une chaîne vide. Dans ce cas particulier, cet exemple

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

échouera car la valeur de $ username est maintenant une chaîne vide.

Ainsi, lorsque vous utilisez $ _GET ou même $ _REQUEST, vous devez utiliser l'opérateur ternaire à la place comme ceci:

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

Maintenant, la valeur de $ username est «personne» comme prévu.


Bonne prise. De plus, coalescing-operator échouera également en cas de chaîne vide.
Choxx
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.