This question already has an answer here:
Here is a simple loop
$list = array("A", "B", "C","D");
foreach ($list as $var) {
print(current($list));
}
Output (demo)
BBBB // Output for 5.2.4 - 5.5.0alpha4
BCD // Output for 4.4.1
AAAA // Output for 4.3.0 - 4.4.0, 4.4.2 - 5.2.3
Question :
foreach
i should be getting AAAA
but not getting that in the current PHP
stable versionNote* I know i can simply use print $var
but the from PHP DOC
current — Return the current element in an array The current() function simply returns the value of the array element that's currently being pointed to by the internal pointer. It does not move the pointer in any way. If the internal pointer points beyond the end of the elements list or the array is empty, current() returns FALSE.
Update 1 - New Observation
Thanks to Daniel Figueroa : Just by wrapping current
in a function you get different result
foreach ( $list as $var ) {
print(item($list));
}
function item($list) {
return current($list);
}
Output ( Demo )
BCDA // What the hell
Question :
foreach
output ? Update 2
$list = array("A","B","C","D");
item2($list);
function item2($list) {
foreach ( $list as $var ) {
print(current($list));
}
}
Output ( See Demo )
AAAA // No longer BBBB when using a function
Question :
AAAA
outside and BBBB
in a function in most PHP version
Why does it start with B?
Since 5.2 foreach
(reliably) advances the array pointer before the loop body starts. See also the FE_RESET
opcode.
$list = array("A", "B", "C","D");
foreach ($list as $var) {
break;
}
var_dump(current($list));
Output:
B
This may have something to with how the ZEND_OP_DATA
pseudo opcode works (which isn't really documented).
Why does current()
keep giving the same value?
Before the loop starts, foreach
creates an internal reference to the array that you're looping over. Once inside the loop, whenever the array variable is modified or passed by reference, the internal reference is disassociated from the variable by making a copy of the array structure (but not the elements). This copied value retains the array pointer (which had earlier been modified by the loop initialization).
This behaviour is also exhibited with a more destructive unset()
operation:
$list = array('A', 'B', 'C', 'D');
foreach ($list as $key => $val) {
echo $val;
unset($list[1], $list[2], $list[3]);
}
echo "\n", print_r($list, true), "\n";
Output:
ABCD
Array
(
[0] => A
)
Passing loop variable to a function
This is another interesting scenario:
$list = array('A', 'B', 'C', 'D');
function itm($arr)
{
return current($arr);
}
foreach ($list as $item) {
print itm($list);
}
var_dump(current($list));
Output:
BCDA
bool(false)
This time, the array is passed by value and thus its array structure is copied (not the elements) into the function's $arr
parameter. Unlike the previous example, there's no disassociation between the loop's internal reference and the $list
symbol because the copy takes place in the function scope.
What about the last "A"
?
This is by far the most mystifying behaviour of foreach
and can only be witnessed under these circumstances. In the last loop iteration, the array pointer is seemingly rewound to the first item; seemingly because at the end of the loop it obviously points beyond the end of the elements (as you can see from the last line of the output).
This may have something to do with the SWITCH_FREE
opcode that's executed at the end of a foreach
.
So why does placing foreach
in a function make it different?
Observe the following code:
function item2($arr)
{
foreach ($arr as $var) {
print(current($arr));
}
var_dump(current($arr));
}
$list = array("A","B","C","D");
item2($list);
Output:
AAAA
string(1) "A"
In this case, the internal reference of the foreach
is initialized with a copy of the array (because it has a refcount > 1) and thus creates an immediate disassociation from the $arr
symbol.
Can it get worse?
Of course! You can get even whackier results when you start using references or nest multiple foreach
loops on the same variable.
So how can I get consistent results?
Use Iterators
or don't rely on getting a consistent value from referencing the array variable during a foreach
operation.
rewind
came from ? does it mean functions rewinds array ??? or return statement has effect on arrays - Baba
FROM PHP.net
The current() function simply returns the value of the array element that's currently being pointed to by the internal pointer. It does not move the pointer in any way
then: use next()
$list = array("A", "B", "C","D");
foreach ($list as $var) {
print(current($list));
next($list);
}
NOTE: the first element will not be printed because foreach moved the pointer to second element of the array :)
This example will explain the full behaviour:
$list = array("A", "B", "C","D");
foreach ($list as $var) {
if(!isset($a)) reset($list); $a = 'isset';
print(current($list));
next($list);
}
out put is ABCD
Please also note that:
As foreach relies on the internal array pointer changing it within the loop may lead to unexpected behavior.
EDIT: I want to share also my new confusing finding!!!
Example1:
$list = array("A", "B", "C","D");
$list_copy = $list;
foreach ($list as $key => $val) {
current($list_copy);
echo current($list);
//next($list);
}
OUTPUT: AAAA
Example2:
$list = array("A", "B", "C","D");
$list_copy = $list;
foreach ($list as $key => $val) {
current($list_copy);
echo current($list);
next($list);
}
OUTPUT: ABCD
When calling current() function inside foreach even for another array it will affect the foreach behavior...
Example3:
$list = array("A", "B", "C","D");
$refcopy = &$list;
foreach ($list as $key => $val) {
if(!isset($a)) { $a = 'isset'; reset($list); }
echo current($list);
next($list);
}
OUTPUT: ACD (WOW! B is missing)
Example: 4
$list = array("A", "B", "C","D");
$refcopy = &$list;
foreach ($list as $key => $val) {
echo current($list);
next($list);
}
OUTPUT: BCD
It can't be decided exactly what will happen inside foreach loop!!!
Well I have no real clue as to why this is, but i suspect it might have something to do with how the assignment is evaluated/processed. For fun I tried this and it resulted in another incorrect
behaviour:
$arr = array('A', 'B', 'C', 'D');
function itm($val) {
return current($val);
}
foreach ($arr as $item) {
print itm($arr);
}
Result: BCDA
So my guess is that whats happening here is that the function call forces the evaluation of current to happen in a ~correct manner. Also the reason for me getting BCDA instead of ABCD is probably because the internal pointer at first is incremented (pointing at B) and then in the en it is reset back to point at A.
It might be worth noting this line in the PHP doc:
Note that the assignment copies the original variable to the new one (assignment by value), so changes to one will not affect the other. This may also have relevance if you need to copy something like a large array inside a tight loop.
I guess this doesn't really count as an answer but I liked your question and wanted to contribute a little.
$arr
by reference and it outputs BBBB
. codepad.viper-7.com/6MxdFr - Leri
Even if a copy of the array was made by foreach i should be getting AAAA but not getting that in the current PHP stable version
Since I found no answer to this question here, I'll (try to) explain.
Before the first iteration of foreach
$list
is not actually copied. Only reference counting of $list
will be increased to 2. So on the first iteration: first value of $list
will be copied in $var
, pointer will move to the second element of $list
and actual copy of $list
will be made. So when you call current
pointer points to second element but on the second and farther iterations it's never modified because actual copy of $list
exists so current
always will output the second element.
Edit:
I've played with debug_zval_dump
to understand this really very unexpected behavior with:
<pre>
<?php
$list = array("A", "B", "C","D");
echo '<h1>Ref count before entering foreach:</h1><br>';
debug_zval_dump($list); echo '<br><br>';
$i = 0;
echo '<h1>Ref count in foreach:</h1><br>';
foreach ($list as $var) {
$i++;
echo '<b>Iteration #'.$i.':</b> ';
debug_zval_dump($list);
echo '<br>';
}
$list = array("A", "B", "C","D"); //re-assign array to avoid confusion
echo '<h1>Ref count before entering foreach that calls method "item" and passes array by value:</h1><br>';
debug_zval_dump($list);
$i = 0;
echo '<h1>Ref count in foreach that calls method "item" and passes array by value:</h1><br>';
foreach ( $list as $var ) {
$i++;
item($list, $i);
}
function item($list, $i) {
echo '<b>Iteration #'.$i.':</b> ';
debug_zval_dump($list);
}
$list = array("A", "B", "C","D"); //re-assign array to avoid confusion
echo '<h1>Ref count before entering foreach that calls method "item" and passes array by reference:</h1><br>';
debug_zval_dump($list);
$i = 0;
echo '<h1>Ref count in foreach that calls method "item" and passes array by reference:</h1><br>';
foreach ( $list as $var ) {
$i++;
itemWithRef($list, $i);
}
function itemWithRef(&$list, $i) {
echo '<b>Iteration #'.$i.':</b> ';
debug_zval_dump($list);
}
And got the following output:
Ref count before entering foreach:
array(4) refcount(2){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) }
Ref count in foreach:
Iteration #1: array(4) refcount(3){ [0]=> string(1) "A" refcount(2) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) }
Iteration #2: array(4) refcount(3){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(2) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) }
Iteration #3: array(4) refcount(3){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(2) [3]=> string(1) "D" refcount(1) }
Iteration #4: array(4) refcount(3){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(2) }
Ref count before entering foreach that calls method "item" and passes array by value:
array(4) refcount(2){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Ref count in foreach that calls method "item" and passes array by value:
Iteration #1: array(4) refcount(5){ [0]=> string(1) "A" refcount(2) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Iteration #2: array(4) refcount(5){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(2) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Iteration #3: array(4) refcount(5){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(2) [3]=> string(1) "D" refcount(1) } Iteration #4: array(4) refcount(5){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(2) } Ref count before entering foreach that calls method "item" and passes array by reference:
array(4) refcount(2){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Ref count in foreach that calls method "item" and passes array by reference:
Iteration #1: array(4) refcount(1){ [0]=> string(1) "A" refcount(4) [1]=> string(1) "B" refcount(3) [2]=> string(1) "C" refcount(3) [3]=> string(1) "D" refcount(3) } Iteration #2: array(4) refcount(1){ [0]=> string(1) "A" refcount(3) [1]=> string(1) "B" refcount(4) [2]=> string(1) "C" refcount(3) [3]=> string(1) "D" refcount(3) } Iteration #3: array(4) refcount(1){ [0]=> string(1) "A" refcount(3) [1]=> string(1) "B" refcount(3) [2]=> string(1) "C" refcount(4) [3]=> string(1) "D" refcount(3) } Iteration #4: array(4) refcount(1){ [0]=> string(1) "A" refcount(3) [1]=> string(1) "B" refcount(3) [2]=> string(1) "C" refcount(3) [3]=> string(1) "D" refcount(4) }
The output is little bit confusing.
In first example foreach
has created internal copy of $list
so reference count was 2 (4 in the result because debug_zval_dump
adds one refCount
). In the second example (pass by value) refCount
increased to 3, because $list
was copied for function. In the third example count kept to 1 because $list
was passed by value. I need some time to realize why. If you get the point out of this result share.
All I can say is that when we passed array by value foreach
was passing array that was iterating, but when passed by reference it took the original $list
. The question is: why was foreach
passing that array?
The code you use if a lie. Even literally it might look like the same code, however the variables are not (http://3v4l.org/jainJ).
To answer your actual question, for consistent results use the right tools.
If you need a variable with an array value, assign it:
$list = array(....);
If you need to get the current value of that array, use it before the foreach
:
$current = current($list);
Because inside the foreach
this might be the same variable name but the value will be different (imagine, you're iterating!).
If you need the current value per each iteration, use it:
foreach ($list as $current) {
...
}
See $current
?
Oh gosh, yeah, it was that easy. Wait I already have consistent results. Oh and it was that easy to not fool myself. Yay! ;)
For the log: Passing a variable as function parameter makes it a new variable. Even when a reference (that is explained).
When in doubt, do not use PHP references. Or not even variables: http://3v4l.org/6p5nZ
Great point out. But it seems memory pointing issue with different version of php. Also current gives only current position which you have not increment(navigated) anywhere so not getting proper output. As different version of php interpreting next and starting point of array in different ways a solution to this could be a reset inside the loop with some condition. (by the way looping and then using current, next prev is not a good way as already have object in var :) what ever it is your choice) This is one way you can get it work:
<?php
$list = array("A", "B", "C","D");
$flag =0;
foreach ($list as $var) {
if($flag==0)
{
reset($list);
$flag=1;
}
print(current($list));
next($list);
}
Output is ABCD. See at http://3v4l.org/5Hm5Y
$list = array("A", "B", "C","D");
foreach ($list as $var) {
echo $var;
}
Should do it.
current
inside a foreach, not how to obtain a specific output. - Charles
Use This you already know what's happen !
$list = array('A', 'B', 'C','D');
foreach ($list as $var) {
var_dump(current($list));
}
may be its help you!
arrays
andforeach
tags too because, IMO, it belongs to it. If you disagree revert my changes. :) - Leri