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

Console Laravel Package

webmozart/console

webmozart/console is a PHP library for building command-line applications with clean input/output abstractions, argument and option parsing, command routing, ANSI formatting, and interactive prompts—making CLI tools easier to structure and test.

Deep Wiki
Context7

Getting Started

Start by pinning the package and wiring up a front-controller script. The whole framework hangs off two objects: an ApplicationConfig (the concrete DefaultApplicationConfig is the one you want) and a ConsoleApplication that consumes it.

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

use Webmozart\Console\ConsoleApplication;
use Webmozart\Console\Config\DefaultApplicationConfig;
use Webmozart\Console\Api\IO\IO;

$config = new DefaultApplicationConfig();

$config
    ->command('greet', 'Greets a person')
    ->argument('name', 'The name of the person to greet')
    ->option('shout', 's', 'Print the greeting in upper case')
    ->callback(function (IO $io, $name, $shout) {
        $io->writeLine($shout ? strtoupper("Hello, $name!") : "Hello, $name!");
    });

(new ConsoleApplication($config))->run();

Where to look first:

  1. Webmozart\Console\Config\DefaultApplicationConfig — the fluent DSL you will spend 90% of your time in.
  2. Webmozart\Console\Api\IO\IO — the object injected into every handler; it replaces Symfony Console's separate Input/Output pair.
  3. Webmozart\Console\Api\Command\CommandHandler — the interface to implement when a closure stops being enough.
  4. The IO implementations under Webmozart\Console\IO\ (e.g. BufferedIO) — this is the key to testing.

First use case: a one-command script (bin/console greet Ada). Note that the callback parameters are resolved by name against the declared arguments/options, so $name and $shout are filled automatically — you are not reading $io->getArgument(...) by hand.

Implementation Patterns

Declarative command tree. Keep all command definitions in one config builder and register it from your front controller. Arguments and options are declared on the CommandConfig, then a handler/callback is attached:

$config->command('user:create', 'Create a user')
    ->argument('email')
    ->option('admin', null, 'Grant admin rights')
    ->option('role', 'r', 'Role to assign', 'user') // default value
    ->handler(new CreateUserHandler());

Handler classes for anything non-trivial. Implement CommandHandler (handle(IO $io)) and return an integer exit code. This keeps the config file declarative and makes handlers unit-testable without booting the application. Use closures only for tiny commands.

Parameter injection in callbacks. Declare the dependencies you need in the closure signature in any order — IO, argument names, option names, and (in many setups) services you have registered. This removes most boilerplate and is the package's nicest ergonomic win over writing raw getArgument() calls.

Sub-commands and aliases. Use beginCommand() / end() to nest:

$config->beginCommand('cache')
        ->beginCommand('clear')
            ->callback(fn (IO $io) => $io->writeLine('Cache cleared.'))
        ->end()
    ->end();

end() returns to the parent level, so the indentation mirrors the command tree. Add ->alias('ca') on a command to shorten typing.

IO discipline. Write normal output with $io->writeLine(), diagnostics with $io->getErrorOutput()->writeLine(), prompt with $io->readLine() / $io->ask() and guard interactive prompts with $io->isInteractive(). Return or set a non-zero exit code for failures so shell scripts and CI can react.

Testing pattern. This is where the package earns its "easily testable" tagline. Build the ApplicationConfig, construct ConsoleApplication, and run it with a buffered IO built from string/stream input instead of STDIN:

$io = new BufferedIO("Ada\n", $out = fopen('php://memory', 'w+'), $err = fopen('php://memory', 'w+'));
$application->run($io);
rewind($out);
$this->assertSame("Hello, Ada!\n", stream_get_contents($out));

Alternatively register a custom IOFactory on the config (e.g. setIOFactory()) so every run in the test suite is hermetic. Assert on the buffered output and on the returned exit code rather than on side effects.

Integration tips. Keep the ConsoleApplication construction in a dedicated bootstrap file so Symfony/container wiring lives in one place. If you already use a DI container, resolve CommandHandler instances from it inside a small factory callback rather than new-ing them in the config.

Gotchas and Tips

  • The package is effectively abandoned. Last tagged release was February 2016. Pin an exact version in composer.json, and expect deprecation notices or reflection-related breakage on modern PHP (7.4+/8.x). Test your specific PHP version before committing; if you hit runtime errors, Symfony Console or a maintained fork is the pragmatic escape hatch.
  • It is not Symfony Console. IO is a single object, not InputInterface + OutputInterface. Don't copy Symfony snippets; argument/option definitions and the run() signature differ. Mixing the two in one project is possible but confusing.
  • Name mismatches fail quietly. Callback injection matches by parameter name. If you declare ->option('dry-run') and type-hint $dryRun, resolution will not do the camelCase conversion for you — either name the parameter to match or read it explicitly from IO.
  • end() accounting. Every beginCommand() needs a matching end(). Missing one silently attaches subsequent commands to the wrong parent, which shows up as "command not found" at runtime.
  • Interactive vs. non-interactive. In CI or piped input, isInteractive() is false and ask() will not block. Assertions that assume a prompt appeared will fail; explicitly set interactivity on the buffered IO in tests.
  • Output formatting tags. The formatter interprets markup in strings (e.g. <info>, <error>). If you print user-supplied text containing < or >, escape it or disable formatting, otherwise you get mangled output or exceptions. Register custom styles on the application config rather than hand-rolling ANSI codes.
  • Error stream separation. Writing errors to $io->getErrorOutput() matters for tests and for 2> redirection; dumping everything to the normal output stream defeats the toolkit's structured output.
  • Help is generated. Descriptions you pass to command(), argument(), and option() become the --help text — write them properly rather than as afterthoughts; there is no separate doc step.
  • Extension point. When the DSL is not enough, subclass DefaultApplicationConfig or implement CommandHandler and register everything there; that keeps you on supported API surface instead of reaching into internals if the abandoned package breaks.
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.
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel
vish4395/laravel-translation-manager
morningtrain/economic
teksite/systeminfo-laravel
atomcms/voting
run6/weather
siokas/laravelembeddirectives
adamlien/pkuuids
gilsonsouza/lara-doc-generator
manelgavalda/enrollment_mobile
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics