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

Php Cli Laravel Package

splitbrain/php-cli

splitbrain/php-cli is a lightweight PHP library for building command-line tools. It offers argument parsing, colored output, tables, progress bars, and interactive prompts, making portable CLI apps easier without heavy dependencies.

Deep Wiki
Context7

Getting Started

splitbrain/php-cli is a dependency-free, single-namespace PHP library for building CLI tools. There is no service provider, no facade, and no Artisan integration — it is deliberately framework-agnostic. In a Laravel codebase you'd reach for it only for standalone bin/ scripts that must run without booting the framework (installers, cron entry points, package tooling), not for php artisan commands.

Install:

composer require splitbrain/php-cli

The four classes you will actually touch:

Class Role
splitbrain\phpcli\CLI Abstract base; you extend it and implement setup() + main()
splitbrain\phpcli\Options Option/command/argument registration and parsed access
splitbrain\phpcli\Colors ANSI styling and TTY detection
splitbrain\phpcli\Table Plain-text table renderer

Minimal working script (bin/report, chmod +x it):

#!/usr/bin/env php
<?php
require __DIR__ . '/../vendor/autoload.php';

use splitbrain\phpcli\CLI;
use splitbrain\phpcli\Options;

class ReportCLI extends CLI
{
    protected function setup(Options $options)
    {
        $options->setHelp('Generate a report.');
        $options->registerOption('n', 'name', 'Filter by name', 'name');
        $options->registerOption('d', 'dry-run', 'Do not write files');
        $options->registerArgument('target', 'Output directory', true);
    }

    protected function main(Options $options)
    {
        $opt = $options->getOpt();          // ['name' => ..., 'dry-run' => ...]
        $args = $options->getArgs();        // positional args, in order

        $this->info("Writing to {$args[0]}");
        $this->success('Done');
    }
}

(new ReportCLI())->run();

Where to look first: Options::registerOption() / registerCommand() / registerArgument() in setup(), then getOpt() / getCmd() / getArgs() in main(). Help text is generated automatically from your registrations, so the fastest sanity check is running the script with --help — the output mirrors exactly what you declared.

First real use case: wrap an existing maintenance script that currently reads $argv by hand. Move the ad-hoc flag parsing into setup(), replace echo/fwrite(STDERR, ...) with $this->info() / $this->error(), and you get --help, consistent exit codes, and colored output for free in about twenty lines.

Implementation Patterns

Treat setup() as declarative only. It runs before parsing; anything with side effects (DB connections, file reads) belongs in main(). If parsing fails, setup() will have already run.

Normalize options into a local config array immediately. getOpt() gives you the raw parsed shape; merge it with defaults so downstream code never branches on false vs null:

protected function main(Options $options)
{
    $cfg = array_merge([
        'name'    => null,
        'dry-run' => false,
    ], $options->getOpt());
}

Subcommand dispatch via getCmd(). Register each command in setup(), then switch in main(). Register the union of all flags across commands in setup() (or a shared "common options" helper method) since the parser is global, and validate per-command in the handler:

protected function setup(Options $options)
{
    $options->registerCommand('build', 'Compile assets');
    $options->registerCommand('clean', 'Remove build artifacts');
    $options->registerOption('f', 'force', 'Skip confirmation'); // valid for both
}

protected function main(Options $options)
{
    switch ($options->getCmd()) {
        case 'build': $this->build($options); break;
        case 'clean': $this->clean($options); break;
        default:
            $this->error('Unknown command');
            exit(1);
    }
}

Logging is your status channel. The base class exposes leveled helpers — debug(), info(), success(), notice(), warning(), error(), fatal() — each applying appropriate color. Drive verbosity from a flag:

$this->setLogLevel($opt['verbose'] ? CLI::DEBUG : CLI::INFO);

Then sprinkle $this->debug() through hot loops without gating every call site.

Failures: prefer fatal() over exit(). fatal() logs through the color pipeline and unwinds via splitbrain\phpcli\Exception, which run() catches to emit a clean message and a non-zero exit code. exit(1) bypasses the color/formatting path and any finally blocks you own.

Tables for machine-adjacent human output. Table renders fixed-width ASCII that stays readable in CI logs (unlike box-drawing output):

$table = new Table($this->colors);
$table->setHeaders(['Name', 'Status']);
foreach ($rows as $r) {
    $table->addRow([$r->name, $r->status]);
}
echo $table->render();

Testing pattern. run() reads the global $argv, which makes full-CLI tests awkward. Test the parse layer directly instead — instantiate Options, register your options, and call parse(['script', '--name=foo', 'target']) to assert getOpt()/getArgs() without touching globals. Keep main() thin and push logic into injectable collaborators so the interesting code is unit-testable without the CLI harness.

Gotchas and Tips

  • Everything is global-ish. run() pulls from $GLOBALS['argv'] / $_SERVER['argv']. Calling your CLI class from inside another process (a queue worker, a test) will parse that process's arguments. Keep CLI classes as thin entry points only.
  • Option values: -n value and --name=value are the safe forms. Don't assume short-flag bundling (-abc) or optional-argument semantics — the parser is intentionally minimal. Test any exotic syntax you rely on before documenting it.
  • -- terminator and negative arguments. Positional args that begin with - need the -- separator; verify behavior for your version rather than guessing, since the argument/option splitter is the most version-sensitive part of the library.
  • getOpt() shape. Flags come back falsy when absent, and options-with-arguments come back false/null rather than a default. Always array_merge() defaults as shown above — isset() alone will bite you on empty-string values.
  • fatal() throws. If you have cleanup (temp dirs, locks, transactions), either register a shutdown handler or wrap main() body in try/finally. An uncaught splitbrain\phpcli\Exception from deep inside your code will surface as a formatted fatal message — sometimes useful, sometimes masking a bug.
  • Colors and no-TTY environments. The Colors class sniffs terminal support, so piped output and cron runs generally come out clean, but Windows consoles and TERM=dumb containers are the usual edge cases. Always register a --no-color escape hatch and honor it by disabling $this->colors before logging anything.
  • Log streams. Verify which stream your version's log methods write to before piping output into a file you intend to parse — mixing status lines into data output is the classic CLI bug.
  • Table width is content-driven. Wide values (long paths, stack traces) produce tables wider than an 80-column terminal; pre-truncate or ellipsize cell content yourself. Multi-byte content is handled, but very wide Unicode glyphs can still skew alignment.
  • Help text ordering follows registration order. Group related options together in setup() — it's the only control you have over the generated --help layout, and it's the documentation most users will actually read.
  • Version/name metadata (setName(), setVersion()) is worth setting even for internal tools; --version in a bug report saves a round trip.
  • Extension points are narrow by design. Subclass CLI for new tools, but avoid subclassing Options — the parser API is small and stable, and wrapping it usually creates more friction than it removes. Put shared behavior in a base CLI subclass or a trait with your own setupCommonOptions() helper.
  • Scope check for Laravel teams: if the script must know about your application (Eloquent, config, queues), use Illuminate\Console/Symfony Console instead. Reach for this package when you explicitly want zero framework bootstrapping and a ~400 KB vendor footprint.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core