Page 1 of 1

passing a variable in foreach (bug og strange behaviour?)

Posted: Thu Jul 10, 2008 5:22 am
by alfmarius
Hello :)

I'm experiencing some strange behaviour in passing a variable in a foreach:

Code: Select all

$a = array("a" => 1, "b" => 0, "c" => 1);
foreach ($a as $key => &$value) {
  if ($value) {
    $value++;
  } else {
    unset($a[$key]);
  }//if-else
}//foreach
 
Outputs:
 
Array
(
    [a] => 2
    [c] => 1
)
 
My original array is much larger, but $a is enough to explain my point here.
I would expect $a[c] to be 2 also, but it isn't, and I have a very hard time
to try to explain to myself why it is like this..

Any ideas ?

:roll:

Re: passing a variable in foreach (bug og strange behaviour?)

Posted: Thu Jul 10, 2008 6:34 am
by jayshields
Don't know about fixing what you already have, but try this:

Code: Select all

 
$a = array("a" => 1, "b" => 0, "c" => 1);
foreach ($a as $key => $value) {
  if ($value) {
    $a[$key]++;
  } else {
    unset($a[$key]);
  }//if-else
}//foreach
 

Re: passing a variable in foreach (bug og strange behaviour?)

Posted: Thu Jul 10, 2008 8:47 am
by alfmarius
Thanks. Yes I know this is also possible, in fact it is how I usually do things. Then I wanted to experiemnt with passing variables, because it looks and reads better. Thats when i stumbled uplon this strange behaviour. Could it be a bug?

Re: passing a variable in foreach (bug og strange behaviour?)

Posted: Thu Jul 10, 2008 9:01 am
by VladSun
alfmarius wrote:Could it be a bug?
Sure :)
http://bugs.php.net/bug.php?id=29992

Re: passing a variable in foreach (bug og strange behaviour?)

Posted: Thu Jul 10, 2008 10:38 am
by alfmarius
VladSun wrote:
alfmarius wrote:Could it be a bug?
Sure :)
http://bugs.php.net/bug.php?id=29992
Thanks for the reply Vlad, but after reading that bug report link, the conclusion of it was that it really wasn't a bug, just strange behaviour. And the case was not exactly the same, in the example they used two foreach loops. Maby the point would be the same, I just couldn't wrap my head around it.

Anyway, I did some more testing:

Code: Select all

$array = array("a" => 1, "b" => 0, "c" => 1);
foreach ($array as $key => &$value) {
var_dump($value);print "<br>";
var_dump($array);print "<br>";
 if ($value) {
   $value = 5;
 } else {
   unset($array[$key]);
 }//if-else
}//foreach
print "<br>";
var_dump($array);
This will output:

Code: Select all

int(1)
array(3) { ["a"]=> ?(1) ["b"]=> int(0) ["c"]=> int(1) }
int(0)
array(3) { ["a"]=> ?(5) ["b"]=> ?(0) ["c"]=> int(1) }
int(1)
array(2) { ["a"]=> ?(5) ["c"]=> int(1) }
 
array(2) { ["a"]=> int(5) ["c"]=> int(1) }
There is a new character ? that I'm not familiar with.
Everything goes as expected untill we get to b which is 0, unsets it and goes on to c. What seems to be the bug/problem is when $value is 0 and we unset the array-element that $value points to. I would expect $value to just point to the next element when going to the top of the foreach, but this doesn't happen. I have a very hard time to find out what really happens, if it is logical (and very strange) or if it is a bug.

Any comments on this last "insight"?

..it's fun to dig deep isn't it? ;)

Re: passing a variable in foreach (bug og strange behaviour?)

Posted: Fri Jul 11, 2008 2:28 am
by VladSun
PHP 5.2.5

Code: Select all

$array = array("a" => 1, "b" => 0, "c" => 1);
foreach ($array as $key => &$value) {
var_dump($value);print "<br>";
var_dump($array);print "<br>";
 if ($value) {
   $value = 5;
 } else {
   unset($array[$key]);
 }//if-else
}//foreach
print "<br>";
var_dump($array);
Output:

Code: Select all

int 1
 
array
  'a' => &int 1
  'b' => int 0
  'c' => int 2
 
int 0
 
array
  'a' => int 5
  'b' => &int 0
  'c' => int 2
 
int 2
 
array
  'a' => int 5
  'c' => &int 2
 
array
  'a' => int 5
  'c' => &int 5
Sorry, couldn't reproduce it.
And yes - I should have read the rest of the page I pointed to :)

Re: passing a variable in foreach (bug og strange behaviour?)

Posted: Fri Jul 11, 2008 2:46 am
by Benjamin
Note: Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it.
http://us3.php.net/manual/en/control-st ... oreach.php

$value is not referenced to $array[%n] and even if it was $array[%n] would be a copy of the original. Why would you expect that to work? Additionally, even if you were working with a referenced copy, the manual states that changing the content of an array while your iterating it with a foreach loop can lead to unpredictable results.

Re: passing a variable in foreach (bug og strange behaviour?)

Posted: Fri Jul 11, 2008 5:55 am
by alfmarius
Hi Astions, thank you for taking your time with this :)
astions wrote:
Note: Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it.
http://us3.php.net/manual/en/control-st ... oreach.php
"foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it."
I find this piece of the sentence a little disturbing. And the whole sentence starts with "Unless the array is referenced..", so I'm not sure if they mean the internal pointer in an unreferenced or referenced foreach. It doesn't state what are the side effects, and to what degree we can actually trust a referenced foreach. But I clearly get the message that one might experience some smelly fish while doing unordinary things in a referenced foreach loop.
astions wrote: $value is not referenced to $array[%n] and even if it was $array[%n] would be a copy of the original. Why would you expect that to work? Additionally, even if you were working with a referenced copy, the manual states that changing the content of an array while your iterating it with a foreach loop can lead to unpredictable results.
Now I'm confused. $value is not a reference to an arrays values in a foreach loop?? I thought that was the point of &$value.. I couldn't find anywhere in the foreach manual that stated that changing the content of an array while foreach-ing it can lead to unpredictable results (maby you mean somewhere else, or in the comments? ..didn't read all of them)
VladSun wrote:Sorry, couldn't reproduce it.
And yes - I should have read the rest of the page I pointed to :)
That reply might be the one that cleared things up a little bit. It, and the fact that I am useing PHP 5.2.0-8+etch11 myself, got me into searching the changelog for more answers. I think I found something in this one:

Bug #35106: nested foreach fails when array variable has a reference
http://bugs.php.net/bug.php?id=35106

The scenario described is not exactly the same as mine, but it certainly resembles it. And considering that this bug was fixed between v5.2.0 and v5.2.1, it might be the fix that makes your (Vlads) results differ from mine. I suspect you have a typo with the c value being 2 and not 1 though? ;)

To conclude: It's seems the suspicious behavior I was experiencing in fact was a bug, that has now been fixed with PHP versions >= 5.2.1

:mrgreen:

Re: passing a variable in foreach (bug og strange behaviour?)

Posted: Fri Jul 11, 2008 7:58 am
by VladSun
alfmarius wrote:I suspect you have a typo with the c value being 2 and not 1 though? ;)
Oh ...
My code was:

Code: Select all

$array = array("a" => 1, "b" => 0, "c" => 2);
So I was able to differ "a" and "c".