Unexpected behavior in foreach when looping and disconnecting in ArrayObject. The item is ignored.

( examples below! )

we just upgraded our server to PHP7 and after that we found an error in our code related to ArrayObject.

The code simply iterates over a copy of the object (type native ArrayObject). The foreach identifier iterates by value.

The purpose of the code is to filter out some values ​​that you do not need. In the example, if the iterated value is two or three, disable it. I tried this using an iterator instead of the copied value and without an iterator.

Results:

- Iterator

  • PHP 5.6: works as expected, the return value is an array with no two or three
  • PHP 7: it only removes “two” and it seems that an element with a value of “three” is not evaluated (see the echo inside the loop, it does not print “three”)

- No iterator

  • PHP 5.6: receives a notification, but works as expected, the return value is an array with no two or three
  • PHP 7: it only removes “two” and it seems that an element with a value of “three” is not evaluated (see the echo inside the loop, it does not print “three”)

First loop => $ key = 0, $ value = "one" // continue

Second loop => $ key = 1, $ value = "second" // unset

Third loop => $ key = 3, $ value = "four" // WTF? where $ key = 2, $ value = "three" ????

Therefore, I cannot understand what is happening. Our temporary solution is to iterate over the original object and not use it. Does anyone know what changes in the PHP core (or ArrayObject / ArrayIterator) do this? I have a search on this, but some people have this problem with foreach if the element was repeated by reference.

If you switch between PHP 5.6 and 7, the behavior changes.

Example 1 (with an 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 an 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); 

Many thanks!

+7
arrays php foreach php-7 arrayobject
source share
1 answer

From my point of view, it is considered bad practice to change the array when passing through it, and the correct way to do this would be to use array_filter .

Since you have an ArrayObject , one solution would be to export it to an array, filter it with array_filter and create a new ArrayObject from the filtered array.

See also here: ArrayObject Filter (PHP)

This behavior is probably due to the fact that php7 handles it differently. As mentioned here: http://php.net/manual/en/control-structures.foreach.php in php5 foreach uses an internal array pointer as opposed to php7.

0
source share

All Articles