phpoperator-precedencenull-coalescing-operator

Why does the PHP null-coalescing operator (??) behave irrationally with == and ===?


var_dump(3 ?? null);
var_dump(3 ?? null === null);
var_dump(3 === null);
var_dump(3 ?? 1 > 2);
var_dump(null ?? 1 > 2);

What do you consider the outputs to be?

3 # Expected
true # Unexpected: I'd expect false
false # This is also what I'd expect
3 # Wait, what?! Why is it 3 and not true??
false # Oh so it only evaluates the right-side boolean if the left side is null??? How unexpected!

So the question is really this:

Why does the PHP engine value === as higher than ??? In what world would anyone want to compare it like if ($count ?? (null === null)) when that is so counter-intuitive to the desired if (($count ?? null) === null)?

Given $count = 3; if ($count ?? 1 > 2), as a rational person, I would expect this to evaluate to if ((3 ?? 1) > 2) -> if (3 > 2) and return true. Instead, it evaluates to if (3 ?? (1 > 2) -> if (3), which just happens to be truthy. But I don't think anyone would rationally want this evaluation...

It seems like a bug in the interpreter to me. Is there any good reason for this state of affairs?


Solution

  • You are seeing this result because ?? has lower precedence than any of the comparison operators (see the manual). Thus for example,

    $count ?? null === null
    

    evaluates as

    $count ?? (null === null)
    

    which evaluates to

    $count
    

    as $count is set.

    For most of your tests, that then evaluates to 3 or a, which are both "truthy", and so the condition test is true. Note that for this particular test, even if $count was unset, it would still evaluate true as null === null. So:

    $count = 3;
    if ($count ?? null === null) { echo "Noooo!!!\n"; } else { echo "Yes!!\n"; }
    

    will output:

    Noooo!!!
    

    The one exception is where $count is set to false (or any other "falsy" value other than NULL, such as 0, "" or []), where the result of the evaluation is then false, which gives you your expected result for

    if ($count ?? true === true) { echo "Noooo!!!!\n"; } else { echo "Yes!!\n"; }
    

    If you add parentheses to properly specify your desired operation, everything works as expected e.g.

    $count = 3;
    if (($count ?? null) === null) { echo "Noooo!!!\n"; } else { echo "Yes!!\n"; }
    

    Output:

    Yes!!
    

    Demo showing faulty and corrected code for each example on 3v4l.org