Pitfall in foreach ($ items as & $ item)

    Many people like to write such constructions in one form or another, everyone has come across:
    foreach ($items as &$item) {
        $item += 2;
    }
    

    But not many people suspect what danger is lurking here.
    Consider an example.

    Vasya Pupkin took the array, walked along it, increasing by two all the elements:
    $items = array(
        'a' => 10,
        'b' => 20,
        'c' => 30,
    );
    foreach ($items as &$item) {
        $item += 2;
    }
    print_r($items);
    

    I looked at the dump, saw that the problem was solved, and left satisfied:
    Array
    (
        [a] => 12
        [b] => 22
        [c] => 32
    )
    

    After some time, Petrovich decided to supplement this section of the code with another search, adding below:
    $newitems = array(
        'a' => 10,
        'b' => 20,
        'c' => 30,
    );
    foreach ($newitems as $key=>$item) {
        $newitems[$key] += 5;
    }
    print_r($newitems);
    

    He looked that his task was also solved, and with a sense of accomplishment closed the file:
    Array
    (
        [a] => 15
        [b] => 25
        [c] => 35
    )
    

    After some time, inexplicable bugs began to come out. Why?
    Let's do var_dump ($ items) at the end of the code:
    array(3) {
      ["a"]=>
      int(12)
      ["b"]=>
      int(22)
      ["c"]=>
      &int(30)
    }
    

    thirty! Vasya Pupkin swears that he checked. Why was 32, and after the Petrovich code 30?

    The reason lies in the ampersand. He reports that someone else refers to the marked data. When leaving, Vasya did not erase the temporary variable that he used to iterate over ($ item). The variable was used with permission to change the source ("&"), which is also called "assignment by reference". He was sure that the variable would be used only inside the loop. Petrovich, using a variable with the same name, during his enumeration, changed its value, and every time the place where this variable was stored changed. And it was stored in the same place as the last element of the Pupkin massif.

    Of course, in the case the article is exaggerated. In practice, such relationships can be very complex, especially if the project is inexpensive, and not enough experienced and fragmented web developers participate in it.

    How can you handle this?
    • Destroy temporary variables after use, especially if they have some kind of relationship with the data used:
      foreach ($items as &$item) $item += 2;
      unset($item);
      
    • Be careful with variables that have already been used by someone.
    • Encapsulate your actions in separate functions, methods or namespaces.
    • Use var_dump instead of print_r and pay attention to ampersand. To dump to a file, not to a browser, an alternative to print_r ($ var, true) would be such a construction:
      function dump() {
          ob_start();
          foreach(func_get_args() as $var) var_dump($var);
          return ob_get_clean();
      }
      

    In conclusion, I will say that bugs related to links can be not only in foreach. And all of them were once discussed. However, this case, judging from my experience, is so widespread in practice that it deserves special attention.

    Also popular now: