Edit report at https://bugs.php.net/bug.php?id=29992&edit=1
ID: 29992 Comment by: email at stevemann dot net Reported by: fletch at pobox dot com Summary: foreach by reference corrupts the array Status: Not a bug Type: Bug Package: Scripting Engine problem Operating System: linux PHP Version: 5.0.1 Block user comment: N Private report: N New Comment: ras...@php.net asked "Ok, simple question then. Do you expect this to output 3?" foreach(array(1,2,3) as $b) { } echo $b; I would much prefer it not to output 3. Personally I think it would make a lot more sense and be a lot safer to have the array element references scoped to the foreach block - so effectively being unset after the block has run. Having the last element of the array floating around outside of the block is very dangerous in my view and can lead to silent errors. As someone else mentioned, I hate to think how much incorrect data there is out there because of the last array element being accidentally changed outside of the block. der...@php.net rather flippantly said: "no, we can't unset it by default, as people might use this for some weird reason." I can think of plenty of non-weird reasons why people might want this behaviour. But if it was unset by default, it's a simple matter to assign the reference to a variable defined outside of the block thereby making it available outside the foreach. In other words, like this: $c = NULL; foreach(array(1,2,3) as $b) { $c = $b; } unset($b);// simulates block-scoping of $b echo $c; This is not a bug, but I believe it's dangerous behaviour of PHP as it would seem quite logical to assume that the element references are scoped to the foreach block only - witness the many comments in this thread to that effect. So my vote would be to change this behaviour to block-scoping in a future version. Previous Comments: ------------------------------------------------------------------------ [2012-03-19 18:51:24] paul dot dillinger at gmail dot com Rasmus, Thanks for looking at this. I found the problem. I would still call it a bug, but I will describe it and you can decide. You are the man after all. You were right, I was passing a variable by reference in the few lines of code in front of my example above: foreach($this->data['external_'.$type] as &$item){ if(!empty($item['package'])){ $packages[] = $item; $library_names[] = $item['library_name']; unset($item); } } /* Code in example above goes here */ BUT, where I see this as a bug was: $packages (the array that was getting changed) was a new array created from the data of each $item. $packages was never being referenced, though the variable $item it was created from was. So, it should be a copy of the data and not THE data right? I fixed it by simply not trying to pass by reference and changing unset($item) to unset($this->data['external_'.$type]). Looking at it, that was the way to do it from the beginning. I see that, but I'm not sure why $packages gets changed down the road (it was correct immediately after this, but it and all copies of it change inside the next foreach). Any thoughts? ------------------------------------------------------------------------ [2012-03-01 18:52:14] ras...@php.net Paul, my guess is that $item is a reference to an element in the $packages array going into this loop. Try using a different variable there. ------------------------------------------------------------------------ [2012-03-01 18:31:08] paul dot dillinger at gmail dot com Rasmus, I think they might be having the same problem than I am where the array literally changes as soon as I enter the foreach. I've given an in depth explanation at: http://codeigniter.com/forums/viewthread/201487/ , but I'll give a summary here. I'm using a newer version of PHP (5.3.8) and foreach is corrupting my array even when it's not being passed by reference. My original code read something like this: if(!empty($packages)){ /* $this->data['external_js'] is normal */ foreach($packages as $item){ /* $this->data['external_js'] has changed */ I noticed that one of my javascript files that this function is packing in to a single package as not present. Even more odd was another was in the package twice. So I started logging the $this->data['external_js'] array to FirePHP to see where the error was happening. Strangely enough it happened immediately after a foreach. I decided to make a separate copy of the array as a "just in case" and report that. It changed the exact same way. I need to literally hand build my JS packages as I can't figure out any way to stop this array from changing once it enters the foreach. Here is the troubleshooting code with comments: if(!empty($packages)){ // checking to see if there are multiple files to be packaged together if($type=='js'){ // check to see if it's javascript as that was the package that had the problem $ext_js_for_firephp = $this->data['external_js']; // found that $this->data['external_js'] was changing so I assign it to a new variable exclusively for logging to FirePHP, this variable exists NO WHERE ELSE in the code. fb_log('$ext_js_for_firephp before', $ext_js_for_firephp); // Log to FirePHP /* fb_log function for reference function fb_log($Label,$Object=null){ $firephp = FirePHP::getInstance(true); if(empty($Object)){ $Object = $Label; $Label = NULL; } $firephp->log($Object, $Label); } */ } foreach($packages as $item){ // Starting the foreach if($type=='js'){ // Again problem was with JS package changing fb_log('$ext_js_for_firephp after', $ext_js_for_firephp); // Log to FirePHP, but now the value is different. } // AGAIN this happened before I started logging the vars, so logging is not causing the issue. It's not an error with the logging output, as this is exactly what the file being built had in it. /* RESULT */ /* Before FirePHP returns: $ext_js_for_firephp before = array( [0] => array( ['template_id'] => 30 ['js_id'] => 9 ['id'] => 9 ['library_name'] => 'modernizr' ['file_name'] => 'modernizr.min.js' ['version_major'] => 2 ['version_minor'] => 0 ['version_build'] => 6 ['static'] => 1 ['package'] => 0 ['footer'] => 0 ['priority'] => 100 ) [1] => array( ['template_id'] => 30 ['js_id'] => 12 ['id'] => 12 ['library_name'] => 'default' ['file_name'] => 'default.js' ['version_major'] => 0 ['version_minor'] => 0 ['version_build'] => 4 ['static'] => 1 ['package'] => 1 ['footer'] => 0 ['priority'] => 90 ) [2] => array( ['template_id'] => 37 ['js_id'] => 11 ['id'] => 11 ['library_name'] => 'jquery-ui-custom' ['file_name'] => 'jquery-ui-1.8.11.custom.min.js' ['version_major'] => 1 ['version_minor'] => 8 ['version_build'] => 11 ['static'] => 1 ['package'] => 0 ['footer'] => 0 ['priority'] => 0 ) ) */ /* After FirePHP returns: $ext_js_for_firephp after = array( [0] => array( ['template_id'] => 30 ['js_id'] => 9 ['id'] => 9 ['library_name'] => 'modernizr' ['file_name'] => 'modernizr.min.js' ['version_major'] => 2 ['version_minor'] => 0 ['version_build'] => 6 ['static'] => 1 ['package'] => 0 ['footer'] => 0 ['priority'] => 100 ) [1] => array( ['template_id'] => 30 ['js_id'] => 12 ['id'] => 12 ['library_name'] => 'default' ['file_name'] => 'default.js' ['version_major'] => 0 ['version_minor'] => 0 ['version_build'] => 4 ['static'] => 1 ['package'] => 1 ['footer'] => 0 ['priority'] => 90 ) [2] => array( ['template_id'] => 30 ['js_id'] => 12 ['id'] => 12 ['library_name'] => 'default' ['file_name'] => 'default.js' ['version_major'] => 0 ['version_minor'] => 0 ['version_build'] => 4 ['static'] => 1 ['package'] => 1 ['footer'] => 0 ['priority'] => 90 ) ) */ ------------------------------------------------------------------------ [2012-02-09 17:20:44] ras...@php.net What do you mean you con't care about the explanation? Ok, simple question then. Do you expect this to output 3? foreach(array(1,2,3) as $b) { } echo $b; If you do, then you don't want us to fix this "bug" because fixing it would mean $b is not 3 here. ------------------------------------------------------------------------ [2012-02-09 16:55:31] looris at gmail dot com No one cares about the technical explanation about why this happens at a low level. I'm quite puzzled you are still in denial about this bug. ------------------------------------------------------------------------ The remainder of the comments for this report are too long. To view the rest of the comments, please view the bug report online at https://bugs.php.net/bug.php?id=29992 -- Edit this bug report at https://bugs.php.net/bug.php?id=29992&edit=1