serendipity_hq/component-array-writer
Serendipity HQ's component-array-writer is a lightweight PHP utility for exporting arrays into clean, readable PHP files. It simplifies generating configuration, data dumps, and component registries in Laravel or any PHP project, with simple, dependable, human-friendly output.
This is a very small, single-purpose package: one class that reads and writes values inside nested PHP arrays using path strings. It is not Laravel-aware (no service provider, no facade, no config file), so treat it like a utility library rather than a framework integration.
Minimal setup:
composer require serendipity_hq/component-array-writer
Before writing any code, open vendor/serendipity_hq/component-array-writer/src/ and read the single class file top to bottom — it is short, and the exact method names/signatures are the only documentation you can rely on (1 star, 0 dependents means no ecosystem examples to copy from). Confirm:
null, throw, or return a default when the path is missing?., but check.First use case: hydrating a nested request payload in a controller without a chain of isset() guards.
$writer = new ArrayWriter($payload); // verify ctor signature first
$writer->write('user.profile.name', $name); // adjust to actual method names
$writer->write('user.profile.locale', 'en_GB');
$payload = $writer->toArray(); // or however the result is exposed
If the class reads as "a thin wrapper around explode('.', $path) and a loop," you have your answer: use it where a path string arrives from outside (form payloads, config overrides, query params), not where you already have the key locally.
Bind it behind an interface. Because the package has no releases you can lean on and no dependents exercising it, wrap it:
interface NestedArrayReader
{
public function get(array $data, string $path, mixed $default = null): mixed;
public function set(array &$data, string $path, mixed $value): void;
}
Implement once with the package, once with Illuminate\Support\Arr, and bind the latter as the default. You get the package's ergonomics where useful and a one-line escape hatch if it goes stale.
Config-override merging. The cleanest real use case is applying a flat list of dotted overrides onto a nested settings array pulled from config/ or a JSON column:
foreach ($overrides as $path => $value) {
$writer->write($path, $value); // 'mail.host' => 'smtp.example.com'
}
This beats hand-rolled recursion and keeps the override format readable in the DB or a .env-adjacent file.
Hydrating form payloads. When a Livewire/Filament/Inertia form submits ['addresses' => [0 => ['line1' => ...]]] and you need to project specific paths into a DTO, a path-string reader lets you keep the field map in one array instead of scattered data_get calls.
Fixture and test builders. Build deep expectation arrays from sparse path/value pairs in Pest/PHPUnit data providers. This is where a package like this earns its keep: no production risk, and the path syntax makes the test intent obvious.
Do not put it in hot loops. If you are reading thousands of paths per request (e.g. a large import), the per-call explode() and loop overhead adds up; resolve the paths once into a flat map at the start of the job.
Integration tip: if you only ever call read, you are paying for a writer you do not need. Laravel's data_get() has been in the framework since 5.5 and does exactly that.
Dots in real keys. write('app.name', ...) cannot address a key literally named app.name. If your data has dotted keys (common with translation files and .env-derived config), this class will silently split them. Escape or use a different separator if the API supports one — check before assuming.
Mutation semantics are the pitfall. Arrays are copy-on-write in PHP. If the constructor takes the array by value, write() either stores the mutated copy internally (so you must fetch it back) or accepts a reference (so you must pass a variable, not a literal). Passing $writer->write(...) the result of a function call to a by-reference parameter is a fatal error. Read the signature; write a one-line test asserting the array you care about actually changed.
Missing-path behaviour is undefined by the description. Whether reading 'a.b.c' where b is a string throws, returns null, or overwrites b is the single most important thing to pin down. Add tests for: missing intermediate key, existing scalar where an array is expected, and empty-string path. If the package is loose here, wrap it so your app's behaviour is defined by your tests, not the dependency's.
No maintenance signal. 1 star, 0 dependents, a single author, and a component- naming convention that suggests it was extracted from an internal project. Pin the exact version in composer.json and commit the lock file; do not use ^ ranges on an unmaintained package. Budget an afternoon to vendor the class into app/Support/ if it ever blocks a PHP upgrade — it is small enough that this is cheap insurance.
Overlap with the framework. Arr::get, Arr::set, Arr::has, data_get, and data_set cover all of this, are covered by Laravel's BC promise, and are already in illuminate/support. The honest assessment: adopt this package only if its API reads better to you than Arr::set($array, 'user.profile.name', $v), or if you need the same code path outside Laravel (Symfony console commands, a queue worker sharing a library). Otherwise the dependency is net-negative — you are trading a guaranteed-present API for one nobody is testing.
No static analysis stubs. With a tiny package you will likely get mixed everywhere and no generics. Add a @phpstan-type alias or a thin typed wrapper so phpstan/psalm does not degrade in the files that touch it.
Extension point is the wrapper, not the class. Unless the package exposes an interface you can implement, do not subclass it — its internals are its own. Put your seam in your own code (see the interface pattern above) so swapping back to Arr is a one-line bind.
How can I help you explore Laravel packages today?