leovie/php-param-generator
A lightweight PHP parameter generator for building parameter definitions or argument lists from concise configuration. Useful for keeping request, method, and service parameters consistent across Laravel and plain PHP projects.
The package is framework-agnostic and has no Laravel service provider, config file, or facade. Treat it as a build-time/dev tool, not a runtime dependency:
composer require --dev leovie/php-param-generator:^2.1.1
Pin an explicit ^2.1.1 rather than a floating range — the v2.1.1 patch changed internal pipeline ordering (serialization deferred until after flattening), and output stability is not explicitly guaranteed by the changelog.
Where to look first
src/ — the project is small enough to read end-to-end in one sitting; do this rather than guessing method names. The public classes are the contract; everything else is internal pipeline (build → flatten → serialize).tests/ directory — the fastest way to learn the intended usage is to read how the maintainer instantiates the generator and asserts on output.Because the package builds strings like Type $name = default from structured input, the README examples will show the input shape (arrays/objects describing parameters) and the output format (a signature string or parameter list). Confirm exact class and method names against the source before writing anything — the docs are thin and the version in your lockfile is the source of truth.
First use case
Sanity-check the tool in a throwaway script before wiring it anywhere:
// Shape is illustrative — confirm class/method names against src/ and README.
$generator = new /* ParameterGenerator class */();
echo $generator->/* generate */([
['type' => 'string', 'name' => 'email', 'default' => null, 'nullable' => true],
['type' => 'int', 'name' => 'limit', 'default' => 10],
]);
// Expected: "?string $email = null, int $limit = 10"
If that prints what you expect, you have validated the two things that matter on day one: the input data shape and the exact output formatting. Then pick one real target — an Artisan scaffolding command or a PHPUnit data provider — and wire it in there only.
1. Wrap it in a thin internal adapter
Never call the generator directly from dozens of call sites. Put a single class in app/Support/Codegen/ (or a small package) that owns the input contract:
namespace App\Support\Codegen;
final class SignatureBuilder
{
public function fromShape(array $shape): string
{
// One place that knows the vendor API.
return (new /* VendorGenerator */())->/* generate */($shape);
}
}
This is the single most valuable pattern given the bus factor. If the package stalls or you swap it, it becomes a one-file change. It also lets you normalize options (formatting, nullable handling) in one place.
2. Artisan command for scaffolding / DTO generation
The natural Laravel integration is a generator command that reads a declarative shape (a PHP array, a YAML/JSON config, or a DB schema) and writes a file:
// app/Console/Commands/GenerateDtoCommand.php
public function handle(SignatureBuilder $builder)
{
$shape = config('shapes.order'); // e.g. id:int, total:float, note:?string
$signature = $builder->fromShape($shape);
$this->putFile('app/DTOs/Order.php', view('stubs.dto', [
'constructorSignature' => $signature,
]));
}
Keep the generated output committed to the repo. That is the whole point: the artifact is ordinary PHP you own, and the dev dependency is disposable.
3. Golden-file / snapshot tests around generated output
Because a patch release altered pipeline ordering, the acceptance criterion for any upgrade is the diff, not the changelog. Store the expected generated output as fixtures and diff in CI:
public function test_dto_shape_output_is_stable(): void
{
$actual = $this->app->make(SignatureBuilder::class)
->fromShape(config('shapes.order'));
$this->assertSame(
file_get_contents(base_path('tests/fixtures/order.signature')),
$actual
);
}
Deterministic output matters here — if you extend the shape with metadata, never include timestamps, random ids, or unordered maps.
4. Test data providers
The use case the description leans on: generate parameter combinations for PHPUnit/Pest data providers instead of hand-writing arrange blocks. This is a good fit where the parameter list is mechanical; it is a bad fit where the test's intent lives in the specific values. A one-paragraph team convention ("generated parameters for new tests only; no assertion may depend on a specific generated value") prevents the readability drift that kills these tools in review.
5. Dynamic proxy / stub creation
For internal tooling that builds proxy classes or stubs at runtime or build time, feed the parameter list from reflection over an existing class. This pairs well with an existing factory layer — it augments, rather than replaces, Faker-based factories.
Upgrade workflow (adopt this permanently)
composer require --dev leovie/php-param-generator:^2.1.1
git add composer.json composer.lock
git commit -m "chore: bump php-param-generator to 2.1.1"
php artisan codegen:regenerate # or whatever your command is
git diff # <-- this diff is the review gate
Keep the dependency bump and the regenerated output in separate commits so the diff is isolated and reviewable.
Serialization ordering changed in v2.1.1 — verify before trusting output
PR #9 ("Don't serialize while flattening") defers serialization to a distinct later stage. For nested arrays and object graphs this can subtly change the serialized shape even though the API is unchanged. Snapshot tests catch this; assertEquals on individual values may not. If you have any surface that depends on the serialized form, diff 2.1.0 vs 2.1.1 output before merging the bump.
Keep it in require-dev, always
It should never appear on a request path. The blast radius of abandoning the package is a regenerated commit, not a production incident — but only if you respect that boundary.
Reflection sensitivity to newer PHP
The library reflects over signatures, so it is sensitive to language features: union and intersection types, enums, readonly properties, and promoted constructor parameters. If you standardize on a recent PHP version, validate generation against a representative signature set before broad adoption, not after.
Thin docs, single maintainer
No SLA, no LTS branch, no security-response commitment, and one-line release notes. Budget a recurring dependency-review slot; be prepared to read src/ rather than the changelog to understand behavior. The v2.1.1 note required inference to interpret — assume future notes will too.
Debugging generated output
Do not debug inside the vendor pipeline. Dump the input shape and the final string side by side in your adapter. Most surprises are input-shape mismatches (wrong key names, nullable vs a ?-prefixed type, default values interacting with nullability), not generator bugs.
Nullable and default handling is where formatting quirks live
?Type $x = null, Type $x = null, and Type $x are three different things. Test all three in your adapter's own unit tests so a vendor formatting change surfaces there, not in fifty generated files.
Extension points: check whether serialization is separately invokable
The v2.1.1 change implies serialization is now its own stage. If that stage is publicly reachable, it is the correct hook for custom value objects and custom default serialization — use it instead of post-processing the generated string. Verify in src/ whether it is public or internal; if internal, keep all customization in your adapter.
Never post-process generated strings with regex
If the output format is not what you need, change the input shape or the package's output-format configuration. String surgery on generated signatures is the number one way this kind of tool becomes unmaintainable.
Do not over-adopt
If you only need the classes once, generate them, commit them, and drop the dependency. Carrying a two-use dev dependency through years of audits costs more than it saves.
How can I help you explore Laravel packages today?