Подтвердить что ты не робот

JS vs PHP: приоритет оператора присваивания при использовании с логическими или

(PHP имеет || и OR. JS имеет только ||.)

JS. Согласно MDN || имеет более высокий приоритет, чем =. Так что это не работает:

a || a = 1;

поскольку он оценивается как:

(a || a) = 1;

что приводит к "Недопустимая левая сторона в присваивании". Я это понимаю. Это имеет смысл.

PHP. По PHP.net он работает так же для PHP: || до =, Тем не менее, я использую это все время:

$a || $a = 1;

Почему это работает в PHP? И в довершение всего: PHP OR имеет более низкий приоритет, чем =, поэтому они не должны делать то же самое:

$a || $a = 1;
$a OR $a = 1;

но они... https://3v4l.org/UWXMd

Я думаю, что JS '|| работает в соответствии с таблицей MDN, а PHP OR работает как таблица PHP, но PHP || не должен работать так, как будто это делает.

Это еще одна странная причуда PHP?

В руководстве также упоминается следующее:

Хотя = имеет более низкий приоритет, чем большинство других операторов, PHP все равно позволит выражениям, похожим на следующее: if (!$a = foo()), и в этом случае возвращаемое значение foo() помещается в $a.

В таблице приоритета указано, что PHP должен оценивать (!$a) = foo(), что не имеет смысла и должно потерпеть неудачу, но PHP оценивает его как !($a = foo()), потому что он любит исключения.

Последующий вопрос: Как вы думаете, if ( $d = $c && $e = $b && $f = $a )? https://3v4l.org/3P2hN Я не понимаю... Я понимаю второй и третий случай (с and), просто не то, что происходит в первом.

4b9b3361

Ответ 1

В соответствии с zend_language_parser.y код анализируется эквивалентно $a || ($a = 1) и $a or ($a = 1) в каждом случае соответственно.

Как суммировано melpomene, постановка присваивания не является инфиксными двоичными операторами над выражениями; скорее операторы присваивания ограничены производством, где левая часть должна быть variable.

За заимствованную цитату:

Таким образом, PHP анализирует выражение единственным возможным способом.

Документация правильна о приоритете.. где она применяется.


Таким образом, $a || $a = 1 следует (обратным) произведениям:

variable "||" variable "=" expr
variable "||" expr_without_variable
expr "||" expr
expr

Случай !$a = foo() аналогичен и анализируется как !($a = foo()) после выполнения (обратных) производств:

"!" variable "=" expr
"!" expr_without_variable
"!" expr                 
expr

Теперь, как насчет $d = $c && $e = $b && $f = $a? Он не анализируется как ($d = $c) && .., хотя && имеет более высокий приоритет, чем назначение. Он фактически анализируется как $d = ($c && ($e = ..)) и т.д., Который должен быть завершен проницательным читателем.

В то время как это может быть не случайно замечено, это различие способно производить разные результаты:

$a = (($c = 1) && ($d = 0));
var_dump($a, $c, $d);         // => false, 1, 0

$b = ($e = 1 && $f = 0);      // => $b = ($e = (1 && ($f = 0)));
var_dump($b, $e, $f);         // => false, false, 0

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

Как неясно, как это может показаться на первый взгляд, это четко определенная грамматика, но технические детали зарыты за какой-то довольно непрофессиональной документацией; и правила отличаются тонко от правил на других языках, подобных синтаксису. Отсутствие официального документа EBNF в документации не помогает.


Несмотря на детали синтаксического анализа, код $a || $a = .. (который является допустимым и четко определенным синтаксисом) должен оставаться корректным с точки зрения оценки, так как левая сторона "или" должна произойти до права из-за гарантированное короткое замыкание.


Для сравнения, в JavaScript, a || a = 1 анализируется как (a || a) = 1 - также синтаксически "действительный" код - за Правила грамматики ECMAScript. Тем не менее, a || a не дает допустимого ссылочного типа спецификаций и, таким образом, запускается ReferenceError времени выполнения.

Ответ 2

Что касается вашего последующего вопроса: if ( $d = $c && $e = $b && $f = $a ) совпадает с:

$d = $c;
if($d) {
    $e = $b;
    if($e) {
        $f = $a;
        if($f) {
            ...
        }
    }
}

Я предполагаю, что вы это знаете, но некоторые вопросы меня смущают, поэтому я упомянул об этом... = оператор присваивания, а не оператор сравнения. if($a = $b) не проверяет, совпадают ли $a и $b, он равен $равному $b, а затем проверяет, соответствует ли $a true. if($a == $b) проверяет, совпадают ли две переменные.

Ответ 3

Выражение $a || $a = 1; эквивалентно этому:

if ( $a != true ) {
    $a = 1;
}

Очень распространенный вариант этой идеи используется для отладки бедных:

$debug = true;

// Thousands of lines of code

$debug && printf("Foo: {$foo}");

// More code

$debug && printf("Bar: {$bar}");

В этой парадигме для включения/отключения отладки необходимо установить только оператор $debug в значение true/false. Я не защищаю этот тип отладки, но я видел это несколько раз.