JS против 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), только не то, что происходит в первом.

16

Решение

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

Как резюмирует Мельпомена, задание производств не инфиксные бинарные операторы над выражениями; скорее операторы присваивания ограничены производства где левая сторона должен быть 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

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

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


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


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

13

Другие решения

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

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, чтобы включить / отключить отладку. Я не защищаю этот тип отладки, но я видел это довольно много раз.

-1
По вопросам рекламы [email protected]