bentools/iterable-functions
Small PHP utility package offering array-like helpers for any iterable (arrays, Traversable, generators): iterable_to_array/to_traversable, map, merge, reduce, filter, values, and chunk—write iterable-agnostic code without TypeError surprises.
Installation:
composer require bentools/iterable-functions:^2.0
Add the namespace to your composer.json aliases (optional but recommended for cleaner imports):
"autoload": {
"psr-4": {
"App\\": "app/",
"BenTools\\": "vendor/bentools/iterable-functions/src"
}
}
Run composer dump-autoload.
First Use Case:
Convert an unknown iterable (array, generator, or Traversable) to an array safely in a Laravel service:
use function BenTools\IterableFunctions\iterable_to_array;
public function processData($data): array
{
// Works for arrays, generators, or ArrayIterator
return iterable_to_array($data);
}
Key Entry Points:
iterable($data)->filter()->map()->asArray().iterable_map(), iterable_filter(), etc., instead of array_* functions when working with mixed iterables.Traversable objects.use function BenTools\IterableFunctions\iterable_to_array;
public function store(Request $request)
{
$data = $request->input('items'); // Could be array or Traversable
$processed = iterable_to_array($data); // Guaranteed array
// Proceed with Eloquent or validation
}
use function BenTools\IterableFunctions\iterable_chunk;
public function importCsv(UploadedFile $file)
{
$handle = fopen($file->path(), 'r');
$generator = function () use ($handle) {
while (($row = fgetcsv($handle)) !== false) {
yield $row;
}
};
foreach (iterable_chunk($generator(), 1000) as $batch) {
Model::insert($batch); // Process in chunks
}
}
use function BenTools\IterableFunctions\iterable;
public function getFilteredUsers()
{
return iterable(User::query()->get())
->filter(fn(User $user) => $user->isActive())
->map(fn(User $user) => $user->toArray())
->asArray();
}
use function BenTools\IterableFunctions\iterable_merge;
public function mergeData()
{
$users = User::query()->get();
$posts = Post::query()->limit(10)->get();
return iterable_merge($users, $posts)
->map(fn($item) => $item->toArray())
->asArray();
}
Traversableuse function BenTools\IterableFunctions\iterable_to_traversable;
public function process(Traversable $items)
{
$traversable = iterable_to_traversable($items);
// Now $traversable is guaranteed to be Traversable (ArrayIterator if input was array)
}
Laravel Service Providers:
Add helper functions to the App facade for global access:
// app/Providers/AppServiceProvider.php
public function boot()
{
if (! function_exists('iterable_to_array')) {
require __DIR__.'/../../vendor/bentools/iterable-functions/src/functions.php';
}
}
Testing: Use the fluent interface to mock iterables in PHPUnit:
$mockIterable = iterable([1, 2, 3])
->filter(fn($value) => $value > 1)
->map('strval');
Performance:
Prefer generators (fn() => yield) over arrays for large datasets to avoid memory spikes. Example:
$generator = fn() => yield from Model::query()->cursor();
foreach (iterable_chunk($generator(), 50) as $batch) {
// Process batch
}
API Responses: Normalize responses to arrays for consistency:
return Response::json(iterable_to_array($data));
iterable_to_array() vs iterator_to_array():
iterator_to_array() throws a TypeError if passed an array. Always use iterable_to_array() for mixed iterables.iterator_to_array($var) with iterable_to_array($var).Fluent Interface Memory Leaks:
iterable($data)->filter()->map() creates intermediate Traversable objects, which can consume memory if not consumed (e.g., with foreach or asArray()).asArray() or iterate over the result to trigger evaluation:
$result = iterable($data)->filter()->map()->asArray(); // Forces evaluation
Generator Exhaustion:
$generator = fn() => yield 1;
$array = iterable_to_array($generator()); // Store as array for reuse
Key Preservation:
iterable_values() reindexes keys like array_values(). If you need to preserve keys, avoid this function.iterable_to_array($iterable, false) to skip key reindexing.PHP 8.1+ Features:
iterable_chunk()) may behave differently with PHP 8.1’s new features (e.g., first-class callable syntax).// PHP 8.1: Use arrow functions
iterable($data)->filter(fn($item) => $item > 0);
Check Iterable Type:
Use var_dump() or get_debug_type() to inspect iterables:
$data = ['a', 'b'];
var_dump(get_debug_type($data)); // string(4) "array"
Lazy Evaluation Debugging:
Add foreach loops to force evaluation and debug intermediate steps:
$result = iterable($data)
->filter(fn($item) => $item > 0)
->map('strval');
foreach ($result as $item) { // Force evaluation
var_dump($item);
}
Type Errors:
If you get TypeError: iterable_to_array(): Argument must be of type iterable, ensure your input is actually iterable:
if (! is_iterable($data)) {
throw new InvalidArgumentException('Input must be iterable');
}
Custom Filter/Map Functions: Extend the fluent interface by creating wrapper classes:
class CustomIterable extends \BenTools\IterableFunctions\Iterable
{
public function customMethod()
{
return $this->map(fn($item) => strtoupper($item));
}
}
Integration with Laravel Collections:
Add a macro to Laravel’s Collection class:
// app/Providers/AppServiceProvider.php
public function boot()
{
\Illuminate\Support\Collection::macro('asIterable', function () {
return iterable($this->all());
});
}
Custom Iterators:
Use iterable_to_traversable() to wrap custom iterators:
$customIterator = new class implements \Iterator {
// ... iterator implementation
};
$traversable = iterable_to_traversable($customIterator);
Performance Profiling: Compare native functions vs. iterable functions for critical paths:
How can I help you explore Laravel packages today?