Skip to content

Be wary of reference loops in PHP

The little bugger, a result of being overly clever, cost me an hour of debugging. Don't repeat my mistakes, clear your loops!

A while ago I refactored a piece of code that set some default values in an array. This is a simplified version of the original code:

function defaults($list) {
  foreach ($list as $key => $item) {
    if (!isset($item["created"])) {
      $list[$key]["created"] = time();
    }
  }

  return $list;
}

As the loop worked quite a number of indexes with various stuff, I thought it would be a bit more readable if we ditched the array-lookup and used a by-reference-loop instead:

function defaults($list) {
  foreach ($list as $key => &$item) {
    if (!isset($item["created"])) {
      $item["created"] = time();
    }
  }

  return $list;
}

All tests passed, the code got a bit more readable, everything was fine. Right up to the point someone added some more code to the function:

function defaults($list, $matching) {
  foreach ($list as $key => &$item) {
    if (!isset($item["created"])) {
      $item["created"] = time();
    }
  }
 
  foreach ($matching as $match) {
    $item = $match->referencing;
    $list[$item->id]['references'][] = $foo;
  }

  return $list;
}

What happened? The short story is, the second loop is overwriting the last item of $list in every iteration. That is because $item is still a reference to the last element of the array $list. Fortunately it's a rather easy fix.

Always unset() a reference loop:

foreach ($list as $key => &$item) {
  // do some funky stuff
}

unset($item);
$item = "safely re-usable";
Categories: php

Comments

Display comments as Linear | Threaded

No comments

The author does not allow comments to this entry