Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does PHP evaluate $b and $b = $b differently when used with $b++ in array index

I am unable to grasp the evaluation logic in the code listed below. Does anyone know why PHP evaluates $b and $b = $b differently in this case?

I have read through a number of questions here at SO and checked the PHP manual. Doing so I've come to understand that "PHP does not (in the general case) specify in which order an expression is evaluated" and that "the behavior can change between versions of PHP or depending on the surrounding code". I don't feel that that applies to this situation though. Or does it?

Being the first to admit this may not be your everyday coding issue, I am still curious. Stumbled upon it trying to do some code golfing.

$a = [[00, 01, 02, 03],
      [10, 11, 12, 13],
      [20, 21, 22, 23],
      [30, 31, 32, 33]];

$b = 2;
echo $a[$b][$b++], PHP_EOL;

$b = 2;
echo $a[$b=$b][$b++], PHP_EOL;

Output - PHP 5.5.14:

32
22
like image 296
mhall Avatar asked Apr 01 '15 19:04

mhall


1 Answers

This looks like the example in the manual used for demonstrating Undefined order of evaluation. From the manual:

Operator precedence and associativity only determine how expressions are grouped, they do not specify an order of evaluation. PHP does not (in the general case) specify in which order an expression is evaluated and code that assumes a specific order of evaluation should be avoided, because the behavior can change between versions of PHP or depending on the surrounding code.

Emphasis added

The example they give:

<?php
$a = 1;
echo $a + $a++; // may print either 2 or 3

$i = 1;
$array[$i] = $i++; // may set either index 1 or 2
?>

You are getting the output you are because in the first example, the index $b++ is being determined first, whereas in the second one the index $b=$b is first.

NB

As for why this is, I believe one possible reason is explained by this note on the same manual page:

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.

I believe they're missing a crucial last word there: first (without, to me reading the note loses a bit of meaning).

Following PHP's own rules, and we assume a FIFO order, !$a should be evaluated first. If $a is currently null or undefined, then !$a will equal true (it will be evaluated and that result will be thrown away). Following that, foo() will be evaluated, and its return value will be assigned to $a (even if we're assuming FIFO, foo() must be evaluated first if its result is to be assigned to something). The result of this assignment will be evaluated by if, and will result in exactly the opposite value that the author wanted.

I'm no C expert, but a bit of searching also lead me this answer which quotes the C90 Standard:

(C90, 6.3) "Except as indicated by the syntax or otherwise specified later (for the function-call operator (), &&, ||, ?:, and comma operators). the order of evaluation of subexpressions and the order in which side effects take place are both unspecitied"

Since PHP is built on C, it makes sense that it would inherit some of its eccentricities.

like image 122
Jeff Lambert Avatar answered Sep 22 '22 11:09

Jeff Lambert