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

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

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

 a || a = 1; 

потому что он оценивается как:

 (a || a) = 1; 

что приводит к «Недопустимая левая сторона при назначении». Я это понимаю. В этом есть смысл.

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

 $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() A (!$a) = foo() , что не имеет смысла и должно потерпеть неудачу, но PHP оценивает его как !($a = foo()) , потому что он любит исключения.

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

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

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

По заимствованной цитате :

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

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


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

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

Случай !$a = foo() A !$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 = .. Код $a || $a = .. (который является допустимым и четко определенным синтаксисом) должен оставаться четко определенным с точки зрения оценки, поскольку левая сторона «или» должна произойти до права из-за гарантированного короткого замыкания .


Для контраста, в JavaScript, a || a = 1 a || a = 1 анализируется как (a || a) = 1 – который также является синтаксически «действительным» кодом – согласно правилам грамматики ECMAScript . Однако, a || a a || a не дает допустимого ссылочного типа спецификации, и, таким образом, возникает вызов RefereneError.

Что касается вашего последующего вопроса: 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) проверяет, совпадают ли две переменные.

Выражение $a || $a = 1; $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. Я не защищаю этот тип отладки, но я видел это довольно много раз.