Bug #29992 [Com]: foreach by reference corrupts the array

From: Date: Fri, 20 Jun 2014 22:09:57 +0000
Subject: Bug #29992 [Com]: foreach by reference corrupts the array
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-186280@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=29992&edit=1 ID: 29992 Comment by: steve at fancyguy dot com 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: I have done this intentionally in some very lazy prototypes. foreach($data as $k => &v) { if (meets_complex_precondition($k)) { break; } } // do some stuff with $v potentially modifying it later unset($v); Comes in handy on occasion when working recursively by reference in deep arrays. Neat trick like 'array_map(null, $arr1, $arr2)'. Previous Comments: ------------------------------------------------------------------------ [2014-02-11 03:34:59] xjis at msn dot com well, i guess to be more exact, i should've said php doesn't have block scope support whereas java/c++ does. now i see that even javascript has added block scoping starting 1.7 (need to use 'let' instead of 'var' to declare block-scoped variables) ------------------------------------------------------------------------ [2014-02-11 00:27:18] xjis at msn dot com guys... i read through this entire thread and now i totally get it. this is just a weird "side-effect" of having a language being "function-level scope" instead of "block-level scope" and with referencing support. and as long as the language stays being 'function-level scope' that also has the referencing support, this is the expected behavior; and there's no easy way around it. what ppl are expecting is exactly that of a block-level scope languages: having &v not referencing anything after for-loop block ended. that's why ppl are keep referring to C/C++/Java because these are all block-level scope languages; and why everyone wants for-loop to "auto-unset" the &v after for-loop block ended; that would basically emulate/mimic the behavior of block-level scope languages; but that is simply not the expected behavior for function-level scope languages so you can't just make a special case only for for-loop blocks --why would you make a special exception just for for-loop; why not for the entire language? then now, you are asking PHP language to completely change itself from being a function-level scope language into a block-level scope language btw, javascript is also a function-level scope language, but it doesn't have this particular problem because it doesn't support referencing ------------------------------------------------------------------------ [2013-12-04 21:50:33] gray dot bowman at gmail dot com Just wanted to chime in that 9 years on, this is still totally unexpected behavior. Today, two professional developers with years experience spent a couple hours trying to figure out why an array that was demonstrably intact had its last element corrupted for apparently no reason once entering a foreach. ------------------------------------------------------------------------ [2013-05-21 06:38:43] email at stevemann dot net Agreed this is not a bug, it's expected behaviour. But it's dangerous as it can slip by without being noticed. It almost certainly means there are thousands of sites which are exhibiting wrong behaviour because of this and no-one realises. Surely the concept of scoping the 'as' variable to the foreach enclosure only can't be considered bad form. It would make so much more sense to 'opt-in' to retrieving the variable outside of the enclosure (by assigning to another persistent variable within the enclosure) rather than the current 'opt-out' system (using unset()) which, unless you happen to have read the warning is HIGHLY DANGEROUS. ------------------------------------------------------------------------ [2013-05-20 15:57:18] paul dot dillinger at gmail dot com OK, I went over this some more. <pre> <?php // Fresh array $clean = array(1,2,3,4); foreach($clean as &$item){ // Nothing is modified in the array, but $item now exists } /*############################################################################## * $item persists outside of foreach and is now $clean[3] * See the warning on http://php.net/manual/en/control-structures.foreach.php * print_r($item); // would return 4 you you uncommented this. * unset($item); // This would remove the pointer to $clean[3]. Expected. *############################################################################*/ echo "A:\n"; /*A*/ print_r($clean); // $clean is still unmodified echo "B:\n"; foreach($clean as $item){ /*############################################################################## * Using AS $item SETS $item TO the current $item value (a.k.a. $clean[0], etc.) * Essentially foreach($clean as $item) is short hand for something like: * $x=0;while($x < count($clean)){$item=$clean[$x]; ### your code ### $x++;} * The problem I had was that I did not expect foreach to be able to set on call *############################################################################*/ /*B*/ print_r($clean); } ?> </pre> So creating the variable is documented, and it isn't a bug. The ability to set the value could be made clearer though. ------------------------------------------------------------------------ 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

« previous php.bugs (#186280) next »