So far we've been using PHP 5.5 and everything seemed to be flowing too good with the code. Since upgrading it to 7 most of the foreach() seems to be having inconsistent behaviour.
For eg: Consider the snippet below:
$array = array('a', 'b', 'c');
self::testForeach($array);
.
.
.
// $array is passed by reference
public static function testForeach(&$array) {
foreach ($array as $key => $val) {
//produces a, b as an output in PHP 5
//produces a, b, c as an output in PHP 7
var_dump($val);
if ($val == 'b') {
//remove 'c' from the array
unset($array[2]);
}
}
}
Behaviour in PHP 5.5:
$array is passed by reference to testForeach() function. So removing "c" from $array inside the loop would directly modify the original array. Hence, the iterated values would be a, b and not c as it gets removed from the array in between.
Behaviour in PHP 7:
$array is passed by reference to testForeach() function. When $array is looped over foreach(), a copy is made say $arrayCopy (according to the doc) which is being iterated over the loop. So removing "c" value from $array would have no effect and will loop all the values contained in the $arrayCopy. Hence the output - a, b, c.
Changing foreach to pass-by-ref is not a soln for me as there are too many foreach on my project and I can't grep and modify each of them.
Is there any other handling done for such behaviour on the latest version. Any tool/parser which could highlight them?
Any hints/ideas ?
Thanks!
Only if you loop the array by reference: foreach ( $array as $key => &$val ) {
Afaik then NO copy is being made. http://php.net/manual/en/control-structures.foreach.php
BE WARNED: in this case the $val remains as a pointer to the last element of the array, a best practice is to unset it.
foreach ( $array as $key => &$val ) {
<?php
$array = array();
testForeach( $array );
// $array is passed by reference
function testForeach( &$array )
{
$array = array( 'a', 'b', 'c' );
foreach ( $array as $key => &$val ) {
//produces a, b as an output in PHP 5
//produces a, b, c as an output in PHP 7
var_dump( $val );
if ( $val == 'b' ) {
//remove 'c' from the array
unset( $array[ 2 ] );
}
}
unset($val);
}
Beside it seems odd to modify the array while iterating, in PHP 7 you can force a reference in the iterate variable:
$array = array('a', 'b', 'c');
foreach ($array as $key => &$val) {
var_dump($val);
if ($val == 'b') {
//remove 'c' from the array
unset($array[2]);
}
}
Will return on both PHP 5 and 7:
string(1) "a"
string(1) "b"
See in action on 3v4l.org.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With