0.1.3 2014-01-27 21:56 UTC


This a small collection of Iterators that covers cases not provided by php's defaults.


By using Iterators we can defer the execution of heavy operation until needed; which means faster execution. If you even find yourself working with code like this:

function getItems() {
    $result = array();
    foreach(callDatabase() as $item) {
        $result[] = doSomething($item); // eager example -- DONT DO THIS
    return $result;

This library provides Iterators that allows to handle such scenario as this:

function getItems() {
    return new Map(callDatabase(), function ($item) {
        return doSomething($item); // lazy creation as needed



    "require": {
        "prelude/prelude-iterators": "*"

See packagist for branch information.


Generator(Closure $yield)

This iterator will call the $yield function when new elements are needed. It will continue generating new elements until some falsy value is returned.

$counter = new Generator(function() {
    static $n = 0;
    return ++$n;

foreach ($counter as $number) {
    echo $number; // prints 1, 2, 3, 4, ... forever

Unlike PHP Generators (php >= 5.5) this Iterator allows to rewind itself and start over.

Records(PDOStatement $stmt[, $fetchStyle])

This iterator will take care of efficiently fetching records, as they are needed, from a PDOStatement. Once all records are fetched, the iterator will close the PDOStatement; so there's no need for to do so.

$records = new Records(
    $pdo->query("SELECT * FROM blablah...")
foreach ($records as $i => $record) {
    // do something with $record

PDOStatement provides differents ways to fetch objects. Records iterators were designed to stay out of your way!

// set the statement to fetch records as User's instances
$stmt->setFetchMode(PDO::FETCH_CLASS, Foo\Bar\User::CLASS);
$records = new Records($stmt);

// .. shortcut style
$records = new Records($stmt, PDO::FETCH_CLASS, Foo\Bar\User::CLASS);

foreach ($records as $user) {
    $user instanceof Foo\Bar\User;

Records iterators use Generators under the hood, so they are really cheap to create; plus you can rewind them multiple times.

Memoize(Iterator $iterator)

Some iterators handle expensive operations. The Memoize will make sure the results are generated only once.

    $iterator = new Memoize(new ExpensiveIterator());

    foreach ($iterator as $item) {
        // call ExpensiveIterator, and memoize each element

    foreach ($iterator as $item) {
        // iterator over the memoized elements

Where(Traversable $elements, Closure $predicate)

Filter the elements by calling the $predicate function.

$admins = new Where($users, function ($user) {
    return $user->isAdmin();

If you're using php >= 5.4 use php's SPL CallabackFilterIterator instead.

Map(Traversable $elements, Closure $transform)

Transform the elements by calling the $transform function on each element.

$names = new Map($users, function ($user) {
    return $user->getName();

Cycle(Traversable $elements)

This iterator will cycle indefinitely. Once the last element is reach, it will start over

$cycle = new Cycle(new \ArrayIterator(array(1)));
forEach ($cycle as $i) {
    echo $i; // always prints 1 --> infinite loop

Query(Iterator $iterator)

The Query provides a chainable interface for creating Iterators. All the operations can be made using regular Iterators, but this class allows working with them in a very natural way:

$names = Query::from($db->getUsers())
              ->where(function($user) {
                  return $user->isAdmin(); // pick only admins
              ->select(function($user) {
                  return $user->getName(); // return their names
              ->limit(5); // return the first 5

foreach ($names as $name) {
    echo $name;

The Query::from method was crafted to accept both array and Traversable instances.


Please give it a try, and let me know!