A zero-dependency PHP library for building command-line applications. Covers argument parsing, styled output, interactive prompts, tables, and progress bars.
Requires PHP 8.1+
composer require mdalikadar/php-cli-toolkitRegister arguments and options before calling run(), then read values with the accessor methods.
use PhpCliToolkit\Arguments\Parser;
use PhpCliToolkit\Exceptions\ValidationException;
$parser = new Parser();
$parser->registerArg('filename', 'File to process', isRequired: true);
$parser->registerArg('output', 'Output path', isRequired: false, default: 'out.txt');
$parser->registerOption('verbose|v', 'Enable verbose mode');
$parser->registerOption('format|f', 'Output format', isRequired: false, default: 'json');
try {
$parser->run();
} catch (ValidationException $e) {
foreach ($e->errors() as $error) {
echo $error . "\n";
}
exit(1);
}
$file = $parser->getArg('filename');
$output = $parser->getArg('output');
$verbose = $parser->hasOption('verbose');
$format = $parser->getOption('format');Invocation examples:
php script.php data.csv --format=csv -v
php script.php data.csv --verbose| Input form | Registered as | Result |
|---|---|---|
--format=json |
format |
"json" |
--verbose |
verbose |
true |
-v |
verbose|v |
true |
-abc |
a, b, c |
each true |
$parser->getArg('name'); // value or null
$parser->getOption('name'); // value or null (alias-aware)
$parser->hasArg('name'); // true if value is not null
$parser->hasOption('name'); // true if set and not false
$parser->getArgs(); // ['name' => value, ...]
$parser->getOptions(); // ['name' => value, ...]
$parser->synopsis(); // ['arguments' => [...], 'options' => [...]]For multi-command CLIs, extend Command and register with Application.
use PhpCliToolkit\Command\Command;
use PhpCliToolkit\Output\Text;
use PhpCliToolkit\Output\Styles\Colors;
class GreetCommand extends Command {
public function name(): string { return 'greet'; }
public function description(): string { return 'Greet a user by name'; }
protected function setup(): void {
$this->parser->registerArg('name', 'Name to greet', isRequired: true);
$this->parser->registerOption('shout|s', 'Uppercase the output');
}
public function handle(): int {
$name = $this->parser->getArg('name');
$msg = "Hello, {$name}!";
if ($this->parser->hasOption('shout')) {
$msg = strtoupper($msg);
}
(new Text($msg))->style([Colors::GREEN])->write();
return 0;
}
}use PhpCliToolkit\Command\Application;
$app = new Application('my-tool', '1.0.0');
$app->register(new GreetCommand(), new OtherCommand());
$app->run();Invocation:
php cli.php greet Alice
php cli.php greet Alice --shout
php cli.php helpApplication automatically catches ValidationException and prints each error before exiting with code 1. The built-in help / --help / -h command lists all registered commands.
Text renders ANSI-styled output with automatic word-wrap to the terminal width.
use PhpCliToolkit\Output\Text;
use PhpCliToolkit\Output\Styles\Colors;
use PhpCliToolkit\Output\Styles\BgColors;
use PhpCliToolkit\Output\Styles\Formattings;
(new Text('Success!'))->style([Colors::GREEN, Formattings::BOLD])->write();
(new Text('Warning!'))->style([Colors::YELLOW])->write();
(new Text('Error!'))->style([Colors::WHITE, BgColors::RED])->write();
(new Text('Info'))->style([Colors::CYAN, Formattings::ITALIC])->write();Colors (foreground):
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE
and bright variants: BRIGHT_BLACK, BRIGHT_RED, … BRIGHT_WHITE
BgColors (background):
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE
and bright variants: BRIGHT_BLACK, … BRIGHT_WHITE
Formattings:
BOLD, DIM, ITALIC, UNDERLINE, BLINK, INVERT, HIDE, RESET
padding() and margin() are named-argument shortcuts over space() and edgeSpace():
(new Text('Hello'))
->style([Colors::WHITE, BgColors::BLUE])
->padding(top: 1, bottom: 1, left: 2, right: 2, style: [BgColors::BLUE])
->margin(top: 1, bottom: 1)
->write();space() adds inner padding around the text. edgeSpace() adds outer padding that spans the full terminal width. Use the position constants Text::TOP, Text::BOTTOM, Text::LEFT, Text::RIGHT, or Text::ALL.
(new Text('Boxed message'))
->style([Colors::WHITE, BgColors::BLUE])
->space(Text::ALL, 1, [BgColors::BLUE])
->edgeSpace(Text::TOP, 1, [BgColors::BRIGHT_BLACK])
->edgeSpace(Text::BOTTOM, 1, [BgColors::BRIGHT_BLACK])
->write();The space() call signature: space(position, count, style_array, char = ' '). The char can be any character, so you can draw custom borders:
(new Text('Section title'))
->style([Colors::BRIGHT_WHITE, Formattings::BOLD])
->edgeSpace(Text::BOTTOM, 1, [Colors::BRIGHT_BLACK], '-')
->write();Renders an ASCII table to stdout. Column widths are computed automatically.
use PhpCliToolkit\Output\Table;
$table = new Table(
headers: ['Name', 'Role', 'Status'],
rows: [
['Alice', 'Admin', 'Active'],
['Bob', 'Editor', 'Inactive'],
['Carol', 'Viewer', 'Active'],
]
);
$table->render();Output:
+-------+---------+----------+
| Name | Role | Status |
+-------+---------+----------+
| Alice | Admin | Active |
| Bob | Editor | Inactive |
| Carol | Viewer | Active |
+-------+---------+----------+
toString() returns the table as a string without printing it.
Lays out content in columns side by side. Each column is a Cell that handles its own word-wrap and padding.
Pass an integer to split the terminal width into N equal columns. Pass a gap to control spacing between them.
use PhpCliToolkit\Output\Grid;
use PhpCliToolkit\Output\Styles\Colors;
use PhpCliToolkit\Output\Styles\BgColors;
(new Grid(columns: 3, gap: 2))
->cell('Left column content', style: [Colors::WHITE, BgColors::BLUE])
->cell('Middle column content', style: [Colors::WHITE, BgColors::GREEN])
->cell('Right column content', style: [Colors::WHITE, BgColors::RED])
->render();Cells wrap automatically at the column boundary. If cells in the same row have different heights, shorter ones are padded with blank lines so all columns stay aligned.
Pass an array of integers to control each column's exact width in characters:
(new Grid(columns: [20, 40, 20], gap: 1))
->cell('Narrow')
->cell('This is the wide centre column with more room for content')
->cell('Narrow')
->render();Each cell has independent top / right / bottom / left padding. Use named constructor arguments on Cell directly, or pass them as positional args to the cell() shorthand:
use PhpCliToolkit\Output\Cell;
(new Grid([30, 30], gap: 2))
->add(new Cell(
content: 'Padded cell',
style: [Colors::WHITE, BgColors::BLUE],
paddingTop: 1,
paddingBottom: 1,
paddingLeft: 2,
paddingRight: 2,
paddingStyle: [BgColors::BLUE],
))
->cell('No padding here')
->render();Add more cells than there are columns and the grid automatically flows into multiple rows:
$headers = ['Name', 'Role', 'Status'];
$rows = [['Alice', 'Admin', 'Active'], ['Bob', 'Editor', 'Inactive']];
$grid = new Grid([20, 20, 20], gap: 1);
foreach ($headers as $h) {
$grid->cell($h, style: [Colors::BRIGHT_WHITE, Formattings::BOLD]);
}
foreach ($rows as $row) {
foreach ($row as $cell) {
$grid->cell($cell);
}
}
$grid->render();Returns the rendered grid as a string without printing it, useful for composing output:
$output = (new Grid([40, 40]))->cell('A')->cell('B')->toString();Renders an in-place progress bar that updates on the same line.
use PhpCliToolkit\Output\ProgressBar;
$bar = new ProgressBar(total: 100);
foreach ($items as $item) {
process($item);
$bar->advance();
}
$bar->finish();Output (updates in place):
[==============================>---------] 75% (75/100) ETA:1s
advance(int $step = 1) moves the bar forward. finish() snaps to 100% and writes a newline.
All methods are static and read from STDIN.
use PhpCliToolkit\Input\Prompt;
$name = Prompt::ask('What is your name?', default: 'World');
// What is your name? [World]:
$confirmed = Prompt::confirm('Are you sure?', default: false);
// Are you sure? [y/N]:
$password = Prompt::secret('Enter password');
// Enter password: (input hidden)
$color = Prompt::select('Pick a color', ['Red', 'Green', 'Blue']);
// Pick a color
// [1] Red
// [2] Green
// [3] Blue
// Choice:select() re-prompts automatically on invalid input.
All library exceptions extend PhpCliToolkit\Exceptions\CliException.
use PhpCliToolkit\Exceptions\ValidationException;
use PhpCliToolkit\Exceptions\CliException;
try {
$parser->run();
} catch (ValidationException $e) {
foreach ($e->errors() as $error) {
echo $error . "\n";
}
exit(1);
} catch (CliException $e) {
echo $e->getMessage() . "\n";
exit(1);
}ValidationException is thrown by Parser::parse() and Parser::run() when required arguments or options are missing. Application catches it automatically when using the command system.
composer install
./vendor/bin/phpunit