====== PHP RFC: Iterator chaining ======
* Version: 0.9
* Date: 2021-03-20
* Author: Max Semenik, maxsem.wiki@gmail.com
* Status: Draft
* First Published at: http://wiki.php.net/rfc/iterator_chaining
===== Introduction =====
SPL provides a set of iterators that allow feeding one into another to create powerful data pipelines, however doing that is not particularly convenient:
$iterator = new RegexIterator($myIterator, '/some pattern/');
$iterator = new CallbackFilterIterator($iterator, fn(...) {...});
$iterator = new LimitIterator($iterator, 0, 123);
$iterator = new InfiniteIterator($iterator);
Phew, that's a lot of typing! Not easy to comprehend, either. Alternatively, a functional approach could be used:
$iterator = new InfiniteIterator(new LimitIterator(new CallbackFilterIterator(new RegexIterator($myIterator, '/some pattern/'), fn(...) {...}), 0, 123));
Note how the order of writing is gorgeously opposite to the order of data flow.
Can we make it better?
===== Proposal =====
I like how this is implemented Rust. Translated to PHP, that would look like:
$iterator = $myIterator->regex('/some pattern/')
->callbackFilter(fn(...) {...})
->skip(123)
->infinite();
Wouldn't that be way more readable and easy to write? I'm proposing to implement it the following way:
**Create a trait** that extends iterators and allows to feed them into other iterators:
trait IteratorChain {
public function skip(int $count): LimitIterator {}
public function limit(int $count): LimitIterator {}
public function skipAndLimit(int $numToSkip, int $limit): LimitIterator {}
public function regex(string $regex, int $mode = RegexIterator::MATCH, int $flags = 0 , int $preg_flags = 0): RegexIterator {}
public function callbackFilter(callable $callback): CallbackIterator {}
public function noRewind(): NoRewindIterator {}
public function cached(int $flags = CachingIterator::CALL_TOSTRING): CachingIterator {}
public function multiple(int $flags = MultipleIterator::MIT_NEED_ALL|MultipleIterator::MIT_KEYS_NUMERIC, string|int|null $info = null): MultipleIterator {}
public function infinite(): InfiniteIterator {}
}
**Use this trait** in all SPL iterators, where appropriate. Full list:
* ''ArrayIterator''
* ''DirectoryIterator''
* ''EmptyIterator'' - for uniformness
* ''IteratorIterator'' and thus all its subclasses
* ''MultipleIterator''
* ''RecursiveArrayIterator''
===== Backward Incompatible Changes =====
None.
===== Proposed PHP Version(s) =====
8.1.
===== RFC Impact =====
Should not impact existing functionality (other than making some of it easier to use).
===== Open Issues =====
Need to decide on precise details of interface before going into voting.
===== Future Scope =====
This section details areas where the feature might be improved in future, but that are not currently proposed in this RFC.
===== Proposed Voting Choices =====
Accept this RFC (yes/no)? - 2/3 votes required
===== Patches and Tests =====
Links to any external patches and tests go here.
If there is no patch, make it clear who will create a patch, or whether a volunteer to help with implementation is needed.
Make it clear if the patch is intended to be the final patch, or is just a prototype.
For changes affecting the core language, you should also provide a patch for the language specification.
===== Implementation =====
After the project is implemented, this section should contain
- the version(s) it was merged into
- a link to the git commit(s)
- a link to the PHP manual entry for the feature
- a link to the language specification section (if any)
===== References =====
Links to external references, discussions or RFCs
===== Rejected Features =====
Keep this updated with features that were discussed on the mail lists.