Hello internals,
(this is my first email to this list, hopefully I'm doing ok.)
(I sent this message before, but I think it was rejected due to html/multipart
and lack of [PHP-DEV] in subject)
Background / motivation:
Currently in PHP we have an interface "Iterator", and a final class
"Generator"
(and others) that implement it.
Using an iterator in foreach () is straightforward:
foreach ($iterator as $key => $value) {..}
However, if we want to iterate only a portion and then continue elsewhere
at the
position where we stopped, we need to do something like this:
for ($iterator->rewind(); $iterator->valid(); $iterator->next()) {
$value = $iterator->current();
[..]
}
This is unpleasantly verbose, and also adds performance overhead due to
additional function calls.
Also, manually writing an iterator is quite painful.
I sometimes implement "readers" that can be used like this (*):
// Gets a reader at position zero.
$reader = $readerProvider->getReader();
while (FALSE !== $value = $reader->read()) {
[..]
}
(*) Note that I am using FALSE
as an equivalent for "end of data". Of
course it
would be nice if we had a dedicated constant for this, that does not
constrain
the range of possible values of the iterator.
Such readers are much easier to write than iterators.
However, there is no native support for foreach(), and for generator syntax
with
yield.
Adapters from Iterator to Reader and vice versa are possible to write.
However, such userland adapters add additional performance overhead: One
call to
->read() will trigger one call to ->valid(), one to ->current(), one to
->next().
Proposal:
Establish a new interface in core, "Reader" or "ReaderInterface" (*).
This interface has only one method, "->read()".
The existing interface Iterator will remain unchanged.
(*) I am open to other naming suggestions. In fact in my own projects I
called
this thing "StreamInterface", and distinguish between
"ObjectStreamInterface",
"RowStreamInterface" etc. Currently I think "Reader" might be more suitable.
Let the final class "Generator", and possibly other native iterators,
implement
Reader in addition to Iterator.
Optionally, add an interface "ReaderAggregate", or
"ReaderAggregateInterface",
or "ReaderProvider". This interface has only one method, "getReader()".
Let ReaderAggregate extend Traversable, and add foreach() support.
The key will simply be a counter.
Open questions:
- The naming of "Reader", "ReaderAggregate", "->read()".
- Which return value to use for "end of data", so that
FALSE
would become a
valid value.
I currently don't see a better option than FALSE.
Maybe a sub-interface of ReaderAggregate can have an method "->getStopValue()",
which would allow to specify something other than FALSE.
- Andreas Hennings
(https://github.com/donquixote)