php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #26396 foreach scope modality
Submitted: 2003-11-25 04:58 UTC Modified: 2003-11-26 17:12 UTC
Votes:8
Avg. Score:4.5 ± 0.7
Reproduced:4 of 6 (66.7%)
Same Version:1 (25.0%)
Same OS:1 (25.0%)
From: php dot net dot 1 at odi dot ch Assigned:
Status: Wont fix Package: Arrays related
PHP Version: 4.3.3 OS: *
Private report: No CVE-ID:
Have you experienced this issue?
Rate the importance of this bug to you:

 [2003-11-25 04:58 UTC] php dot net dot 1 at odi dot ch
Description:
------------
The behaviour of foreach seems to be scope dependent. The following code (slightly more than 20 lines) should yield the same results in both cases, but doesn't.

I know that foreach uses the internal array pointer. The result beeing "de" or "de fr it" is NOT the topic here. The point is that the two results differ, although the code is the same except for the scope.

This could be the reason for bug #19285

Reproduce code:
---------------
<?php
$usr_langs = array('de', 'fr', 'it');

function f() {
  global $usr_langs;
  
  foreach($usr_langs as $lang) {
    # do something 
  } 
}

function g() {
  global $usr_langs;
  
  foreach ($usr_langs as $lang) {
    f();
    echo "$lang "; 
  }
}

echo "Test1:<br>";
g();
echo "<br>----------<br>";


echo "Test2:<br>";
foreach ($usr_langs as $lang) {
  f();
  echo "$lang "; 
}

?>

Expected result:
----------------
Test1:
de
----------
Test2:
de


OR even better

Test1:
de fr it
----------
Test2:
de fr it 

Actual result:
--------------
Test1:
de
----------
Test2:
de fr it 

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2003-11-25 12:36 UTC] helly@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

You can not nest foreach calls.
 [2003-11-25 13:12 UTC] jpatrin at pnicorp dot com
You *CAN* nest foreach loops, as I have been doing it for a LONG time. You can even nest foreach loops with the same array and the output will be as expected (see code at bottom). Because foreach works on a copy of the array, it does not change the internal pointer and therefore there are two bugs here. The first being that the outputs aren't the same and second being that all values int he array are not output by g().

What seems to be happening if that f() is somehow altering the internal pointer of the *copy* that g() is operating on. Is it almost certain that this is a problem with how global is implemented.

This code:
<?php
foreach($usr_langs as $lang) {
  echo "1 $lang<br/>";
  foreach($usr_langs as $lang2) {
    echo "2 $lang2<br/>";
  }
}
?>
Produces this output:
1 de
2 de
2 fr
2 it
1 fr
2 de
2 fr
2 it
1 it
2 de
2 fr
2 it
 [2003-11-25 13:22 UTC] jpatrin at pnicorp dot com
Here's the proof that the global keyword is broken. If you change the code to use $GLOBALS as such:
<?php

function f() {

  foreach($GLOBALS['usr_langs'] as $lang) {
  }
}

function g() {

  foreach($GLOBALS['usr_langs'] as $lang) {
    f();
    echo $lang.' ';
  }
}

echo 'Test1:<br>';
g();
echo '<br>----------<br>';

echo 'Test2:<br>';
foreach($usr_langs as $lang) {
  f();
  echo $lang.' ';
}

?>

The output is:

Test1:
de fr it
----------
Test2:
de fr it 

As was originally expected. Please either open this bug again or explain why global is treated differently than $GLOBALS.
 [2003-11-25 16:08 UTC] helly@php.net
Interesting, anybody?
 [2003-11-25 16:27 UTC] jpatrin at pnicorp dot com
Here's a bit more. If you use 

$usr_langs =& $GLOBALS['usr_langs'];

instead of

global $usr_langs;

the same bug presents it self.

Also, if you put

global $usr_langs;

above the echo "Test2..." You get only "de" in the output. It seems like global is munging the scope of foreach copies.
 [2003-11-25 17:21 UTC] helly@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

I was right then. Global creates a reference and referenced arrays cannot be nested. When an array is passed to foreach and it is not a reference then a copy of the array is created. That's where the difference comes from.
 [2003-11-25 18:11 UTC] jpatrin at pnicorp dot com
Ok, I'll accept that response, but why does foreach not make a copy of the referenced array? I see no place in the foreach docs that say that it doesn't make a copy when the variable is a reference.

Sidenote: I thought that all PHP vars were refernces and that usinf =& made it a refernce to the same object instead of a refernce to a copy of the object. If this is true, the copy should still be made just fine. foreach is ALWAYS supposed to make a copy of the array and foreach over that.
 [2003-11-26 17:12 UTC] helly@php.net
PHP variables are implemented as refcounted unions in c. A reference in PHP means a flag in php which is the difference of making copies or working on the original memory. PHP Objects are handles in PHP 5 so copying it doesn't make a difference - only the handle is copied.
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Thu Apr 17 01:01:56 2014 UTC