Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Iterator Laravel Package

hoa/iterator

Hoa\Iterator is a PHP library offering a comprehensive collection of iterator classes and helpers, including filters, maps, limits, aggregates, and recursive traversals, for composing elegant, reusable iteration logic over arrays and other traversable data.

View on GitHub
Deep Wiki
Context7

Getting Started

This is not a Laravel package — it is a standalone SPL iterator toolkit (lazy Map, Filter, Limit, Lookahead, Mock, Infinite, plus IteratorIterator/IteratorAggregate bridges). Before writing any code, decide whether Laravel's built-in LazyCollection already covers the job. It usually does; you reach for hoa/iterator only for the few primitives SPL lacks (notably a first-class lookahead/peek iterator and a deterministic Mock sequence).

Install (with eyes open)

composer require hoa/iterator
composer show hoa/iterator          # expect 3.x, last release 2017-01-10
composer why hoa/consistency        # pulls in Hoa's runtime helper package

The package is archived and unmaintained. Pin the exact version in composer.json, commit the lock file, and treat any transitive hoa/* dependency as a long-term liability. If you cannot accept vendor code that will never receive a PHP-version fix, stop here and use LazyCollection.

Where to look first

The repo is flat and tiny — one class per file at the root (Map.php, Filter.php, Limit.php, Lookahead.php, Mock.php, Infinite.php). Read those six before anything else; each is ~50–120 lines and just decorates an inner Iterator. Confirm constructor argument order in the source of the version you installed, because it changed between major versions.

First use case: streaming a large export lazily

The realistic day-to-day win is transforming rows from a file or DB::cursor() without materialising the whole set.

use Hoa\Iterator\Map;
use Hoa\Iterator\Limit;

$rows = (function () use ($path) {
    $h = fopen($path, 'rb');
    try {
        while (($row = fgetcsv($h)) !== false) {
            yield $row;
        }
    } finally {
        fclose($h);
    }
})();

$emails = new Map($rows, fn (array $row) => strtolower(trim($row[0] ?? '')));
$page   = new Limit($emails, 0, 500);

foreach ($page as $i => $email) {
    // one row in memory at a time
}

Implementation Patterns

1. Keep Hoa types out of your public API. Expose Iterator/IteratorAggregate from your own classes and keep the Hoa\Iterator\* construction private. That gives you a one-file migration to LazyCollection or plain generators when the dependency is dropped.

final class CsvRowStream implements IteratorAggregate
{
    public function __construct(private string $path) {}

    public function getIterator(): Iterator
    {
        return new Map($this->rawRows(), fn (array $r) => array_combine($this->header, $r));
    }
}

2. Bridge Laravel collections both ways. LazyCollection already satisfies IteratorAggregate, so $lazy->getIterator() returns a Generator you can feed into a Hoa decorator. Going back: LazyCollection::make($hoaIterator) or, for finite results, collect(iterator_to_array($it, false)) — always pass false to avoid key collisions.

3. Compose as a decorator stack, outermost-limit first. new Limit(new Map(new Filter($source, $f), $g), 0, 100) means only 100 items ever traverse the filter; reversing the order filters everything first. Order is a performance decision, not a style one.

4. Use lookahead for parsing and tailing. Lookahead lets you peek at the next element without consuming it — the clean fit for tokenizers, log tailers, and "does the next line continue this record" CSV repair, which is awkward with plain generators.

5. Use Mock as a test double. For collaborator code that type-hints Iterator, Mock gives a deterministic, rewindable sequence without hand-rolling an anonymous class. Do not leak it into production paths.

6. Prefer DB::cursor() over wrapping a query builder yourself. Eloquent's cursor() is already a lazy LazyCollection; wrapping it in Map/Filter is the idiomatic integration point, and it sidesteps the package's own database-free design entirely.

7. No service provider required. These are plain value-less classes. If you want container injection, bind your own adapter interfaces (RowStream), never Hoa\Iterator\Map directly — resolving an iterator from the container is almost always a design smell.

Gotchas and Tips

  • PHP 8.1+ deprecations. Hoa classes implement Iterator/OuterIterator without typed return declarations, so you will see Deprecated: Return type of ... should either be compatible with ... or #[\ReturnTypeWillChange] flood the logs. You cannot patch vendor code safely. Mitigations: suppress that deprecation in config/logging.php or ErrorException handling, wrap in classes you own and annotate those with #[\ReturnTypeWillChange], or avoid the package on 8.1+.
  • Autoloader friction. Hoa historically ships its own Hoa\Consistency autoloader alongside Composer. In a Laravel app with optimized autoloading (composer dump-autoload -o), verify classes still resolve and check for duplicate/conflicting registration in bootstrap/app.php era boots; do not let it register global state at boot.
  • Single-pass semantics. Iterators backed by generators cannot be rewound — calling rewind() after a partial pass throws Exception: Cannot rewind a generator that was already run. Never hand the same instance to two consumers; hand out a factory or a fresh getIterator().
  • Infinite iterators will OOM. Infinite, Repeater, and any unbounded source are fatal with iterator_to_array(), iterator_count(), or collect(). Always pair with Limit and an explicit break; unit-test the bound.
  • Resources leak on exception. If a callback throws mid-iteration, the inner generator's finally only runs if it is unwound. Write source generators with try/finally around fclose/unset, as in the example above.
  • Key preservation surprises. Check key() explicitly if downstream code cares about the original keys — decorators may re-index. iterator_to_array($it) silently overwrites duplicate keys; that is a data-loss bug that looks like "some rows vanished."
  • Do not serialize iterators. They cannot be queued in a job payload or cached. Materialise to an array at the boundary if you need to hand data to a queue.
  • Debugging. iterator_count($it) and iterator_to_array($it) consume the iterator — debugging with them changes behaviour. Use foreach with dump([$it->key(), $it->current()]) on a throwaway instance, and step with Xdebug through valid()/next().
  • SPL is the maintained alternative. CallbackFilterIterator, LimitIterator, MultipleIterator, and RecursiveIteratorIterator cover most of Filter/Limit/Aggregate with native support and correct return types. hoa/iterator's genuine differentiators are Lookahead, Mock, and the Buffer-style classes — justify the dependency on one of those specifically.
  • Extension points. Subclass Map/Filter or extend FilterIterator if you need custom behaviour; otherwise implement OuterIterator yourself in a small class rather than inheriting from an archived base.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin