Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS vs PHP: assignment operator precedence when used with logical-or

(PHP has || and OR. JS only has ||.)

JS. According to MDN || has higher precedence than =. So this doesn't work:

a || a = 1;

because it's evaluated as:

(a || a) = 1;

which results in an "Invalid left-hand side in assignment". I understand that. That makes sense.

PHP. According to PHP.net it works the same for PHP: || before =. However, I use this all the time:

$a || $a = 1;

Why does it work in PHP?? And to top it off: PHP's OR has lower precedence than =, so these shouldn't do the same:

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

but they do... https://3v4l.org/UWXMd

I think JS' || works according to MDN's table, and PHP's OR works like PHP's table, but PHP's || shouldn't work like it does.

Is this yet another weird PHP quirk?

The manual also mentions this:

Although = has a lower precedence than most other operators, PHP will still allow expressions similar to the following: if (!$a = foo()), in which case the return value of foo() is put into $a.

The precedence table dictates PHP should evaluate (!$a) = foo(), which makes no sense and should fail, but PHP evaluates it as !($a = foo()), because it loves exceptions.

Follow-up question: What do you think if ( $d = $c && $e = $b && $f = $a ) does? https://3v4l.org/3P2hN I don't get it... I do understand the second and third case (with and), just not what happens in the first.

like image 660
Rudie Avatar asked Sep 19 '15 18:09

Rudie


People also ask

Which logical operator has higher precedence in PHP?

The precedence of an operator specifies how "tightly" it binds two expressions together. For example, in the expression 1 + 5 * 3 , the answer is 16 and not 18 because the multiplication ("*") operator has a higher precedence than the addition ("+") operator. Parentheses may be used to force precedence, if necessary.

Which logical operator has the higher precedence from the following?

The logical-AND operator ( && ) has higher precedence than the logical-OR operator ( || ), so q && r is grouped as an operand.

Which operator has highest precedence in?

Certain operators have higher precedence than others; for example, the multiplication operator has higher precedence than the addition operator. Here, operators with the highest precedence appear at the top of the table, those with the lowest appear at the bottom.

What is the order of precedence of operators?

In the United States and in France, the acronym PEMDAS is common. It stands for Parentheses, Exponents, Multiplication/Division, Addition/Subtraction. PEMDAS is often expanded to the mnemonic "Please Excuse My Dear Aunt Sally" in schools.


1 Answers

According to zend_language_parser.y the code is parsed equivalently to $a || ($a = 1) and $a or ($a = 1) in each case, respectively.

As summarized by melpomene, the assignment productions are not infix binary operators over expressions; rather assignment operators are restricted productions where the left-hand side must be a variable production.

Per a borrowed quote:

Thus PHP parses the expression in the only possible way..

The documentation is correct about the precedence .. where it applies.


Thus $a || $a = 1 follows the (reversed) productions of:

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

The case of !$a = foo() is similar and is parsed as !($a = foo()) after following the (reversed) productions:

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

Now, how about $d = $c && $e = $b && $f = $a? It is not parsed as ($d = $c) && .. even though the && does have a higher precedence than the assignment. It is actually parsed as $d = ($c && ($e = ..)) and so on, to be completed by the astute reader.

While it might not be casually noticed, this difference is capable of producing varying results:

$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

Parenthesis should thus generally be used when mixing assignment operators with operators of higher precedence, especially when the result of such may be .. unclear.

As inconsistent as this may initially seem, it is a well-defined grammar - but the technical details are buried behind some fairly layman documentation; and the rules differ subtly from those in other C-syntax-like languages. The lack of an official EBNF in the documentation doesn't help.


Despite the parsing details, the $a || $a = .. code (which is valid and well-defined syntax) should remain well-defined from an evaluation viewpoint as the left side of the 'or' must occur prior to the right due to guaranteed short-circuiting.


For contrast, in JavaScript, a || a = 1 is parsed as (a || a) = 1 - which is also syntactically 'valid' code - per the ECMAScript Grammar Rules. However, a || a does not yield a valid Reference Specification Type and thus a runtime ReferenceError is thrown.

like image 91
user2864740 Avatar answered Nov 04 '22 23:11

user2864740