PHP ternary operator vs null coalescing operator

2019-01-01 12:19发布

问题:

Can someone explain the differences between ternary operator shorthand (?:) and null coalescing operator (??) in PHP?

When do they behave differently and when in the same way (if that even happens)?

$a ?: $b

VS.

$a ?? $b

回答1:

When your first argument is null, they\'re basically the same except that the null coalescing won\'t output an E_NOTICE when you have an undefined variable. The PHP 7.0 migration docs has this to say:

The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.

Here\'s some example code to demonstrate this:

<?php

$a = null;

print $a ?? \'b\';
print \"\\n\";

print $a ?: \'b\';
print \"\\n\";

print $c ?? \'a\';
print \"\\n\";

print $c ?: \'a\';
print \"\\n\";

$b = array(\'a\' => null);

print $b[\'a\'] ?? \'d\';
print \"\\n\";

print $b[\'a\'] ?: \'d\';
print \"\\n\";

print $b[\'c\'] ?? \'e\';
print \"\\n\";

print $b[\'c\'] ?: \'e\';
print \"\\n\";

And it\'s output:

b
b
a

Notice: Undefined variable: c in /in/apAIb on line 14
a
d
d
e

Notice: Undefined index: c in /in/apAIb on line 33
e

The lines that have the notice are the ones where I\'m using the shorthand ternary operator as opposed to the null coalescing operator. However, even with the notice, PHP will give the same response back.

Execute the code: https://3v4l.org/McavC

Of course, this is always assuming the first argument is null. Once it\'s no longer null, then you end up with differences in that the ?? operator would always return the first argument while the ?: shorthand would only if the first argument was truthy, and that relies on how PHP would type-cast things to a boolean.

So:

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

would then have $a be equal to false and $b equal to \'g\'.



回答2:

If you use the shortcut ternary operator like this, it will cause a notice if $_GET[\'username\'] is not set:

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

So instead you have to do something like this:

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

The null coalescing operator is equivalent to the above statement, and will return \'default\' if $_GET[\'username\'] is not set or is null:

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

Note that it does not check truthiness. It checks only if it is set and not null.

You can also do this, and the first defined (set and not null) value will be returned:

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

Now that is a proper coalescing operator.



回答3:

The major difference is that

  1. Ternary Operator expression expr1 ?: expr3 returns expr1 if expr1 evaluates to TRUE but on the other hand Null Coalescing Operator expression (expr1) ?? (expr2) evaluates to expr1 if expr1 is not NULL

  2. Ternary Operator expr1 ?: expr3 emit a notice if the left-hand side value (expr1) does not exist but on the other hand Null Coalescing Operator (expr1) ?? (expr2) In particular, does not emit a notice if the left-hand side value (expr1) does not exist, just like isset().

  3. TernaryOperator is left associative

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

    Null Coalescing Operator is right associative

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

Now lets explain the difference between by example :

Ternary Operator (?:)

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

Here is the table that explain the difference and similarity between \'??\' and ?:

\"enter

Special Note : null coalescing operator and ternary operator is an expression, and that it doesn\'t evaluate to a variable, but to the result of an expression. This is important to know if you want to return a variable by reference. The statement return $foo ?? $bar; and return $var == 42 ? $a : $b; in a return-by-reference function will therefore not work and a warning is issued.



回答4:

Ran the below on php interactive mode (php -a on terminal). The comment on each line shows the result.

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

So this is my interpretation:

1. The Null Coalescing Operator - ??:

  • ?? is like a \"gate\" that only lets NULL through.
  • So, it always returns first parameter, unless first parameter happens to be NULL.
  • This means ?? is same as ( !isset() || is_null() )

2. The Ternary Operator - ?:

  • ?: is like a gate that lets anything falsy through - including NULL
  • 0, empty string, NULL, false, !isset(), empty() .. anything that smells falsy
  • Just like the classic ternary operator: echo ($x ? $x : false)
  • NOTE: ?: will throw PHP NOTICE on undefined (unset or !isset()) variables

3. So doctor, when do I use ?? and ?: ..

  • I\'m only joking - I\'m not a doctor and this is just an interpretation
  • I would use ?: when
    • doing empty($x) checks
    • Classic ternary operation like !empty($x) ? $x : $y can be shortened to $x ?: $y
    • if(!$x) { fn($x); } else { fn($y); } can be shortened to fn(($x ?: $y))
  • I would use ?? when
    • I want to do an !isset() || is_null() check
    • e.g check if an object exists - $object = $object ?? new objClassName();

4. Stacking operators ...

  1. Ternary Operator can be stacked ...

    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 & credit for this code

    This is basically a sequence of:

    if( truthy ) {}
    else if(truthy ) {}
    else if(truthy ) {}
    ..
    else {}
    
  2. Null Coalese Operator can be stacked ...

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

    This is a sequence of:

    if(!isset($x) || is_null($x) ) {} 
    else if(!isset($y) || is_null($y) ) {}
    else {}
    
  3. Using stacking, I can shorten this:

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

    To this:

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

    Cool, right? :-)



回答5:

Both of them behave differently when it comes to dynamic data handling.

If the variable is empty ( \'\' ) the null coalescing will treat the variable as true but the shorthand ternary operator won\'t. And that\'s something to have in mind.

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

And the output:

1b
2b

2d
1f

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

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



回答6:

Scroll down on this link and view the section, it gives you a comparative example as seen below:

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

However, it is not advised to chain the operators as it makes it harder to understand the code when reading it later on.

The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.

Essentially, using the coalescing operator will make it auto check for null unlike the ternary operator.



回答7:

It seems there are pros and cons to using either ?? or ?:. The pro to using ?: is that it evaluates false and null and \"\" the same. The con is that it reports an E_NOTICE if the preceding argument is null. With ?? the pro is that there is no E_NOTICE, but the con is that it does not evaluate false and null the same. In my experience, I have seen people begin using null and false interchangeably but then they eventually resort to modifying their code to be consistent with using either null or false, but not both. An alternative is to create a more elaborate ternary condition: (isset($something) or !$something) ? $something : $something_else.

The following is an example of the difference of using the ?? operator using both null and false:

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

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

By elaborating on the ternary operator however, we can make a false or empty string \"\" behave as if it were a null without throwing an 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---

Personally, I think it would be really nice if a future rev of PHP included another new operator: :? that replaced the above syntax. ie: // $var = $false :? \"true\"; That syntax would evaluate null, false, and \"\" equally and not throw an E_NOTICE...



回答8:

Both are shorthands for longer expressions.

?: is short for $a ? $a : $b. This expression will evaluate to $a if $a evaluates to TRUE.

?? is short for isset($a) ? $a : $b. This expression will evaluate to $a if $a is set and not null.

Their use cases overlaps when $a is undefined or null. When $a is undefined ?? will not produce an E_NOTICE, but the results are the same. When $a is null the result is the same.



回答9:

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.


回答10:

Null Coalescing operator performs just two tasks: it checks whether the variable is set and whether it is null. Have a look at the following example:

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

The above code example states that Null Coalescing operator treats a non-existing variable and a variable which is set to NULL in the same way.

Null Coalescing operator is an improvement over the ternary operator. Have a look at the following code snippet comparing the two:

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

So, the difference between the two is that Null Coalescing operator operator is designed to handle undefined variables better than the ternary operator. Whereas, the ternary operator is a shorthand for if-else.

Null Coalescing operator is not meant to replace ternary operator, but in some use cases like in the above example, it allows you to write clean code with less hassle.

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