Mitchell Jiang
Mitchell Jiang

Reputation: 41

PHP - What does "Operator precedence and associativity do not specify an order of evaluation" mean?

I am reading the Operator Precedence section of the PHP Manual. I am confused about (or say, I don't understand the following sentences quite much):

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.

It also gives two examples to illustrate the undefined order of evaluation.

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

From what I understand, $a evaluats to 1 first because the associativity of the addition operator is left. Then 1 is added to $a++, which evaluates to 1. So, then result should be 2. Why does the comment in the documentation say "may print either 2 or 3"?

The second example is:

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

Similarly, $i++ evaluates to 1 first because the associativity of the assignment operator is right. Then the value of 1 should be set to the index 2 of the array. Why does the comment say "may set either index 1 or 2"?

The only explanation I can think of is that the order of code in the two examples above can be executed the opposite of what I reasoned.

Any thoughts to help me unravel my confusion will be greatly appreciated.

Upvotes: 3

Views: 664

Answers (2)

trincot
trincot

Reputation: 350137

The confusion concerns associativity. Where you say it applies there is no associativity at play.

Look at the given examples:

First example

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

You can break down the evaluation of that expression in two steps:

  1. Get the values of $a and calculate $a++
  2. Perform the addition

The precedence rules have something to say about this:

  • The evaluation of $a++ should happen before the evaluation of the addition:
    ++ has higher precedence than +

But that is it.

There is no rule that says that the value of $a should be retrieved before $a++ is evaluated. That is not what left associativity is about.

Associativity

Left (or right) associativity only comes into play when two operators with the same precedence (e.g. twice the same operator) occur one after the other in one expression. For instance

$a = 0
$a++ - 1 - $a++

The first - is guaranteed to be executed before the second, by the left associativity rule.

But, this leaves open the possibility for the following sequence of evaluation:

  1. evaluate the second $a++
  2. evaluate the first $a++
  3. evaluate the first subtraction (required to happen before next step)
  4. evaluate the second subtraction

So, although the associativity rule fixed that step 4 had to come after step 3, there is no rule that dictates the order of the first two steps.

Second example

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

Again, there is no left associativity at play here. PHP is free to perform the following two steps in either order:

  • evaluate $i++
  • evaluate $i for the index

There is no rule that dictates in which order this should happen.

Second example, with right associativity

Right associativity would be at play if the statement was like this:

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

Now PHP has to execute the following steps, with some freedom in the order of them:

  1. evaluate (rightmost) $i
  2. perform the addition
  3. assign to $i in the middle, yielding its value
  4. evaluate $i for the index
  5. retrieve array element at evaluated index
  6. assign the value to that array element

Associativity wants steps 3 and 6 to be in that order. It is not allowed that the array element receives its value before the "middle" $i does. But it does not prevent PHP from going for this alternative:

  1. evaluate $i for the index
  2. retrieve array element at evaluated index
  3. evaluate (rightmost) $i
  4. perform addition
  5. assign to $i in the middle, yielding its value
  6. assign the value to the retrieved array element

As steps 5 and 6 are still in the correct order, the associativity rule is abided by. Still the result is different.

Second example, with left associativity

Left associativity would also be at play if the statement was like this:

$array[$i][$j] = $i++;

It is quite trivial, but the left associativity rule for [ means that first the left-most index is used to retrieve an element from the array, and only then the second index should be used on the resulting array. But note that this still does not mean that $i has to be evaluated before $j.

Take this variation:

$i = 1;
$array[$i++][$i] = 13;

You cannot assume that the element $array[1][2] will get the value 13. It might well be $array[1][1]. Left associativity says nothing about this. It does state however that the retrieval of array elements (in this construct) must be done from left to right.

In steps, this is a possible scenario:

  1. The second occurrence of $i is evaluated: 1
  2. $i++ is performed and yields 1
  3. The array element 1 is retrieved (which is an array hopefully)
  4. Element 1 is taken from the resulting array

Step 3 and 4 have to happen in that order. They don't even have to be adjacent steps. This also is allowed for PHP to do:

  1. $i++ is performed and yields 1
  2. The array element 1 is retrieved (which is an array hopefully)
  3. The second occurrence of $i is evaluated: 2
  4. Element 2 is taken from the resulting array

Step 2 occurs before step 4, so the associativity rule is not broken.

Issues with the Precedence Article

Even though the statement at hand from Operators Precedence seems to be warranted, that article has at least one error concerning precedence, as I have elaborated in answer to this question.

Upvotes: 3

Barmar
Barmar

Reputation: 780798

Just because an operator is left-associative, it doesn't mean the parameters are evaluated left-to-right. In

$a + $a++

It can evaluate $a first or $a++ first. In the first case, the result is 1 + 1 = 2; in the second case the result is 2 + 1 = 3.

Associativity just specifies how multiple expressions are grouped when they're combined. For instance, if you have

1 - 2 + 3

left-associativity specifies that this is interpreted as

(1 - 2) + 3 = 2

rather than

1 - (2 + 3) = -4

But if this expression had sub-expressions with side effects, it could still evaluate them right-to-left (or in some mixed order).

Upvotes: 1

Related Questions