php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #60534 Iterating an array twice w and w/o references changes last item to second last
Submitted: 2011-12-15 15:15 UTC Modified: 2020-08-28 03:38 UTC
From: aron dot cederholm at gmail dot com Assigned:
Status: Duplicate Package: Arrays related
PHP Version: 5.3.8 OS: Arch Linux
Private report: No CVE-ID: None
 [2011-12-15 15:15 UTC] aron dot cederholm at gmail dot com
Description:
------------
Iterating an array twice, first time with references, second time *without* 
references, will trigger a bug in the second iteration where the last item of the 
array is overwritten by the second last item in the array. This seem to be the 
same bug as #46123, which was dismissed on erroneous grounds




Test script:
---------------
<?
# For a more verbose look at this bug, watch http://pastebin.com/gq2qekYh

$array = array('a', 'b', 'c', 'd'); # testing array
foreach ($array as &$a) {}
foreach ($array as $a) {}
echo join(',', $array); # will produce a,b,c,c *not* a,b,c,d as expected
die();

## while, these will work as expected

# 1
foreach ($array as $a) {}
print_r($array);
die();

# 2
foreach ($array as &$a) {}
print_r($array);
die();

# 3
foreach ($array as &$a) {}
foreach ($array as &$a) {}
print_r($array);
die();

# 4
foreach ($array as $a) {}
foreach ($array as $a) {}
print_r($array);
die();

# 5
foreach ($array as $a) {}
foreach ($array as &$a) {}
print_r($array);
die();

?>

Expected result:
----------------
a,b,c,d

Actual result:
--------------
a,b,c,c

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-12-15 17:09 UTC] anon at anon dot anon
This gets submitted as a bug about twice a month. It's still not a bug.

After the first loop finishes $a is a reference to the last element of the array. That means that during the second loop, the last value of the array is constantly being modified by the loop. Once it gets to the last element, 'd' has been overwritten already.

foreach ($array as &$a) {
	echo "($a): " . join(',', $array) . "\n";
}
foreach ($array as $a) {
	echo "($a): " . join(',', $array) . "\n";
}

(a): a,b,c,d
(b): a,b,c,d
(c): a,b,c,d
(d): a,b,c,d
(a): a,b,c,a
(b): a,b,c,b
(c): a,b,c,c
(c): a,b,c,c

As it says in the manual, unset() after foreach'ing by reference: http://www.php.net/manual/en/control-structures.foreach.php
 [2011-12-15 18:02 UTC] cataphract@php.net
-Status: Open +Status: Duplicate
 [2020-08-28 03:32 UTC] supplier at enex dot net
Can anyone please provide an example of how this unusual behaviour might be useful? Having pulled my hair out (like many others no doubt) it seems odd that this behaviour persists unless there is an obvious point to it (other than to separate the super-geeks from the lesser-geeks) For super-geeking there is always the standard for loop, so if there is a reason to punish us lesser geeks with this foreach quirk please advise what the use-case is. 

thanks a million!
 [2020-08-28 03:38 UTC] requinix@php.net
@supplier: It's not a quirk. It's a reference. A natural consequence of the fact that PHP does not have block scopes.
Protip: if you think the answer to a problem is to use a reference, think again.
 [2020-08-28 03:42 UTC] supplier at enex dot net
Awesome quick response thanks! This is why I love php, huge community!

Though I have no idea what that meant ;-p 

FYI for anyone having this issue just prefix your foreach with an unset:

unset($value); //if $value already exists you better unset it first ..or tear your hair out!
foreach ($array as $value) { // etc
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Jul 25 17:01:27 2024 UTC