PHP short-ternary ("Elvis") operator vs null coalescing operator
Asked Answered
M

14

571

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
Matador answered 2/1, 2016 at 22:23 Comment(2)
A good way of testing it is to use a php console in interactive mode (php -a). Then ini_set('error_reporting', 'E_ALL'), and ini_set('display_errors', 'on'). Then you can try var_dump($var ?? 'default')) e.g. and see what happens wether you set any type of values before itAlternative
Not easy to find via googling: documentation: It is possible to leave out the middle part of the ternary operator. Expression expr1 ?: expr3 returns expr1 if expr1 evaluates to true, and expr3 otherwise.Protolithic
V
598
  • Elvis ?: returns the first argument if it contains a "true-ish" value (see which values are considered loosely equal to true in the first line of the Loose comparisons with == table). Or the second argument otherwise

      $result = $var ?: 'default';
      // is a shorthand for 
      $result = $var ? $var : 'default';
    
  • Null coalescing ?? returns the first argument if it's set and is not null. Or the second argument otherwise

      $result = $var ?? 'default';
      // is a shorthand for 
      $result = isset($var) ? $var : 'default';
    

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'; // 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";

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'; // false
$b = false ?: 'g'; // 'g'

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

Verret answered 2/1, 2016 at 22:41 Comment(5)
Tip: if you have been using ?? instead of ?: but then find yourself needing to make your code compatible with PHP versions older than 7(for a plugin for ex), then you may want to swap out the ?? with isset($something) ? $something : $something_else everywhere in your code. You can easily do this with Notepad++ or nedit (and other editors too) using the find/replace tool, selecting the regular expression option and inserting in the find field: "\s*(\S+)\s*\?\?" and in the replace field: " isset($1) ? $1 :" without the quotes (nedit uses \1 instead of $1). Then replace all.Intima
This is the right answer however the truthiness check is the major diference and should be more emphasized.Bearberry
@Verret Not satisfied with your answer. Both are not same. Have different result.Gaylord
It's worth noting that you can use ?? with chaining. For example: $b = []; var_dump($b['a']['b']['c'] ?? 'default'); or with objects $b = new Foo; var_dump($b->a()->b()->c() ?? 'default');Vyatka
Please be aware that the behavior is also different with $a = [];. See: 3v4l.org/iCCa0Doyen
L
266

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

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

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() )

Use of ??

  • shorten !isset() || is_null() check
  • e.g $object = $object ?? new objClassName();

Stacking Null Coalese Operator

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

        // This is a sequence of "SET && NOT NULL"s:

        if( $x  &&  !is_null($x) ){ 
            return $x; 
        } else if( $y && !is_null($y) ){ 
            return $y; 
        } else { 
            return $z; 
        }

The Ternary Operator ?:

  • ?: is like a gate that lets anything falsy through - including NULL
  • Anything falsy: 0, empty string, NULL, false, !isset(), empty()
  • Same like old ternary operator: X ? Y : Z
  • Note: ?: will throw PHP NOTICE on undefined (unset or !isset()) variables

Use of ?:

  • checking empty(), !isset(), is_null() etc
  • shorten ternary operation like !empty($x) ? $x : $y to $x ?: $y
  • shorten if(!$x) { echo $x; } else { echo $y; } to echo $x ?: $y

Stacking Ternary Operator

        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: http://php.net/manual/en/language.operators.comparison.php#95997
   
        // This is basically a sequence of:

 
        if( truthy ) {}
        else if(truthy ) {}
        else if(truthy ) {}
        ..
        else {}

Stacking both, we can shorten this:

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

To this:

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

Cool, right? :-)

Leede answered 24/3, 2018 at 12:23 Comment(4)
Superior, except for one mistake: shorten if(!$x) { echo $x; } else { echo $y; } to echo $x ?: $y. One does not equal the other. The condition has to be if($x) instead, without negation. It still has let me to learn a bit about this operator that was new to me in it's short version, so post received an upvote.Environs
In php, please always use elseif as a single word to align with PSR-12 coding standards. I know that you were just making a demonstration, but isset($_GET['name']) && !is_null($_GET['name']) in the first place is redundant checking.Immesh
This is a very good answer to learn how these operators work, but I hope I never have to troubleshoot production code that uses both operators in one statement!Phonogram
Very good explanation, but don't miss this line: Note: ?: will throw PHP NOTICE on undefined (unset or !isset()) variables, you will still have to use !empty() or isset() (depending on the behaviour you need) if you cannot guarantee that a variable (specially an array index) has been set.Laszlo
I
81

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.

Indolent answered 14/1, 2017 at 20:9 Comment(1)
In order no to have a notice generated, one should use $var = empty($other_var) ? 'default_value' : $other_var;. Note that this excludes '', null, false, and 0Alternative
G
46

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 image description here

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.

Here is the data from the above image in text format as well:

    Expression           var_dump(($x) ?: 'default')       var_dump($x ?? 'default')

    $x=""                string 'default' (Length=7)       string '' (Length=0)
    $x=null              string 'default' (Length=7)       string 'default' (Length=7)
    $x is undefined      string 'default' (Length=7)       string 'default' (Length=7)
                         Notice: Undefined variable: x
    $x=array()           string 'default' (Length=7)       array (size=0)
                                                             empty   
    $x=array("a","b")    array (size=2)                    array (size=2)
                           0 => string 'a' (Length=1)        0 => string 'a' (Length=1)
                           1 => string 'b' (Length=1)        1 => string 'b' (Length=1)
    $x=false             string 'default' (Length=7)       boolean false
    $x=true              boolean true                      boolean true
    $x=1                 int 1                             int 1
    $x=42                int 42                            int 42
    $x=0                 string 'default' (Length=7)       int 0
    $x=-1                int -1                            int -1
    $x="1"               string '1' (Length=1)             string '1' (Length=1)
    $x="0"               string 'default' (Length=7)       string '0' (Length=1)
    $x="-1"              string '-1' (Length=2)            string '-1' (Length=2)
    $x="anystring"       string 'anystring' (Length=9)     string 'anystring' (Length=9)
    $x=new stdClass      object(stdClass)[1]               object(stdClass)[1]
Gaylord answered 18/11, 2017 at 10:29 Comment(0)
W
18

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

Whimsical answered 23/5, 2016 at 17:17 Comment(1)
This is clearly counter intuitive for PHP, where an empty string is usually considered false. Yet it is clearly indicated in the docs for ??: It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.Velazquez
A
16

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.

Alimentary answered 7/12, 2018 at 17:41 Comment(0)
A
10

Practical short answer :

Try:

var_dump('' ?: 'ok');  // prints: ok

vs

var_dump('' ?? 'ok');  // prints empty string

The shorthand ternary operator (?:) will return the value on the left side if the left side value evaluates to something "truthy"; otherwise the right side value will be returned.

whereas

The null coalescing operator (??) will return the value on the left if the left side value is declared and not null; otherwise the right side value will be returned.

In other words, ?: tests for truthiness, and ?? acts as shorthand for isset().


*CAUTION : if you want to test a variable with ?:, you must first ensure it is initialized/set, otherwise PHP will raise an E_NOTICE (whereas ?? won't).

Alternative answered 15/3, 2022 at 13:40 Comment(0)
B
8

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

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.

Bushore answered 2/1, 2016 at 22:33 Comment(6)
Please don't consider chaining... it's as difficult to read/understand as chained ternariesGlaring
@MarkBaker this is simply the example from PHP's site. I'll be updating the post to say how it is not advised.Bushore
@MarkBaker Chained ternaries are hard to understand because PHP has broken ternary associativity. This does not apply to the coalesce operator and imho chained coalesce is perfectly understandable.Lallygag
I disagree. Chaining the null coalesce is a great feature, and it doesn't make it hard to read if you understand the operator. It's commonly used in javascript and once people get comfortable with it in PHP this call to not use chaining should stop. Chaining ternaries is very hard to read, but null coalesce is easy. As you read from left to right it just lists which value should be used next.Missive
This looks very much like the common a || b || c pattern in JS, except PHP's can be used for booleans (false || 2 in JS is 2; false ?? 2 in PHP is false)Purgatory
I disagree with you and others regarding not using chaining. It's like saying dont ever use for loops because might not understand them. Developers/coders are perfectly free to use coding standards and practices that they understand, even if others do not. Personally, I view chained coalescing as very similar to switch statements. It returns the first value that is found (set), and the last value if nothing is found.Mcmanus
A
8

For the beginners:

Null coalescing operator (??)

Everything is true except null values and undefined (variable/array index/object attributes)

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"

this is basically check the variable(array index, object attribute.. etc) is exist and not null. similar to isset function

Ternary operator shorthand (?:)

every false things (false,null,0,empty string) are come as false, but if it's a undefined it also come as false but Notice will throw

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

Hope this helps

Aucoin answered 13/6, 2019 at 18:50 Comment(0)
D
3

The other answers goes deep and give great explanations. For those who look for quick answer,

$a ?: 'fallback' is $a ? $a : 'fallback'

while

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


The main difference would be when the left operator is either:

  • A falsy value that is NOT null (0, '', false, [], ...)
  • An undefined variable
Dropkick answered 8/8, 2019 at 9:0 Comment(1)
There should be no $a = in the above expansion of ??. $a ?? 'fallback' does not set or change the value of $a. (It merely returns a value).Triangle
I
1

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

Intima answered 9/7, 2017 at 2:21 Comment(1)
you can use $var = $false ?? null ?: "String is empty/false/null/undefined";Youlandayoulton
O
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.
Odellodella answered 15/9, 2017 at 11:7 Comment(1)
If you have some method calls in the chain, PHP has nullsafe operators: Is there a "nullsafe operator" in PHP?Immesh
D
0

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

Dupaix answered 14/11, 2017 at 1:47 Comment(1)
isset($_POST['fullname']) already checks for NULL values - so the && !is_null($_POST['fullname']) in the first example is redundant anywayDropkick
S
0

When using the superglobals like $_GET or $_REQUEST you should be aware that they could be an empty string. In this specal case this example

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

will fail because the value of $username now is an empty string.

So when using $_GET or even $_REQUEST you should use the ternary operator instead like this:

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

Now the value of $username is 'nobody' as expected.

Seignior answered 25/2, 2019 at 9:43 Comment(1)
Good catch. Also, coalescing-operator will also fail in case of an empty string.Icebox

© 2022 - 2024 — McMap. All rights reserved.