Это ошибка в PHP null coalesce operator или ожидаемое поведение?

#php #null-coalescing-operator

#php #null-coalescing-operator

Вопрос:

Я наткнулся на оператор if, использующий PHPs null coalesce operator, который ведет себя не так, как «ожидалось». Рассматриваемый код выглядит примерно так:

 if ($foo['bar'] ?? false || $foo['baz'] ?? false) { /* ... */ }
  

меняем его на

 if (($foo['bar'] ?? false) || ($foo['baz'] ?? false)) { /* ... */ }
  

решает это.

Я провел быстрый тест в своем терминале:

 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)

  

Теперь, что, я думаю, происходит в тесте № 3, так это то, что он выполняется как

 $test['baz'] ?? (null || $test['foobar']) ?? null
  

итак, если $test['baz'] вычисляется значение unset (что, очевидно, и происходит), выполняется следующий null || $test['foobar'] get, что приводит к $test['foobar'] отправке уведомления.

Мой вопрос: Это ожидаемое поведение оператора null coalesce в PHP? Я вроде ожидал, что он будет привязываться сильнее, чем, например, оператор || (or).
С другой стороны, в RFC (https://wiki.php.net/rfc/isset_ternary), есть явный пример:

 var_dump(0 || 2 ?? 3 ? 4 : 5); // ((0 || 2) ?? 3) ? 4 : 5 => int(4)
  

что может указывать на то, что приведенный выше пример является правильным поведением.

Что вы думаете? Должно ли об этом сообщаться как об ошибке? Я знаю, что это не «правильный» вопрос, однако, поскольку я не смог найти отчет об ошибке / обсуждение / ветку об этом, я подумал, что должен быть ресурс, документирующий это.
Если вы / моды не согласны, я снова удалю вопрос.

Комментарии:

1. Если RFC подтверждает поведение, почему это должно быть ошибкой?

2. Разве это не просто приоритет оператора . || имеет более высокий приоритет, чем ?? .

3. @PatrickQ Это точно не подтверждает поведение, но у IMO есть похожий случай. Вот почему я спросил.

4. Я не думаю, что ответ необходим. Это скорее случай RTM.

5. @Barmar ИМО, по этой причине многие вопросы здесь должны исчезнуть. Но опять же, я не согласен со всей новой философией контента ради контента.

Ответ №1:

Это ожидаемое поведение из-за приоритета оператора

|| имеет более высокий приоритет, чем ?? , поэтому ваше исходное утверждение обрабатывается как

 if ($foo['bar'] ?? (false || $foo['baz']) ?? false)