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

Simple Console Laravel Package

asika/simple-console

Lightweight PHP library for building simple command-line applications. Define commands, parse arguments and options, format console output, and handle user input without a full framework—ideal for small CLI tools, scripts, and maintenance utilities.

View on GitHub
Deep Wiki
Context7

Getting Started

asika/simple-console is a single-file, dependency-free CLI kernel. There is no application container, no command registry, and no auto-generated help — you subclass Asika\SimpleConsole\Console, implement doExecute(), and get argv parsing, stdout/stderr helpers and exit codes for free.

1. Install (or vendor the file):

composer require asika/simple-console

Because the whole framework is one file (src/Console.php), you can also just copy it into bin/ for zero-dependency build scripts.

2. Read the source first. It is roughly 300–500 lines. Skim it once, top to bottom, and you will know the entire API — getArgument(), getOption(), out(), err(), execute() and the option-parsing regex. There is nothing hidden behind traits or service providers.

3. Smallest working command:

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

use Asika\SimpleConsole\Console;

class GreetCommand extends Console
{
    protected $help = <<<HELP
Usage: greet [name] [-u|--upper]

Say hello.
HELP;

    protected function doExecute(): int
    {
        $name = $this->getArgument(0, 'World');
        $msg  = "Hello, {$name}!";

        if ($this->getOption('u') || $this->getOption('upper')) {
            $msg = strtoupper($msg);
        }

        $this->out($msg);
        return 0;
    }
}

exit((new GreetCommand($argv))->execute());
php greet.php Alice --upper

4. First real use case: replace one-off shell glue. A bin/deploy.php, bin/db-reset.php or a Makefile replacement that needs arguments, flags, prompts and a meaningful exit status is exactly the sweet spot. If you would otherwise write parse_str/getopt() boilerplate, this package pays for itself in ten lines.

Implementation Patterns

One class per command, a base class for bootstrap. Put shared setup (autoload, config load, DB connection, logger) in an abstract AppCommand extends Console, then have each command extend that. Keep doExecute() free of exit() calls so it stays testable.

abstract class AppCommand extends Console
{
    protected function boot(): void { /* load config */ }
    protected function doExecute(): int { $this->boot(); return $this->handle(); }
    abstract protected function handle(): int;
}

Argument vs option discipline. Positional arguments are read by index (getArgument(0), getArgument(1, $default)); options by name (getOption('v') or getOption('verbose')). Decide early whether a flag is a boolean switch or takes a value (--env=prod), and document it in $help. Treat getArgument() indexes as part of your public contract — reordering them is a breaking change for anyone's CI script.

Subcommand dispatcher. The package has no router, so the standard pattern is a single front controller that peeks at $this->getArgument(0) and delegates:

$map = [
    'build'  => BuildCommand::class,
    'deploy' => DeployCommand::class,
];
$name = $this->getArgument(0, 'list');

if (!isset($map[$name])) {
    $this->err("Unknown command: {$name}");
    return 1;
}

return (new $map[$name]($this->input))->execute();

This keeps the tiny framework tiny and gives you a clean bin/app <command> UX.

Interactive scripts with a --no-interaction escape hatch. Ask only when stdin is a TTY and the user hasn't opted out. Every prompt should have a flag that supplies the value instead, otherwise your script can never run in CI.

$env = $this->getOption('env');

if ($env === null) {
    if ($this->getOption('n')) {
        $this->err('--env is required in non-interactive mode');
        return 1;
    }
    $env = $this->ask('Target environment:');
}

Stream separation. Route machine-readable results and progress to out() (stdout) and diagnostics/warnings to err() (stderr). That single convention makes php bin/report.php | jq work and keeps CI logs readable. Honour a quiet flag by guarding every progress line.

Composer script integration. Wire commands into composer.json and remember the -- separator — Composer strips everything before it:

{ "scripts": { "release": "php bin/console.php build --minify" } }
composer release -- --env=staging

Testing. Because the constructor takes an argv array, you can drive commands without a subprocess. Instantiate with a hand-built array, run execute(), assert on the returned int and on captured output streams. This is the main reason to return codes instead of calling exit().

Packaging as a phar or standalone bin. The framework's lack of runtime dependencies makes it ideal for a box/php-scoper phar. Since it is one MIT file, you can also inline it and ship a truly single-file tool.

Gotchas and Tips

  • argv offset. The command typically expects the raw $argv (with the script name at index 0) and handles slicing internally — but confirm this in src/Console.php before building arrays for tests. Off-by-one on getArgument(0) is the single most common source of "why is my argument empty".
  • Option syntax is regex-based, not a full parser. Expect support for -v, -abc bundling, --verbose and --key=value. Do not assume --key value (space-separated) works. If you need that, read the value from the next positional argument yourself or normalise argv before constructing the command.
  • Unknown options are not rejected by default. Typos like --verbsoe may be silently ignored. If your command's correctness depends on a flag, validate at the top of doExecute() and fail loudly with a non-zero code.
  • No built-in --help generation. You are responsible for the $help string and for handling -h. Keep it short but include usage, every option, and one example — future-you reading CI logs will thank you.
  • Exit codes are the contract. Return 0 on success, non-zero on failure, and never exit() inside doExecute() — that kills testability and skips any cleanup. Let the thin bin/ wrapper do exit($cmd->execute()).
  • No ANSI colour helper. Emit raw escape sequences only when stream_isatty(STDOUT) is true; otherwise plain text. Colour codes leaking into GitHub Actions or Jenkins logs is a classic self-inflicted wound.
  • out() usually appends a newline. Don't add \n yourself or you get double-spaced logs; conversely, use whatever raw-write path the class offers for progress bars that need \r.
  • PHP version drift. Pin a version range in composer.json and check the latest release notes (last tag 2025-06-06). With only 26 stars and near-zero dependents, the ecosystem feedback loop is thin — treat minor bumps as potentially breaking and lock the version in CI-heavy projects.
  • Vendor or fork it. Given the size and the low bus factor, the correct risk posture is: treat the file as part of your codebase. Copy it in, or maintain a fork, so an abandoned upstream can never block you. There is no security surface to speak of (no deps, no network I/O).
  • Namespace collision. If you vendor the file into a project that already autoloads Asika\SimpleConsole, Composer will warn or you'll get a duplicate-class fatal. Only do one or the other.
  • Extension points are deliberately minimal. Override doExecute() for logic, override execute() if you need pre/post hooks or global exception-to-exit-code translation, and use the $input/$output properties for anything the helpers don't cover. If you find yourself wanting a container, events or command discovery, that is the signal to graduate to Symfony Console rather than to bolt those features onto this package.
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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo