Today I was refactoring some code in one of my libraries, and ended up replacing a named Iterator class with a Generator. To my surprise this changed behaviour, which I noticed due to a broken test. A test verifying that I could iterate multiple times through the iterator – good that I did write that! And so I found out that you can not actually rewind a Generator. By extension, you can also iterate through the iterator only once. If you try to rewind or iterate multiple times, you’ll get an Exception. Gah!
1 2 3 |
$generator = $myGeneratorFunction(); iterator_to_array($generator); iterator_to_array($generator); // boom! |
1 2 3 |
$generator = $myGeneratorFunction(); $generator->next(); $generator->rewind(); // boom! |
Luckily, this is trivial to get around. Just make an Iterator that recreates the Generator on rewind and delegates to it. I’ve created a (extremely) little library that holds such an adapter, so I can tackle this problem without cluttering my other libraries with such a random infrastructure concern. Which of course you can use as well if you want.
1 2 3 4 |
$generator = new RewindableGenerator( $myGeneratorFunction ); iterator_to_array($generator); iterator_to_array($generator); // works as expected $generator->rewind(); // works as expected |
The library is called Rewindable Generator. You can install it with Composer using the package name jeroen/rewindable-generator.
FYI the CachingIterator class existed long before generators did…
http://php.net/manual/en/class.cachingiterator.php