(examples at the bottom!!!)
we have just upgrade our backend to PHP7 and after that, we have found a bug in our code related to an ArrayObject.
The code just loops over a copy of an Object (type native ArrayObject). The foreach iterates by value.
The purpose of the code is to filter some values that you don't need. In the example, if the iterated value is "two" or "three", unset it. I have tried it using iterator instead of the copied value and without the iterator.
Results:
First loop => $key = 0, $value = "one" // continue
Second loop => $key = 1, $value = "second" // unset
Third loop => $key = 3, $value = "four" // WTF? where is the $key = 2, $value = "three"????
So I cannot understand what's going on. Our temporal solution is to iterate over the original object and unset from the copy. Does anybody knows which change in the PHP core (or ArrayObject/ArrayIterator) makes this? I have search about it but some people has this problem with foreach were the item iterated is by reference.
If you switch between PHP 5.6 and 7, the behaviour changes.
Example 1 (with iterator)
$elements = new ArrayObject();
$elements->append('one');
$elements->append('two');
$elements->append('three');
$elements->append('four');
print_r($elements);
$clone = clone $elements;
$it = $clone->getIterator();
echo "\n------\n";
foreach ($it as $key => $value) {
echo $key."\t=>\t".$value."\n";
if ($value == 'two' || $value == 'three') {
$it->offsetUnset($key);
}
}
echo "\n------\n";
print_r($clone);
Example 2 (without iterator)
$elements = new ArrayObject();
$elements->append('one');
$elements->append('two');
$elements->append('three');
$elements->append('four');
print_r($elements);
$clone = clone $elements;
echo "\n------\n";
foreach ($clone as $key => $value) {
echo $key."\t=>\t".$value."\n";
if ($value == 'two' || $value == 'three') {
$clone->offsetUnset($key);
}
}
echo "\n------\n";
print_r($clone);
Thanks so much!
From my understanding , it is considered a bad practice to
modify an array while looping through it, and the proper way to do it would be using array_filter
.
Since you have an ArrayObject
, one solution would be to export it to an array, filter it using array_filter
and create a new ArrayObject
from the filtered array.
See also here : Filter ArrayObject (PHP)
Probably this behavior is due to the fact that loops are handled differently in php7. As mentioned here: http://php.net/manual/en/control-structures.foreach.php in php5 foreach
uses an internal array pointer in contrast to php7.
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