artemeon / console
Wrapper library around `symfony/console` providing a customized console styling and some helpers.
Requires
- php: ^8.3
- illuminate/support: ^11.40.0|^12.16.0|^13.2.0
- laravel/prompts: ^0.3.22
- nunomaduro/termwind: ^1.5|^2.0
- symfony/console: ^5.0|^6.0|^7.0|^8.0
Requires (Dev)
- laravel/pint: ^1.30.5
- pestphp/pest: ^5.0
- pestphp/pest-plugin-type-coverage: ^5.0
- phpstan/phpstan: ^2.2.8
- rector/rector: ^2.6.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Wrapper library around symfony/console providing a customized console styling and some helpers.
It brings a Laravel-style command API to any Symfony Console application: signature-based input definitions, interactive prompts powered by Laravel Prompts and Termwind-styled output, without requiring the Laravel framework.
Requirements
- PHP 8.3 or higher
symfony/console5, 6, 7 or 8
Installation
composer require artemeon/console
Quick Start
Extend Artemeon\Console\Command, define a signature and implement handle() (or __invoke()):
use Artemeon\Console\Command; class GreetCommand extends Command { protected string $signature = 'greet {name : The name of the person} {--y|yell : Print the greeting in uppercase}'; protected ?string $description = 'Greet someone'; public function handle(): int { $greeting = 'Hello, ' . $this->argument('name') . '!'; $this->info($this->option('yell') ? strtoupper($greeting) : $greeting); return self::SUCCESS; } }
Register it like any other Symfony command:
use Symfony\Component\Console\Application; $app = new Application(); $app->add(new GreetCommand()); $app->run();
Defining Commands
The following properties can be set on a command:
| Property | Description |
|---|---|
$signature |
Name, arguments and options of the command (see below). |
$name |
Command name, used when no $signature is defined. |
$description |
Short description shown in the command list. |
$help |
Help text shown by help <command>. |
$aliases |
Alternative names for the command. |
$hidden |
Hide the command from the command list. |
Signature Syntax
Arguments and options are wrapped in curly braces. A description can be added after :.
| Syntax | Meaning |
|---|---|
{user} |
Required argument |
{user?} |
Optional argument |
{user=admin} |
Optional argument with default value |
{users*} |
Required array argument |
{users?*} |
Optional array argument |
{users=*admin,guest} |
Array argument with default values |
{--force} |
Flag (no value) |
{--f|force} |
Flag with shortcut |
{--queue=} |
Option with optional value |
{--queue==} |
Option with required value |
{--queue=default} |
Option with default value |
{--id=*} |
Array option with optional values |
{--id==*} |
Array option with required values |
{--id=*1,2} |
Array option with default values |
{--ansi!} |
Negatable option (--ansi / --no-ansi) |
{user : The user's name} |
Any parameter with a description |
Without a Signature
Instead of a signature, you can set $name and return Symfony input definitions:
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; protected string $name = 'greet'; protected function getArguments(): array { return [new InputArgument('name', InputArgument::REQUIRED, 'The name of the person')]; } protected function getOptions(): array { return [new InputOption('yell', 'y', InputOption::VALUE_NONE, 'Print the greeting in uppercase')]; }
Accessing Input
$this->argument('name'); // single argument $this->arguments(); // all arguments $this->hasArgument('name'); $this->option('yell'); // single option $this->options(); // all options $this->hasOption('yell');
Prompting for Missing Arguments
When a required argument is missing and the terminal is interactive, the user is prompted for it automatically. The question is derived from the argument's description (What is the name of the person?). You can customize the questions:
protected function promptForMissingArgumentsUsing(): array { return [ 'name' => 'Who should be greeted?', 'email' => ['What is the email address?', 'E.g. jane@example.com'], // label and placeholder 'role' => fn () => $this->select('Which role?', ['admin', 'editor']), // custom prompt ]; }
Override afterPromptingForMissingArguments() to run logic once all missing arguments are collected.
Output
The command output uses ArtemeonStyle, a Termwind-based style with labeled message blocks:
$this->title('Deployment'); $this->section('Assets'); $this->info('Fetching 128 records'); $this->success('Import finished'); $this->warn('3 records skipped'); $this->error('Connection failed'); $this->note('Remember to clear the cache'); $this->caution('This cannot be undone'); $this->text('Plain text'); $this->line('A line', 'comment'); $this->listing(['First', 'Second']); $this->newLine(); $this->table(['ID', 'Name'], [[1, 'Jane'], [2, 'John']]); $this->grid(['apple', 'banana', 'cherry']); $this->callout('Heads up', 'A new version is available.');
Prompts
All prompts from Laravel Prompts are available as methods. They fall back to Symfony questions on Windows and are skipped when the input is non-interactive.
$name = $this->ask('What is your name?', placeholder: 'E.g. Jane', required: true); $bio = $this->textarea('Tell us about yourself'); $password = $this->password('Password'); $age = $this->number('How old are you?', min: 0, max: 150); $confirmed = $this->confirm('Do you want to continue?'); $brand = $this->select('Choose a car brand', ['audi' => 'Audi', 'bmw' => 'BMW']); $toppings = $this->multiselect('Choose toppings', ['cheese', 'ham', 'olives']); $city = $this->suggest('City', ['Berlin', 'Hamburg', 'Munich']); $city = $this->autocomplete('City', ['Berlin', 'Hamburg', 'Munich']); $userId = $this->search('Search a user', fn (string $value) => findUsers($value)); $row = $this->dataTable(['ID', 'Name'], [[1, 'Jane'], [2, 'John']]); $this->pause();
choice(), secret(), anticipate() and askWithCompletion() are kept as aliases for Laravel-style compatibility. For multi-step input use $this->form().
Progress and Tasks
use Laravel\Prompts\Support\Logger; // Spinner while a callback runs $result = $this->spin(fn () => fetchData(), 'Fetching data...'); // Task with spinner and live log output $this->task('Running migrations', function (Logger $logger) { $logger->line('Migrating users table'); }); // Progress bar over an iterable or a number of steps $this->withProgressBar($users, fn (User $user) => $user->sync()); // Manual progress bar $this->progressStart(100); $this->progressAdvance(); $this->progressFinish(); // Streamed text output $this->stream();
Utilities
$this->toClipboard($token, 'Token copied to clipboard.'); // macOS, Linux (xclip) and Windows $this->notify('Build finished', 'All tests passed'); // desktop notification (macOS and Linux) $this->terminalTitle('Deploying...'); $this->terminalWidth(); $this->terminalHeight(); $this->clear(); $this->clearScreen();
Artemeon\Console\Clipboard::copy() can also be used on its own.
Post Call Hook
Register a closure that runs after every command with the command's exit code, e.g. for logging or telemetry:
use Artemeon\Console\Command; Command::setupPostCallClosure(function (int $exitCode): void { // ... });
Examples
The example directory contains runnable example commands:
php example/example text
php example/example confirm
php example/example select
php example/example multiselect
php example/example password
Development
composer test # run the test suite composer test:type-coverage # check type coverage composer phpstan # static analysis composer pint # check code style composer refactor # run Rector
License
The Artemeon Console Library is open-sourced software licensed under the MIT license.