rfc:php7_foreach

This is an old revision of the document!


PHP RFC: Fix "foreach" behavior

Introduction

The behavior of foreach statement in PHP for some edge cases is not exactly defined. Actually, it is implementation driven and quite weird.

Most these edge cases are related to manipulation with internal pointer and modification of elements of array, iterated by foreach. The behavior may depend on the value of reference-counter or the fact - if the value is a reference or not. I'll provide just few examples to demonstrate the existing inconsistencies.

Result of current() is undefined

$ php -r '$a = [1,2,3]; foreach($a as $v) { echo $v . " - " . current($a) . "\n";}'
1 - 2
2 - 2
3 - 2

$ php -r '$a = [1,2,3]; $b = $a; foreach($a as $v) { echo $v . " - " . current($a) . "\n";}'
1 - 1
2 - 1
3 - 1

unset() may exclude an element from iteration or not

$ php -r '$a = [1,2,3]; foreach($a as $v) { echo "$v\n"; unset($a[1]);}'
1
2
3

$ php -r '$a = [1,2,3]; $b = &$a; foreach($a as $v) { echo "$v\n"; unset($a[1]);}'
1
3

It's possible to write more inconsistent or strange examples...

Proposal

We propose consistent foreach statement behavior and provide efficient implementation.

The changes in beahvior affects only edge undefined cases and keeps the conceptual behavior unchanged.

At first, foreach statement may iterate:

  • by value - foreach ($a as $v)
  • by reference- foreach ($a as &$v)

At second, foreach statement may iterate over:

  • array - $x = [1,2,3]; foreach ($x as $v)
  • plain object - $x = new SomeClass(); foreach ($x as $v)
  • iterator object - $x = new SomeIteratorClass(); foreach ($x as $v)

Iteration over iterator objects (by value and by reference) is kept the same.

foreach() by value over array

foreach by value over array will never use or modify internal array pointer. It also won't duplicate array, it'll lock it instead (incrementing reference counter). This will lead to copy-on-write on attempt of array modification inside the loop. As result, we will always iterate over elements of array originally passed to foreach, ignoring any possible array changes. The following examples demonstrates the changes in behavior (in comparison to examples from introduction).

The value of internal pointer is unaffected

$ sapi/cli/php -r '$a = [1,2,3]; foreach($a as $v) { echo $v . " - " . current($a) . "\n";}'
1 - 1
2 - 1
3 - 1

Modifications of the original array are ignored

$ sapi/cli/php -r '$a = [1,2,3]; $b = &$a; foreach($a as $v) { echo "$v\n"; unset($a[1]);}'
1
2
3

foreach() by value over plain object

TODO

foreach() by reference over array

TODO

foreach() by reference over plain object

TODO

Implementation Details

TODO

Backward Incompatible Changes

Some rare cases where the foreach statement behavior was undefined may be changed. The implementation changes few such PHPT tests.

Proposed PHP Version(s)

PHP7

RFC Impact

To Performance

New behavior allows elimination of array duplication and this should lead to better performance, on the other hand some HashTable operations require additional check(s). Anyway, for Wordpress-3.6 the proposed patch reduces the number of executed CPU instructions by ~1%. It saves about 200 array duplications and destructions per each request to home page.

To Opcache

OPCache has to support new opcodes. All necessary OPCache changes are provided with the proposed implementation

Open Issues

  • complete RFC
  • complete implementation
  • performance optimization

Future Scope

This sections details areas where the feature might be improved in future, but that are not currently proposed in this RFC.

Proposed Voting Choices

The vote is a straight Yes/No vote, that requires a 2/3 majority

Patches and Tests

Pull request for master branch: https://github.com/php/php-src/pull/1034

It doesn't solve all the problems yet.

Implementation

After the project is implemented, this section should contain

  1. the version(s) it was merged to
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature

References

Links to external references, discussions or RFCs

rfc/php7_foreach.1422548207.txt.gz · Last modified: 2017/09/22 13:28 (external edit)