nikic/iter
nikic/iter is a small PHP library for working with iterables and generators. It provides lazy, functional-style helpers like map, filter, reduce, and chain to build efficient pipelines over arrays and Traversables without extra memory overhead.
## Getting Started
### **Minimal Setup**
1. **Installation**
Add the package via Composer (updated for v2.4.1):
```bash
composer require nikic/iter:^2.4
No additional configuration is required—it remains a drop-in utility.
First Use Case: Basic Iteration with Type Safety
Leverage the improved toArray() return type (list<T>) for better IDE support:
use function nikic\iter\map;
use function nikic\iter\filter;
$numbers = [1, 2, 3, 4, 5];
$doubledEvens: list<int> = filter(map($numbers, fn(int $n) => $n * 2), fn(int $n) => $n % 2 === 0);
foreach ($doubledEvens as $num) {
echo $num; // Outputs: 4, 8 (with type-safe `list<int>`)
}
Where to Look First
map, filter, take, drop, reduce, toArray() (now typed as list<T>).tests/ directory for edge cases (e.g., typed iterators).Use generators with strict typing for memory-efficient, type-safe pipelines:
use function nikic\iter\map;
use function nikic\iter\filter;
use function nikic\iter\toArray;
$users = [
['name' => 'Alice', 'age' => 30],
['name' => 'Bob', 'age' => 25],
];
// Type-safe transformation: `list<string>` output
$names: list<string> = toArray(
filter(
map($users, fn(array $user) => $user['name']),
fn(string $name) => strlen($name) > 3
)
); // ['Alice']
Chain functions for declarative, type-safe data transformation:
use function nikic\iter\chain;
$products = [
['price' => 10.99, 'taxable' => true],
['price' => 5.00, 'taxable' => false],
];
$taxablePrices: list<float> = chain($products)
->filter(fn(array $p) => $p['taxable'])
->map(fn(array $p) => $p['price'])
->toArray(); // [10.99]
Generate infinite sequences with type safety (e.g., list<int>):
use function nikic\iter\cycle;
use function nikic\iter\take;
$infinite: \Generator = cycle([1, 2, 3]);
$firstFive: list<int> = take($infinite, 5); // [1, 2, 3, 1, 2]
Eloquent Lazy Loading with Typed Results:
use Illuminate\Database\Eloquent\Builder;
use function nikic\iter\filter;
use function nikic\iter\toArray;
$users: list<\App\Models\User> = toArray(
filter(
User::query()->cursor(),
fn(\App\Models\User $user) => $user->is_active
)
);
Queue Jobs with Typed Batches:
use function nikic\iter\take;
$batch: list<int> = take(range(1, 1000), 100);
ProcessBatchJob::dispatch($batch);
Extend the library with typed generators:
function range(int $start, int $end): \Generator {
for ($i = $start; $i <= $end; $i++) {
yield $i;
}
}
$sum: int = reduce(range(1, 100), fn(int $carry, int $n) => $carry + $n, 0); // 5050
Stateful Generators (Unchanged) Avoid closures capturing external state. Prefer pure functions:
// ❌ Bad: Captures `$this`
$this->items = [1, 2, 3];
$gen = function () { yield $this->items[0]; };
// ✅ Good: Stateless
$gen = function () { yield 1; };
Memory Leaks with Infinite Sequences (Unchanged)
Always bound infinite sequences with take()/drop():
// ❌ Dangerous
$infinite = cycle([1, 2]);
foreach ($infinite as $n) { /* ... */ }
// ✅ Safe
$firstTen: list<int> = take(cycle([1, 2]), 10);
Type Safety Improvements (v2.4.1)
toArray() now returns list<T>: Explicitly type your results for better IDE support:
$result: list<string> = toArray(map([1, 2, 3], fn(int $n) => (string)$n));
fn() syntax for type hints:
// ✅ PHP 8.1+
$filtered: list<int> = filter([1, 2, 3], fn(int $n) => $n > 1);
// ❌ PHP 7.x (fallback)
$filtered = filter([1, 2, 3], function ($n) { return $n > 1; });
Inspect Typed Generators
Use iterator_to_array() with type assertions:
$gen = map([1, 2, 3], fn(int $n) => $n * 2);
$array: list<int> = iterator_to_array($gen); // [2, 4, 6]
Short-Circuiting with Typed Data
Combine takeWhile/dropWhile for early termination:
$validData: list<int> = dropWhile([null, 1, 2], fn(?int $item) => $item === null);
Custom Typed Functions Create domain-specific iterators with strict typing:
function pluck<T, K extends keyof T>(array $items, K $key): \Generator {
foreach ($items as $item) {
yield $item[$key];
}
}
$names: \Generator = pluck($users, 'name');
Performance Tuning (Unchanged)
Use chunk() for parallel processing:
$chunks: list<list<int>> = chunk(range(1, 1000), 100);
Laravel Service Providers (Enhanced) Bind typed iterators to the container:
$this->app->bind('iter.range', fn() => range(1, 100));
$this->app->bind('iter.pluck', fn() => pluck(...));
try-catch:
try {
$result: list<int> = toArray(
filter([1, 2, 'invalid'], fn(int $n) => $n > 1)
);
} catch (TypeError $e) {
Log::error('Type mismatch in iterator', ['error' => $e]);
}
toArray() Return Type: Now explicitly list<T> (e.g., list<int>, list<string>) for better static analysis.
Example:
$numbers: list<int> = toArray(map([1, 2, 3], fn(int $n) => $n * 2));
fn() syntax and strict typing for cleaner code.
NO_UPDATE_NEEDED would **not** apply here due to the meaningful type safety improvements in v2.4.1.
How can I help you explore Laravel packages today?