Why does if($x = 1 && $x == 1) throw error but if ($x == 1 && $x = 2) doesn't in php?

Viewed 52

In php the following code gives a warning of undefined variable $x:

if($x = 1 && $x == 1)

I thought it was equivalent to if( ($x = 1) && ($x == 1) ), but that's not the case. I've been told, it's because && has higher precedence than =, which causes the expression to get converted to:

if($x = (1 && ($x == 1)))

So far so good, but now consider:

$x=1; if($x == 1 && $x = 2)

This doesn't throw error. Why doesn't it get converted to:
$x=1; if( (($x == 1) && $x) = 2 )

I've been told thats due to = being right assosiative, but https://www.php.net/manual/en/language.operators.precedence.php says When operators have equal precedence their associativity decides how the operators are grouped.. Here we have =, && and == all being of different precedence.

P.S; My actual code is if($result != false && $res = $stmt->get_result()), which has been copied from some other reputable source, so seems like not using unneeded parenthesis is common in php.

1 Answers

I've played with several conditions and below is what I've got.

First, let's consider that we init $x before if statements to avoid undefined variable notice.

Second, let's confirm the precedence for operators:

  1. == is applied 1st
  2. && is applied 2nd
  3. = is applied 3rd

This returns true:

$x = 1;
if ($x = 1 && $x == 1) {
    echo 'true';
} else {
    echo 'false';
}

It goes like ($x = (1 && ($x == 1))) -> ($x = (1 && true)) -> ($x = true) -> true.


If we compare $x to another value than the assigned one we will get false:

$x = 1;
if ($x = 2 && $x == 2) {
    echo 'true';
} else {
    echo 'false';
}

It goes like ($x = (2 && ($x == 2))) -> ($x = (2 && false)) -> ($x = false) -> false.


The last one returns true:

$x = 1;
if ($x == 1 && $x = 2) {
    echo 'true';
} else {
    echo 'false';
}

It goes like ((($x == 1) && $x) = 2) -> ((true && $x) = 2) -> (true = 2) -> true.

The last comparison can't be interpreted by PHP so it's an approximate view.

It looks like the last action (true = 2) totally depends on the left operand. If we put $x = 2; we will get (false = 2) -> false.


I'm not sure about the last one and here is the only place were some mistakes can happen.

Otherwise, it looks like precedence works as expected.

Anyway, I always put parenthesis for an assignment action inside if operator (especially inside ternary if) to be sure that I will get what I expect. I don't think this affects performance or readability too much, but it may prevent some logical errors.


UPDATE:

Concering your code if($result != false && $res = $stmt->get_result()) it's not correct to compare it to if($x == 1 && $x = 2) because in your code are two different variables.

In this case logical operator will not call the second part at all if the fisrt one is false, see the 1st example here

UPDATE-2:

After the discussion under this answer we can see that the last conditions ($x == 1 && $x = 2) work like like (($x == 1) && ($x = 2)) -> ((1 == 1) && 2) -> true and $x becomes 2 after it.

Related