Post-increment acting weird
Asked Answered
A

1

5

I've narrowed a problem down to this code

$a = 3;
$a = 3 * $a++;  
echo $a; //9

$a = 3;
$a = $a * $a++;  
echo $a; //12

Here are VLD opcodes for 1st operation

compiled vars:  !0 = $a
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   ASSIGN                                                   !0, 3
   3     1        POST_INC                                         ~2      !0
         2        MUL                                              ~3      ~2, 3
         3        ASSIGN                                                   !0, ~3
   4     4        ECHO                                                     !0
   5     5      > RETURN                                                   1

for 2nd operation ($a * $a++)

compiled vars:  !0 = $a
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   ASSIGN                                                   !0, 3
   3     1        POST_INC                                         ~2      !0
         2        MUL                                              ~3      !0, ~2
         3        ASSIGN                                                   !0, ~3
   4     4        ECHO                                                     !0
   5     5      > RETURN                                                   1

2 questions :

  1. Why is the post-increment executed first? It doesn't make any sense to me. Traditionally, I thought it would increment the variable after performing all other operations in the expression. That's what it says on the official PHP site as well. So, by my logic (which might be incredibly flawed), both expressions would return 10. But as we can see, the POST_INC is being performed before anything else.

  2. As we can see, during the MUL operation, for first case, ~2 should be the result for the POST_INC (so value should be 4), which then multiplied by 3 is 12. But in the second case, where !0 is still 3, ~2 appears to hold a value of 3 as well, for reasons unknown to me, so we get 9 in the end. Why does this happen?

I'm not fluent with reading opcodes, so maybe i missed something, I'm guessing the order of operands ~2, 3 vs !0, ~2 matters, but I don't understand how.

Accomplished answered 23/1, 2020 at 14:32 Comment(0)
B
6

The key actor here is operator precedence and for that reason, despite being the last element in the expression, $a++ is evaluated first (before i. e. $a). Note the post in post increment there means action post evaluation of that expression (variable) and not evaluation of the whole expression (line of code).

In your first case, the code is this:

$result = 3 * $a++;

so the value of $a used for multiplication is 3 because it's read first and then incremented. There's no more $a used in that expression, so new value of $a does not really matter and will not affect us unless $a is referenced again:

     $a = 3
$result = 3 * $a++
        = 3 * 3 
                // $a is 4 now
        = 9

The second case is different:

$result = $a * $a++;

because we have more than one references to $a. The evaluation will got that way:

     $a = 3
$result = $a * $a++
        = $a * 3  // value of `$a` is 4 after post-increment
                  // evaluation, and this affects us as we 
                  // evaluate $a again
        = 4 * 3
        = 12

For answer completeness let's and add one more case:

$result = $a++ * $a++;

Evaluation would the go similarly, with different $a value at the end:

     $a = 3
$result = $a++ * $a++
         // $a is 4 now
        = 3 * $a++
        = 3 * 4
                 // $a is 5 now
        = 12

This looks clear once you understand that but from the other hand in nicely demonstrates how easily you can outsmart yourself, by writing code that you think you know how it works vs how it really works :) So you either need to read the language docs carefully to ensure you know for sure what code you wrote would do or just stay away from writing too "smart looking" code for sake of personal sanity during future debugging sessions :) KISS principle exists for a reason.

Boll answered 23/1, 2020 at 14:37 Comment(4)
I don't understand the link between your answer and the operator precedence. From that pag I see '*' is left-associative. Shouldn't that mean that $a is read first before $a++?Chaparajos
that's irrelevant here as ++ has higher precedence.Boll
Indeed, totally overlooked it. ThanksChaparajos
Indeed. And im guessing to understand what happens in the opcodes, in the first example the value (!0) is being incremented, but the return of post-increment is the unincremented value (~2) which is then used multiplication - so "3". For the second case, the incremented value (!0) is used for multiplication - so 4. So i was reading that wrong too, and that made me waste another hour just to become even more puzzled. Thank you for your answer.Accomplished

© 2022 - 2024 — McMap. All rights reserved.