====== 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.