Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Post-increment acting weird

Tags:

php

opcode

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.

like image 639
ChungaChanga Avatar asked Jan 23 '20 14:01

ChungaChanga


1 Answers

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.

like image 119
Marcin Orlowski Avatar answered Sep 20 '22 03:09

Marcin Orlowski