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

Fixed Array Functions Laravel Package

petrobolos/fixed-array-functions

Fixed Array Functions offers reliable, predictable replacements for common PHP array helpers, helping Laravel and PHP developers avoid edge-case bugs and inconsistent built-in behavior when searching, filtering, mapping, and manipulating arrays with safer key and value handling.

View on GitHub
Deep Wiki
Context7

Getting Started

petrobolos/fixed-array-functions gives you a Laravel Collection-style fluent API on top of SplFixedArray — useful when you have dense, known-size data (import rows, numeric series, bulk payloads) and want to avoid the memory overhead of a plain PHP array.

Install

composer require petrobolos/fixed-array-functions

No service provider or config publish step — the package exposes a global fixedArray() helper that is autoloaded. v3.1.0 supports Laravel 13 (and earlier), and upgrading from 3.0.x is a plain composer update with no breaking changes.

Where to look first

  1. The README method reference — it now lists 70+ methods with the "Collection parity" mapping, which is your fastest way to answer "does this exist yet?"
  2. CONTRIBUTING.md if you plan to fork, patch, or send a PR — it documents dev setup and coding standards.
  3. SECURITY.md for the vulnerability disclosure channel if you're shipping this in a client-facing app.

First use case

Start with the highest-value, lowest-risk conversion: read-only aggregation over a large dataset you already hold in memory.

use function fixedArray;

$orders = fixedArray($orderRows);

$total   = $orders->sum('amount');
$average = $orders->avg('price');
$largest = $orders->max('amount');

If that path works for your data shape, expand into transformation chains (map → filter → reduce) before reaching for the conditional helpers (when / unless), which are slightly easier to misuse.

Implementation Patterns

1. Terminal aggregation in reporting code

Aggregate methods accept a property name, so reporting code stays declarative instead of looping:

$stats = [
    'count'   => $rows->count(),
    'total'   => $rows->sum('amount'),
    'avg'     => $rows->avg('amount'),
    'min'     => $rows->min('amount'),
    'max'     => $rows->max('amount'),
    'empty'   => $rows->isEmpty(),
];

2. Validation / triage with partition()

This is the cleanest fit for import and batch-processing pipelines — split good rows from bad in one pass, then hand each side to a different job:

[$valid, $invalid] = fixedArray($rows)
    ->partition(fn ($row) => $this->validator->passes($row))
    ->toArray();

ImportJob::dispatch($valid);
RejectReportJob::dispatch($invalid);

3. Optional transformations with when() / unless()

Use these when a flag comes from config, a request, or a queued job's options — it keeps a single chain instead of branching into two:

$rows = fixedArray($rows)
    ->when($request->boolean('double'), fn ($a) => $a->map(fn ($v) => $v * 2))
    ->unless($request->boolean('keep_order'), fn ($a) => $a->sort());

Note the callback receives the array and must return it — treat the whole thing as immutable and always assign the result.

4. Extract-and-join for exports and emails

pluck('name')->join(', ') replaces a array_column() + implode() pair and reads better inside a mailer or CSV builder:

$recipients = fixedArray($users)->pluck('email')->join('; ');

5. Observability inside a chain with tap()

tap() is the right tool for "log the intermediate state without breaking the pipeline" — much better than sprinkling Log::info between statements:

$result = fixedArray($payload)
    ->tap(fn ($a) => logger()->info('import.batch', ['count' => $a->count()]))
    ->filter(fn ($row) => $row['active'])
    ->map(fn ($row) => $this->normalise($row))
    ->pipe(fn ($a) => $a->sum('amount'));

6. pipe() as the escape hatch to Laravel land

pipe() returns whatever the callback returns — a scalar, a Collection, an Eloquent model. Use it as the seam between the fixed-array world and the rest of the app:

return fixedArray($rows)->pipe(fn ($a) => collect($a->toArray()));

7. Bridging to and from Eloquent

Keep the boundary explicit. Build fixed arrays from query results after ->get(), and convert back with ->toArray() before passing to response()->json(), insert(), or anything expecting a plain array:

$rows = fixedArray(Order::query()->where('status', 'open')->get()->toArray());

Don't try to wrap a query builder or a lazy collection directly — SplFixedArray needs a concrete size up front.

8. Choosing the right tool

Reach for fixedArray() when the dataset is large, dense, numerically indexed, and read-mostly. Stay on collect() when you need associative keys, sorting stability with string keys, lazy evaluation, or heavy mutation (push/pop/splice-heavy code). Converting back and forth repeatedly is the main anti-pattern.

Gotchas and Tips

Keys are positional indices, not your original keys — this is the big one. SplFixedArray is an integer-indexed structure. The new filter(fn ($value, $key) => ...) signature does not give you Laravel's associative keys; it gives you 0..n-1. If you rely on filter to inspect a database id or string key, use the value instead ($value['id']). The same applies to keys(), which returns the positional index list, not your payload's keys.

The filter() signature change is technically backward compatible but not behaviorally silent. Existing closures declaring one parameter are unaffected. But if you already had a callback that accepted a second argument (e.g. fn ($v, $k = null) or a two-arg callable), it will now receive the index where previously it received nothing. A pre-upgrade grep for ->filter(fn ($ and ->filter(function ($ with two-arg signatures is a five-minute insurance policy.

Immutability is not obvious. Methods like map, filter, when, and unless return new instances. Calling $data->sort(); on its own line is a no-op. Always assign or chain.

isEmpty() on a resized array. SplFixedArray has a declared size that can be larger than the number of meaningful entries. Verify how your version treats trailing null slots before using isEmpty() as a validation gate — a "resized to 10, populated with 3" array may report as non-empty.

Empty-set aggregates. Confirm the return contract of min(), max(), and avg() on an empty array (typically null, but check) before feeding them into arithmetic — null + 0 and division are easy ways to ship a subtle bug.

Call toArray() at every boundary. JSON responses, Eloquent insert()/upsert(), mailer payloads, and most third-party SDKs want a plain array. SplFixedArray is not JsonSerializable in the way you'd expect and will not serialize the way collect() does.

join() type safety changed. v3.1.0 tightened handling of null and non-scalar elements. If you were joining a column that can contain null or nested arrays, diff the output on a representative fixture after upgrading — it's the one place where "no breaking changes" and "improved type safety" can still bite.

Debugging: there is no dump()/dd() parity. Use ->tap(fn ($a) => dump($a->toArray())) and inspect the plain array. tap() returns the original instance, so it slots anywhere in a chain without changing types.

Performance claims deserve a benchmark on your data. The package optimises filter() and aggregates to avoid intermediate allocations, but object construction and method dispatch are not free. Fixed arrays win on large homogeneous numeric datasets; for a few hundred rows, plain arrays or collect() are frequently faster end to end. Benchmark the realistic size before rewriting existing code.

Static analysis is a selling point here — use it. PHPStan Level 9 with zero errors means the package ships useful generics/annotations. If you're on PHPStan or Psalm, run at your highest level after the bump; you'll catch the key-vs-value confusion above at analysis time rather than in production.

Extension points are limited. There's no macro system analogous to Collection::macro(). For custom domain helpers, prefer composition (a small service class that owns the fixedArray() lifecycle) over subclassing, so future upstream changes don't fight your overrides. If you need a missing method, the CONTRIBUTING.md and PR template in this release make upstreaming the cheap path — 290 tests and full coverage means your addition will be held to a clear standard.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource