Iterator wrapper around a generator factory that can be restarted on demand
Find a file
2026-01-11 13:32:28 -06:00
lib Initial commit 2026-01-11 13:32:28 -06:00
tests Initial commit 2026-01-11 13:32:28 -06:00
.gitignore Initial commit 2026-01-11 13:32:28 -06:00
AUTHORS Initial commit 2026-01-11 13:32:28 -06:00
composer.json Initial commit 2026-01-11 13:32:28 -06:00
composer.lock Initial commit 2026-01-11 13:32:28 -06:00
LICENSE Initial commit 2026-01-11 13:32:28 -06:00
README.md Initial commit 2026-01-11 13:32:28 -06:00
test Initial commit 2026-01-11 13:32:28 -06:00

Self-Restarting Generator

Self-Restarting Generator is a small PHP library for creating restartable generators. It wraps a generator factory so that calling rewind() replaces the current generator instance with a fresh one, allowing generators to be reused in multiple iterations, foreach loops, and other iterator contexts.

This is useful when you want to iterate over the same generator logic more than once without having to duplicate generator code or manually reset state such as when you're wanting to feed a data source to a template engine of some kind.

Requirements

  • PHP >= 8.1

Installation

Install via Composer:

composer require mensbeam/self-restarting-generator

Overview

PHP generators normally cannot be rewound once they have started:

$g = (function () {
    yield 1;
    yield 2;
})();

foreach ($g as $v) {}    // works
foreach ($g as $v) {}    // Fatal error: Cannot rewind a generator

SelfRestartingGenerator solves this by storing a generator factory (Closure(): callable(): Generator) and always creating a new generator instance when rewind() is called.

Features

SelfRestartingGenerator implements Iterator, so it behaves like any other iterator in PHP:

  • You can iterate with foreach.
  • rewind() restarts the generator from the beginning.
  • All generator methods are supported:
    • send(mixed $value)
    • throw(Throwable $exception)
    • getReturn(): mixed

Basic Usage

use MensBeam\SelfRestartingGenerator;

$gen = new SelfRestartingGenerator(function (): \Closure {
    return function (): \Generator {
        $db = new \SQLite3('test.db');
        $result = $db->query('select * from table_name limit 5');

        while ($row = $result->fetchArray(\SQLITE3_ASSOC)) {
            yield $row;
        }
        $db->close();
    };
});

// First pass
foreach ($gen as $value) {
    var_export($value);
    echo "\n";
}

// Second pass — works because rewind replaces the generator instance
foreach ($gen as $value) {
    var_export($value);
    echo "\n";
}

License

MIT License. See the LICENSE file for details.