Call unset() after a PHP foreach loop with values passed by reference

I just spent a couple of hours debugging something really counterintuitive, where PHP’s print_r seemingly told me that an array had different content to the content that the same array contained according to a foreach loop. Consider this slightly contrived example code:

<?php
$fields = array( 'a' => 0, 'b' => 0, 'c' => 0, 'd' => 0, 'e' => 0, 'f' => 0 );
$i = 0;
foreach( $fields as $key => &$value )
{
	$value = ++$i;
}
print_r( $fields );
foreach( $fields as $key => $value )
{
	echo "$key => $value\n";
}

Now, call me crazy, but I expect both the print_r call and the second foreach loop to return pretty much the same information: the keys, a to f, and their corresponding values, 1 to 6. If you also expected that, you would be wrong. This is what you actually get:

Array
(
	[a] => 1
	[b] => 2
	[c] => 3
	[d] => 4
	[e] => 5
	[f] => 6
)
a => 1
b => 2
c => 3
d => 4
e => 5
f => 5

Yes, that’s right, the final key inexplicably appears to have the same value as the previous key, despite print_r suggesting otherwise.

Okay, so it’s not really inexplicable, just very unexpected. The reason is that the first foreach loop passes values by reference (&$value) while the second passes them by value ($value). As the PHP documentation states, “Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().”

Why? I have no idea. My intuition strongly suggests to me that $value should go out of scope after the first foreach loop, but apparently my intuition is wrong. Maybe PHP 7 will fix this madness, but at time of writing the stock PHP versions in Debian Jessie and Ubuntu Wily behave as outlined above.

Anyway, the short version is that you can avoid this by adding unset( $value ); immediately after the first foreach.

Comments

Add new comment

(If you're a human, don't change the following field)
Your first name.
(If you're a human, don't change the following field)
Your first name.

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.