php-standard-library/iter
Inspect and reduce any PHP iterable (arrays, generators, iterators) with small, focused helpers from PHP Standard Library - Iter. Designed for common iteration tasks and consistent behavior across iterable types.
Installation:
composer require php-standard-library/iter
Add to composer.json under require:
"php-standard-library/iter": "^6.2"
First Use Case:
Replace a simple foreach loop with iter():
use Iter\Iter;
$numbers = [1, 2, 3, 4, 5];
$doubled = Iter::from($numbers)
->map(fn($n) => $n * 2)
->toArray();
// Result: [2, 4, 6, 8, 10]
Where to Look First:
Iter\Iter class methods (e.g., map, filter, reduce, chunk).Lazy Evaluation Pipelines: Process iterables without materializing intermediate results:
$result = Iter::from($largeDataset)
->filter(fn($item) => $item['active'])
->map(fn($item) => $item['id'])
->chunk(100)
->toArray();
Generator Integration: Work seamlessly with PHP generators:
function fetchUsers(): Generator {
yield ['id' => 1, 'name' => 'Alice'];
yield ['id' => 2, 'name' => 'Bob'];
}
$activeUsers = Iter::from(fetchUsers())
->filter(fn($user) => $user['active'])
->toArray();
Composable Transformations:
Chain operations like map/filter/reduce:
$total = Iter::from($orders)
->filter(fn($order) => $order['status'] === 'completed')
->map(fn($order) => $order['amount'])
->reduce(fn($carry, $amount) => $carry + $amount, 0);
Laravel-Specific Workflows:
$users = User::query()->cursor();
$names = Iter::from($users)->pluck('name')->toArray();
$resources = Iter::from($posts)
->map(fn($post) => new PostResource($post))
->toArray();
Batch Processing:
Use chunk for memory-efficient batching:
Iter::from($largeArray)
->chunk(500)
->each(fn($chunk) => processBatch($chunk));
Data Cleaning:
$cleaned = Iter::from($rawData)
->filter(fn($item) => !empty($item['value']))
->map(fn($item) => sanitize($item['value']))
->toArray();
Nested Iterables: Flatten or transform nested structures:
$flat = Iter::from($nestedArray)
->flatten()
->map(fn($item) => $item * 2);
Conditional Aggregation:
$stats = Iter::from($logs)
->groupBy(fn($log) => $log['level'])
->map(fn($group) => count($group))
->toArray();
Type Safety: Use PHP 8.1+ return types for clarity:
/** @return array<int, int> */
public function getDoubled(array $numbers): array {
return Iter::from($numbers)
->map(fn($n) => $n * 2)
->toArray();
}
Error Handling:
Wrap pipelines in try-catch for robustness:
try {
$result = Iter::from($generator)
->map(fn($item) => riskyOperation($item))
->toArray();
} catch (Throwable $e) {
Log::error('Pipeline failed', ['error' => $e->getMessage()]);
}
Performance:
toArray() only when needed (materializes the iterable).each() for side effects without intermediate storage:
Iter::from($items)->each(fn($item) => $item->save());
Testing: Mock generators in unit tests:
$mockGenerator = $this->createMock(Generator::class);
$mockGenerator->method('getIterator')->willReturn(new ArrayIterator([1, 2, 3]));
$result = Iter::from($mockGenerator)->sum();
$this->assertEquals(6, $result);
Lazy Evaluation Surprises:
toArray() or sum() trigger execution.tap() to inspect intermediate states:
Iter::from($data)
->tap(fn($i) => Log::debug('After filter:', $i->toArray()))
->filter(...);
Generator Exhaustion:
$generator = fetchData();
$firstPass = Iter::from($generator)->filter(...)->toArray();
$secondPass = Iter::from(fetchData())->map(...); // Re-fetch
Type Inconsistencies:
ArrayObject + Generator) may require explicit casting:
$iterable = new ArrayObject([1, 2, 3]);
$result = Iter::from(iterator_to_array($iterable))->sum();
Memory Leaks:
// Bad: Holds reference to unevaluated generator
class Processor {
private $iterable;
public function __construct(Generator $generator) {
$this->iterable = Iter::from($generator); // Unevaluated!
}
}
PHP 8.1+ Dependencies:
iterable return types and named arguments. Ensure your project targets PHP 8.1+.Inspect Iterables:
Use toArray() or tap() to debug:
Iter::from($data)
->tap(fn($i) => dd($i->toArray()))
->filter(...);
Stack Traces: Lazy pipelines may obscure errors. Add context:
try {
$result = Iter::from($data)
->map(fn($item) => $item->invalidMethod())
->toArray();
} catch (Throwable $e) {
throw new RuntimeException(
"Pipeline failed at step 'map': " . $e->getMessage(),
0,
$e
);
}
Performance Profiling: Compare lazy vs. eager operations:
// Lazy (memory-efficient)
$lazySum = Iter::from($largeArray)->sum();
// Eager (materializes)
$eagerSum = array_sum($largeArray); // May crash with OOM
Global Helpers: The package doesn’t auto-register helpers. Add manually if desired:
// In AppServiceProvider
if (!function_exists('iter')) {
function iter(iterable $iterable): Iter\Iter {
return Iter\Iter::from($iterable);
}
}
Custom Iterators:
Extend Iter\Iter for domain-specific logic:
class UserIter extends Iter\Iter {
public function active(): self {
return $this->filter(fn($user) => $user->active);
}
}
Configuration:
No runtime config. All behavior is method-based (e.g., Iter::from($data)->...).
Custom Methods: Add domain-specific operations:
Iter::from($users)
->map(fn($user) => $user->fullName())
->filter(fn($name) => str_contains($name, 'Admin'))
->toArray();
Integration with Laravel:
public function register() {
$this->app->singleton('iter', fn() => new Iter\Iter());
}
// app/Facades/Iter.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Iter extends Facade {
protected static function getFacadeAccessor()
How can I help you explore Laravel packages today?