48

I'm trying to understand the behavior of or operator. Please see the below examples:

$e = false || true;
var_dump($e);

Output is as expected: bool(true);

$f = false or true;
var_dump($f);

Output is as expected: bool(false). I understood this in a way that the = has a higher precedence than the Or, so that's why the $f is assigned to false.

But the below code works quite opposite of what I thought. I thought that the $foo will be assigned to 5 and then compared to itself. But the $foo is getting assigned only when if the $foo is set that means it is checking if the $foo is assigned to anything before, assign 5 to it.

$foo or $foo = 5; 

Can anyone explain why this is so?

leppie
  • 115,091
  • 17
  • 196
  • 297
Daric
  • 16,229
  • 11
  • 41
  • 58
  • 6
    +1 good question. Extremely useful to future programmers for anyone who is stuck on this point. – Lucas Aug 31 '12 at 10:21
  • 1
    Not directly related you might find [this page](http://codeboutique.com/tools/pvaat/pvaat.php) useful. I've thought about adding boolean operators as well. – Michael Aug 31 '12 at 10:26
  • I've updated the page to show 'or' and 'and' in different situations. There are some odd results. – Michael Aug 31 '12 at 15:53

4 Answers4

49

The basics:

  1. An assignment expression results in the assigned value.

    What does that mean? $foo = 'bar' is an expression, in which the assignment operator = assigns a value. An expression always returns a value itself. Just like the expression 1 + 2 results in the value 3, the expression $foo = 'bar' results in the value 'bar'. That's why this works:

    $foo = $bar = 'baz'; // which is: $foo = ($bar = 'baz');
    
  2. Boolean operations are short-circuiting operations. Both sides are not always evaluated if they don't need to be. true || false is always true overall, since the lefthand operand is true, so the whole expression must be true. false is not even being evaluated here.

  3. Operator precedence dictates in which order parts of an expression are grouped into sub-expressions. Higher precedence operators are grouped with their operands before lower precedence operators.

Therefore:

$e = false || true;

false || true is being evaluated, which results in the value true, which is assigned to $e. The || operator has a higher precedence than =, therefore false || true is grouped into an expression (as opposed to ($e = false) || true).

$f = false or true;

Here now or has a lower precedence than =, which means the assignment operation is grouped into one expression before or. So first the $f = false expression is evaluated, the result of which is false (see above). So then you have the simple expression false or true which is evaluated next and results in true, but which nobody cares about.

The evaluation works like this:

1. $f = false or true;
2. ($f = false) or true;  // precedence grouping
3. false or true;         // evaluation of left side ($f is now false)
4. true;                  // result

Now:

$foo or $foo = 5; 

Here, again, $foo = 5 has a higher precedence and is treated as one expression. Since it occurs on the right side of the or operator, the expression is only evaluated if necessary. It depends on what $foo is initially. If $foo is true, the right hand side will not be evaluated at all, since true or ($foo = 5) must be true overall. If $foo has a falsey value initially though, the right hand side is evaluated and 5 is assigned to $foo, which results in 5, which is true-ish, which means the overall expression is true, which nobody cares about.

1. $foo or $foo = 5;
2. $foo or ($foo = 5);   // precedence grouping
3. false or ($foo = 5);  // evaluation of left side
4. false or 5;           // evaluation of right side ($foo is now 5)
5. true;                 // result
deceze
  • 510,633
  • 85
  • 743
  • 889
  • Got it in a first move. Thanks for such a nice explanation. FYI: I got beneficial from your `charset` topic recently. Thanks – Daric Aug 31 '12 at 11:56
  • It appears that when not contrained by short-circuiting operators like `or`, PHP _does_ evaluate parenthesized expressions first, e.g. `$foo = 2; echo $foo + ($foo = 4)` will print 8, not 6. Is this actually specified in the language, or just an implementation decision? – Barmar Sep 04 '12 at 19:15
  • @Barbar I'm sure you can figure out the evaluation logic behind that somehow, but I'd say that the behavior of such self-modifying and mostly nonsensical operations are by definition mostly undefined. – deceze Sep 04 '12 at 20:35
  • @Barmar It is not documented AFAIK, but this is not mainly because of the parantheses but because for an expression A + B, PHP first evaluates A **and** B, then sums them up. Now comes the tricky part: $foo evaluates to the corresponding ZVal (variable reference) while ($foo = 4) evaluates to the *value* 4 with the side-effect of modifying $foo. *Then* they get summed up. – Fabian Schmengler Feb 08 '13 at 08:02
  • could you let me know when to use like `$b or $b = '1';` since if $b is not set than its rises [Undefined variable error](http://3v4l.org/R19Dq) – NullPoiиteя Feb 14 '13 at 04:18
20

As per the php.net webpage about Logical Operators:

This:

$e = false || true;

Acts like this:

$e = (false || true) // If false is true, then $e = false. Otherwise true

This:

$f = false or true;

Would act like this:

($f = false) or true; // $f = false is true, as the assignment succeeded

This:

$foo or $foo = 5; 

Would act like this:

$foo or ($foo = 5) // foo = undefined or foo = 5, so foo = 5

For the last one, undefined is basically like false, therefore foo equals 5.

Also, here's the link for the operator precedence order: http://www.php.net/manual/en/language.operators.precedence.php

UPDATE:

Ok, now let's get to the main point. Like how we all know when using a fetched query:

while($row = @mysql_fetch_assoc($result))

And we all know while loops only execute on true, therefore $row = @mysql_fetch_assoc($result) returns true.

Same with Daric's question.

$foo or $foo = 5;

Is basically:

$foo or ($foo = 5);

Which is basically:

$foo = undefined or ($foo = 5); // $foo = 5 actually returns true

Which is also

$foo = undefined or true;

And as I have previously mentioned, undefined = false, so therefore $foo = 5 (as that is the true statement).

I hope everyone can understand.

Lucas
  • 16,930
  • 31
  • 110
  • 182
  • *"If $f cannot be assigned, then true"* - No, rather *if the result of the expression `$f = false` is `true` or ...*. And the result of an assignment expression is the assigned value. – deceze Aug 31 '12 at 10:21
  • @deceze I would think otherwise. That was according to the PHP documentation. – Lucas Aug 31 '12 at 10:21
  • Can you post the reference to that? There is no way something cannot be assigned though, the `=` operator always returns the assigned value. – deceze Aug 31 '12 at 10:23
  • @deceze Actually your explanation seems true too. I wouldn't want to continue this argument anymore. Either way, it still is roughly the same. – Lucas Aug 31 '12 at 10:23
  • 3
    @think123 PHP document does point out that assignment expression "returns" the assigned result http://www.php.net/manual/en/language.operators.assignment.php . This is not "roughly the same" because `$f=true or false` will not "execute" `false` while `$f=false or true` will "execute" `true`. – Passerby Aug 31 '12 at 10:27
  • if you think my code needs improving, can you please edit it? thank you very much. – Lucas Aug 31 '12 at 10:28
  • @think123: Again thinking on this left me confused a totally confused, I think there is something still not clear in mind. – Daric Aug 31 '12 at 11:07
  • @Daric what is it you don't understand? I really want to help you. – Lucas Aug 31 '12 at 11:16
  • @think123: If you are available can we go in the php chat system. – Daric Aug 31 '12 at 11:23
  • @Daric Oh no not now, sorry. I just finished editing my answer, It should be able to help you. – Lucas Aug 31 '12 at 11:24
  • 1
    @think123: here in this statement `$foo or $foo = 5;` Why should not it first assign the `5` to `$foo` as the `=` has higher precedence than the `or`. Why not `=` evaluated first. – Daric Aug 31 '12 at 11:30
  • What? `@mysql_*`? Can't you use PDO for your example? I haven't seen `@` *and* `mysql_*` in an *answer* before. – uınbɐɥs Sep 04 '12 at 19:14
  • @ShaquinTrifonoff It's just for testing purposes, not strictly for usage. – Lucas Sep 29 '12 at 07:41
3
$foo or $foo = 5;

Suppose let say $foo=true or $foo=5;

here it will not evaluate after or operator expresion so output will be $foo=1 Now the expression is

$foo=false or $foo=5;

Here it will evaluate after or as = higher precedence so $foo as of which of which it will evaluate $foo=5so output will be 5 But when we evaluate $foo=false or true so here it will consider = higher precedence so the output will be $foo=false but whole expression will evaluate as true because false or true becomes false

Arpit Srivastava
  • 2,249
  • 1
  • 16
  • 28
  • Precedence refers to grouping, not order of evaluation. As deceze explained, `or` always evaluates its left argument first, because it's a short-circuiting operator. – Barmar Sep 04 '12 at 19:01
2
<?php
$foo = false;
$foo or ($foo = '5');
echo $foo;
?>

Check this you can assign the value "5" for $foo.

Compare than or = has high priority.. Thats the fact.... :)

Basith
  • 1,077
  • 7
  • 22