I've stumbled about an if statement, using PHPs null coalesce operator, not behaving as "expected". The code in question looks something like this:
if ($foo['bar'] ?? false || $foo['baz'] ?? false) { /* ... */ }
changing it to
if (($foo['bar'] ?? false) || ($foo['baz'] ?? false)) { /* ... */ }
solves it.
I ran a quick test in my terminal:
root@docker:/application# php -v
PHP 7.2.11-2+ubuntu18.04.1+deb.sury.org+1 (cli) (built: Oct 15 2018 11:40:35) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.2.11-2+ubuntu18.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies
with Xdebug v2.6.1, Copyright (c) 2002-2018, by Derick Rethans
root@docker:/application# php -a
Interactive mode enabled
php > $test = ['foo' => 'bar'];
php > var_dump($test['baz'] ?? null); // as expected
php shell code:1:
NULL
php > var_dump(($test['baz'] ?? null)); // as expected
php shell code:1:
NULL
php > var_dump($test['baz'] ?? null || $test['foobar'] ?? null); // as expected, but there's a Notice
PHP Notice: Undefined index: foobar in php shell code on line 1
PHP Stack trace:
PHP 1. {main}() php shell code:0
php shell code:1:
bool(false)
php > var_dump(($test['baz'] ?? null) || ($test['foobar'] ?? null)); // as expected
php shell code:1:
bool(false)
Now, what I think happens in test no. 3, is that it get's executed as
$test['baz'] ?? (null || $test['foobar']) ?? null
so if the $test['baz']
evaluates to unset (which it obviously does), next null || $test['foobar']
get's executed, which leads to, $test['foobar']
throwing the notice.
My question: Is this the expected behaviour of the null coalesce operator in PHP? I kind of expected it to bind stronger than for example the ||
(or) operator.
On the other hand, in the RFC (https://wiki.php.net/rfc/isset_ternary), there's an explicit example:
var_dump(0 || 2 ?? 3 ? 4 : 5); // ((0 || 2) ?? 3) ? 4 : 5 => int(4)
which could indicate, the example above is correct behaviour.
What do you think? Should this get reported as bug? I know It's not a "proper" question, however since I wasn't able to find a bug report/discussion/thread about it, I thought there should be a resource documenting it.
If you/the mods don't agree, I'll remove the question again.
This is expected behavior due to operator precedence
||
has higher precedence than ??
, so your original statement is treated as
if ($foo['bar'] ?? (false || $foo['baz']) ?? false)