sebastian/cli-parser
sebastian/cli-parser is a lightweight PHP library for parsing $_SERVER['argv'], extracted from PHPUnit. It gives command-line tools a simple way to read and validate arguments and options without a full CLI framework.
Architecture Fit
sebastian/cli-parser is a single-purpose library: it takes $_SERVER['argv'] plus a getopt-style option specification and returns a structured list of matched options and arguments. It is a leaf dependency — no container, no events, no I/O, no command lifecycle. That makes it architecturally inert: it will not conflict with anything, but it also will not participate in anything.
The fit question for a TPM is therefore not "does it work in Laravel?" (it will, trivially — it's plain PHP) but "where in our architecture do we have CLI parsing that is not already served by Symfony Console?" Laravel's Artisan is a Symfony Console application; every Artisan command already gets tokenization, option/argument definition, validation, --help, --ansi, --no-interaction, and exit codes for free. This package duplicates a strict subset of that. Realistic fit is confined to:
artisan-adjacent shim).--dev graph (though note Symfony Console is already in the graph in any Laravel app).Integration Feasibility
High mechanically, low in value. Installation is composer require --dev sebastian/cli-parser; usage is a handful of lines with no configuration. There is no service provider, no facade, no config file, no publishable assets — so there is nothing to integrate, only to call. That's a feature for a script and a dead end for a product surface.
The feasibility ceiling is set by what the library deliberately omits: subcommands, option/argument grouping, type coercion, dependency rules between options (--format requires --output), array/repeatable options, auto-generated help, interactive prompts, and structured error types you can map to exit codes. Every one of those becomes our code, our tests, and our bug surface. That's the real cost line — not the Composer install.
Technical Risk
phpunit/phpunit itself; Packagist's dependent count won't reflect that. Do not use it as evidence of abandonment — but do use the actual signal: this library's primary consumer is a test runner, so its evolution is driven by PHPUnit's needs, never ours. If our requirements diverge, we fork or we leave.Key Questions
argv/getopt() logic? That is the only scenario with a clear, measurable win — and the win is correctness and maintenance, not capability.--help and predictable errors; this package gives neither.Stack Fit
Poor-to-neutral for the Laravel application layer; acceptable for the edges. The package has no awareness of the service container, the Artisan command lifecycle, Illuminate\Console\Command's input/output helpers, or Laravel's event and exception handling. If a script using it wants config, logging, or DB access, we are manually bootstrapping bootstrap/app.php — at which point the framework is already up and its native CLI facilities are available, which undercuts the premise for using a bare parser.
The one defensible stack position is as a require-dev dependency powering tooling that never intends to be an Artisan command and should never grow into one.
Migration Path
There is nothing to migrate from on the Laravel side — there is no incumbent to displace, because Symfony Console already is the incumbent. Migration only makes sense in the narrow direction of replacing ad-hoc parsing:
$argv / getopt() / explode(' ', $argv[1]) site in the repo. Count them. If the count is one or two, this is a 1–2 day change and arguably not worth a formal decision record.Compatibility
Sequencing
Maintenance
Low runtime maintenance, moderate decision maintenance. The library is stable, tiny, and dependency-free — it will not break our build. The recurring cost is governance: someone must own the "when do we bump the MAJOR" question every time PHP moves, and someone must own the feature gap (help text, validation rules, exit codes) that this package intentionally doesn't fill. If the answer to "who owns the gap" is "whoever wrote the script," we've created a small internal framework with one maintainer and no documentation — the same failure mode as the hand-rolled parsers we were trying to eliminate.
Support
Support flows through the vendor's PHPUnit-driven priorities, not ours. There is no Laravel-specific documentation, no community channel, and no commercial backing. In practice this rarely bites, because the API surface is small enough to read in ten minutes and the source is the documentation. But it means internal onboarding must include a deliberate hand-off note: "here is our option spec convention, here is how we format parse errors, here is why this isn't an Artisan command." That note is the actual support artifact.
Scaling
Scales fine on throughput (parsing is trivial and fast — startup latency is a non-issue) and does not scale at all on feature surface. The moment a script needs subcommands, repeatable options, shell completion, interactive prompts, or generated help, it has outgrown the library and must be rewritten against Symfony Console. This is a foreseeable, one-way cliff, and a TPM should treat "convert to Artisan" as a planned roadmap item with an estimate, not an emergency. The pattern to fund: start standalone, convert when the tool becomes team-facing or customer-facing.
Failure Modes
--help won't exist, and exit codes are ours to define. Two CLI surfaces with two error styles is a real (if minor) product-quality regression.Ramp-Up
Near-zero for any PHP developer — the API is a constructor, a parse call, and an array. Genuinely a 30-minute learning curve. The ramp-up cost sits entirely in the conventions we invent around it: the option-spec style, the error-message format, the exit-code table, the test pattern. Budget a day of engineering time to write those down once, and the ramp-up stays at 30 minutes instead of becoming tribal knowledge.
TPM Recommendation
Adopt only if the CLI inventory surfaces standalone, non-Artisan scripts that currently hand-roll parsing — treat it as a correctness and maintenance upgrade for a narrow, internal, developer-facing surface. Do not adopt it as a CLI framework for Laravel, do not adopt it for anything user-facing, and do not adopt it if the inventory comes back empty. The decision's value is the boundary it forces us to draw: Symfony Console for anything command-shaped, this parser only below the framework boot line. Fund the ADR and the ownership assignment alongside the dependency; the package itself is the cheap part.
How can I help you explore Laravel packages today?