php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #16227 Using internal hash position is tricky.
Submitted: 2002-03-22 19:52 UTC Modified: 2004-02-17 12:43 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:0 of 0 (0.0%)
From: php dot net at alienbill dot com Assigned:
Status: Closed Package: Documentation problem
PHP Version: Irrelevant OS: ANY
Private report: No CVE-ID: None
 [2002-03-22 19:52 UTC] php dot net at alienbill dot com
nested while(list=each) produces weird results.
this code

<?php
       $outsidearray = array("key1","key2");
       $insidearray = array("0","1");

           while(list(,$outerval) = each($outsidearray)){
               //$placeholder = $insidearray;
               while(list(,$innerval) = each($insidearray)){
                       print "inloop $innerval for $outerval<br>";
               }
       }
?>

only gets to key1 of the outer loop.
But if you uncomment the $placeholder line,
it works ok.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2002-03-22 20:19 UTC] yohgaki@php.net
Thanks for reporting. I verified with 4.2.0-dev & 4.0.6.
This is _VERY_ bad bug...

[root@dev etc]# php
<?php
       $outsidearray = array("key1","key2");
       $insidearray = array("0","1");

           while(list(,$outerval) = each($outsidearray)){
               //$placeholder = $insidearray;
               while(list(,$innerval) = each($insidearray)){
                       print "inloop $innerval for $outerval<br>";
               }
       }
?>

inloop 0 for key1<br>
inloop 1 for key1<br>

[root@dev etc]# php
<?php
       $outsidearray = array("key1","key2");
       $insidearray = array("0","1");

           while(list(,$outerval) = each($outsidearray)){
$placeholder = $insidearray;
               while(list(,$innerval) = each($insidearray)){
                       print "inloop $innerval for $outerval<br>";
               }
       }
?>

inloop 0 for key1<br>
inloop 1 for key1<br>
inloop 0 for key2<br>
inloop 1 for key2<br>

[root@dev etc]# 
[root@dev etc]# php -v
4.2.0-dev

 [2002-03-22 20:29 UTC] hholzgra@php.net
ever heard of reset() ?
 [2002-03-22 20:48 UTC] yohgaki@php.net
I forgot about reset() since behavior is inconsistent :(

Anyway, this is still not right.
Zend engine is allocating new zval for $insidearray while
it should allocate $placeholder.
---
$placeholder = $insidearray;
               while(list(,$innerval) = each($insidearray)){
                       print "inloop $innerval for $outerval<br>";
---
This bug causes nasty bug in script.
If it is not easy to fix, may be we should suspend this bug?

 [2002-03-22 20:55 UTC] yohgaki@php.net
BTW, I haven't check the exact behavior.
Zend might be resetting internal hash position due to the
assignment. 
 [2002-03-23 06:01 UTC] derick@php.net
Not a bug, but intended behavior. Use foreach().

Derick
 [2002-03-23 06:39 UTC] yohgaki@php.net
Derick, 

foreach() does not solve this bug.
ZendEngine is resetting internal hash position by an
assignment. Therefore, foreach() does does not work.

i.e. The issue is _not_ "Not resetting hash posisiton", but
"Incorrectly resetting RVALUE hash position by an assignment".

This is serious flaw in language.

 [2002-03-23 06:46 UTC] derick@php.net
It's not a flaw, it's designed like this.

Derick
 [2002-03-23 07:10 UTC] yohgaki@php.net
Ok. Then this is documentation problem.
(I would like to hear from Andi or Zeev, though)

There should be note that internal hash position is reset if value is assigned. (Both LVALUE and RVLAUE. They are the same value anyway.)

Also, reference counting side effect should be documented fully.

BTW, to fix this misbihavior, we need a additional variable to keep track of hash position for each zval. (and change related codes) PHP3 does not have problem, since it does not have reference counting. 
 



 [2002-03-23 07:44 UTC] php dot net at alienbill dot com
OK, as a newbie PHP-er (experienced with Perl and Java) the documentation I've been using suggested using that example before foreach().  The foreach() behavior is free from the problem it seems, even though documentation for foreach() on php.net claims that they should be equivalent.  

Can anyone explain to this newbie why the (while list each) version was the "intended behavior"?  Of course, having to use reset() seems kind of odd to me anyway, but I wonder what was "wrong" with my first code... PHP seems to look like it has Perl's "more than one way to do it", but only in very limited areas. (I suppose if push came to shove I could go back to using my own array walkers, but still...)
 [2002-03-23 20:39 UTC] yohgaki@php.net
Both each() and foreach() uses internal hash position variable. 

Difference is foreach() reset internal hash position to the first element while each() does not.

A problematic behavior is an assignment resets internal hash
position. There are other cases programmer has to be careful about internal hash position and reference counting feature.

In addition to that, it is _not_ intuitive that assignment as follows

$arr = array('a', 'b', 'c');

does not reset hash posiiton, while following assignment does reset hash position.

$arr2 = $arr1;

Anyway, some internal hash posision behaviors are inconsistent and _not_ intuitive at all. This would be really hard to find if a user experience the problem. Therefore, it should be documented :)


 [2002-03-23 20:44 UTC] yohgaki@php.net
Oops,

$arr = array('a', 'b', 'c');

does reset position :)
 [2002-07-16 11:22 UTC] hholzgra@php.net
each() as a function does not know in which context it was called so it just returns the 'current' element and advances the internal position pointer, very similar to the java next_element() iterator (hope i got the name right). when no more elements are left it returns false until being reset

each() can not know that it was called from different loop runs so it will return false forever until reset. one might 
think about auto-reseting it after returning false once, but backwards compatibility will be in our way once again here

the problem does not happen this way in java because there next_element() advances the internal pointer before returning values and you have to use first_element() to get
the first one (which implies a reset)

foreach uses a private copy of the array structure so it has its own internal pointer which is recreated for every foreach instance so the problem does occure here
 [2002-10-01 06:13 UTC] yohgaki@php.net
The issue is ZendEngine mess up hash position by assignment.
I'll open new one for this.
 [2002-10-01 06:28 UTC] yohgaki@php.net
FYI. The bug is fixed. Current CVS version does not swap internal hash position on assinment.
 [2003-01-20 22:54 UTC] vdvo at vdvo dot net
Well, so is it fixed or is it not? This bug's status is still Open.
 [2004-02-17 12:43 UTC] irchtml@php.net
Closing this as yohgaki@php.net said the bug was fixed.
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Tue Oct 19 17:03:33 2021 UTC