A fluent, lazy enumeration library for PHP inspired by LINQ and Java Streams. Provides a unified API for working with arrays, iterators, and generators through composable transformations and terminal operations.
composer require dan-lugg/enumerableRequires PHP 8.1+.
use function DanLugg\Enumerable\enumerate;
$result = enumerate([1, 2, 3, 4, 5, 6])
->then(filterBy(fn(int $v): bool => $v % 2 === 0))
->then(mapBy(fn(int $v): int => $v * 10))
->into(arrayOf());
// [1 => 20, 3 => 40, 5 => 60]Every enumerable pipeline has three parts:
- Entry point β
enumerate()orgenerated()creates an enumerable from an array, iterator, generator, or callable. - Transformations (
->then(...)) β apply lazy operations like mapping, filtering, grouping, and windowing. - Terminal (
->into(...)) β consumes the pipeline and produces a value (array, string, bool, or custom).
Operations are lazy β no iteration happens until ->into() is called. The $catching parameter on every function
provides error handling for individual elements without stopping the entire iteration.
| Function | Description |
|---|---|
enumerate(iterable|callable, catching?) |
Creates an enumerable from an iterable or callable |
generated(callable, catching?) |
Creates an enumerable from a generator callable |
| Function | Description |
|---|---|
forEachWith(callable) |
Side-effect callable for each element; yields originals unchanged |
mapBy(callable) |
Transforms each element (preserves keys) |
mapKeysBy(callable) |
Transforms each key (preserves values) |
filterBy(predicate?) |
Yields only elements matching the predicate |
takeWhile(predicate?) |
Yields elements while the predicate holds, then stops |
dropWhile(predicate?) |
Skips elements while the predicate holds, then yields the rest |
flatMapBy(selector?) |
Recursively flattens nested iterables with optional transform |
groupBy(callable) |
Groups elements by a selected key |
windowBy(int) |
Partitions into equal-sized window arrays; size must be positive |
| Function | Returns | Description |
|---|---|---|
allOf(predicate?) |
bool |
True if all elements satisfy the predicate |
anyOf(predicate?) |
bool |
True if any element satisfies the predicate |
minOf(selector?) |
mixed |
Minimum selected value |
minKeyOf(selector?) |
int|string|null |
Key of the minimum selected value |
maxOf(selector?) |
mixed |
Maximum selected value |
maxKeyOf(selector?) |
int|string|null |
Key of the maximum selected value |
firstOf(predicate?) |
mixed |
First element matching the predicate |
firstKeyOf(predicate?) |
int|string|null |
First key matching the predicate |
lastOf(predicate?) |
mixed |
Last element matching the predicate |
lastKeyOf(predicate?) |
int|string|null |
Last key matching the predicate |
stringOf(delimiter?) |
string |
Elements joined by delimiter |
arrayOf(depth?) |
array |
Materializes to an array (with optional recursive depth) |
done() |
void |
Consumes the enumerable (use for side effects only) |
Every function accepts a $catching callable that handles exceptions thrown during iteration:
use function DanLugg\Enumerable\enumerate;
$result = enumerate([1, 2, 3, 4, 5], fn(Throwable $e) => null)
->then(mapBy(fn(int $v): int => match ($v) {
3 => throw new \Exception("skip"),
default => $v * 10,
}))
->into(arrayOf());
// [0 => 10, 1 => 20, 3 => 40, 4 => 50] β element 2 skipped, error caughtWithout a $catching handler, exceptions propagate normally and stop iteration.
// Chaining multiple transformations
$result = enumerate(['foo', 'bar', 'baz', 'qux'])
->then(filterBy(fn(string $v): bool => strlen($v) === 3))
->then(mapKeysBy(fn(string $v, int $k): string => strtoupper($v)))
->into(arrayOf());
// ['FOO' => 'foo', 'BAR' => 'bar', 'BAZ' => 'baz', 'QUX' => 'qux']
// Grouping
$result = enumerate(range(1, 10))
->then(groupBy(fn(int $v): string => $v % 2 === 0 ? 'even' : 'odd'))
->into(arrayOf());
// ['odd' => [0 => 1, 2 => 3, ...], 'even' => [1 => 2, 3 => 4, ...]]
// Windowing
$result = enumerate([1, 2, 3, 4, 5])
->then(windowBy(2))
->into(arrayOf());
// [[0 => 1, 1 => 2], [2 => 3, 3 => 4], [4 => 5]]composer install # Install dependencies
composer test # Run tests
composer analyse # Static analysis (PHPStan)
composer cs # Check code style
composer cs-fix # Fix code style automatically
composer docs # Generate API documentationThe equivalent Make targets are make test, make analyse, make lint, make format, and make docs. After cloning, run make init to install dependencies and configure git hooks. Run make help to list all targets.
dan-lugg/enumerable is open-sourced software licensed under the MIT license.