adhocore/cli
A lightweight PHP library to build interactive CLI apps with commands, options, prompts, and colored output. Create structured command-line tools quickly, with input helpers and utilities suited for both simple scripts and larger console applications.
Installation
composer require adhocore/cli
Add to composer.json under autoload-dev if using for dev tools:
"require-dev": {
"adhocore/cli": "^2.0"
}
First Command
Create a basic command class in app/Console/Commands/ (or your preferred directory):
use Adhocore\Cli\Command;
class HelloCommand extends Command
{
protected $name = 'app:hello';
protected $description = 'Prints a greeting';
public function handle()
{
$this->info('Hello, Laravel!');
}
}
Register the Command
In app/Console/Kernel.php, add to $commands:
protected $commands = [
\App\Console\Commands\HelloCommand::class,
];
Run It
php artisan app:hello
For standalone scripts (e.g., deploy.php), bootstrap the CLI system:
require __DIR__.'/vendor/autoload.php';
$cli = new \Adhocore\Cli\Cli();
$cli->addCommand(new class() extends \Adhocore\Cli\Command {
protected $name = 'deploy';
protected $description = 'Deploys the application';
public function handle() {
$this->info('Deploying...');
}
});
$cli->run();
Run with:
php deploy.php deploy
Hierarchical Commands Leverage subcommands for modularity:
class UserCommand extends Command
{
protected $name = 'user';
protected $description = 'User management';
public function handle()
{
$this->addSubcommand(new ListUsersCommand());
$this->addSubcommand(new CreateUserCommand());
$this->runSubcommand();
}
}
Option/Argument Definitions
Define in $options/$arguments arrays:
protected $arguments = [
['name', 'n', 'Required: User name', true],
];
protected $options = [
['--force', 'f', 'Force creation', false, false],
['--role', 'r', 'User role', false, 'admin'],
];
Access parsed values in handle():
$userName = $this->argument('name');
$force = $this->option('force');
Interactive Prompts
Use $this->ask() for user input:
$name = $this->ask('Enter user name', 'John Doe');
$confirm = $this->confirm('Proceed?', false);
Progress Bars For long-running tasks:
$progress = $this->createProgressBar(100);
for ($i = 0; $i <= 100; $i++) {
$progress->advance();
sleep(0.1);
}
$progress->finish();
Output Styling Use methods like:
$this->info() (green)$this->warning() (yellow)$this->error() (red)$this->table() for tabular data.Dependency Injection Pass services via constructor:
class DeployCommand extends Command
{
protected $repository;
public function __construct(UserRepository $repository)
{
$this->repository = $repository;
}
public function handle() {
$this->repository->deploy();
}
}
Laravel Artisan Integration
Extend Illuminate\Console\Command and use adhocore/cli for parsing:
use Adhocore\Cli\Parser;
class CustomCommand extends Command
{
protected function getArguments()
{
return Parser::parseArguments($this->input->arguments());
}
}
Standalone Scripts
For non-Laravel projects, use the Cli facade:
$cli = new \Adhocore\Cli\Cli();
$cli->addCommand(new MyCommand());
$cli->run();
Configuration
Load from .env or config files:
$config = new \Adhocore\Cli\Config('path/to/config.php');
$this->config = $config->get('key');
Argument/Option Naming Conflicts
Avoid overlapping short flags (e.g., -v for --verbose and --version). Use unique prefixes:
['--verbose', 'v', '...'], // OK
['--version', 'V', '...'], // Avoids conflict with -v
Case Sensitivity in Commands
Command names are case-sensitive. Use snake_case consistently:
// Correct:
protected $name = 'app:deploy';
// Avoid:
protected $name = 'App:Deploy'; // Fails!
Subcommand Execution Order Subcommands run in declaration order. Explicitly order critical ones:
$this->addSubcommand(new CriticalTaskCommand());
$this->addSubcommand(new OptionalTaskCommand());
Output Buffering Issues
Ensure STDOUT/STDERR are flushed for real-time progress:
$this->output->flush();
Enable Verbose Mode
Add to handle():
$this->verbose('Debug info: ' . print_r($data, true));
Run with:
php artisan command:name --verbose
Inspect Parsed Input Dump raw input for troubleshooting:
$this->line('Raw arguments: ' . json_encode($this->input->arguments()));
Validation Errors Handle validation failures gracefully:
if ($this->option('required_flag') === null) {
$this->error('Flag --required is mandatory.');
return 1;
}
Reuse Command Logic Extract shared logic into traits or base classes:
trait HandlesDeployment
{
protected function deploy()
{
// Shared deployment logic
}
}
Custom Help Sections
Override getHelp() for structured output:
public function getHelp()
{
return $this->formatHelp(
"Usage: {$this->name} [options]",
[
['--env=ENV', 'Specify environment (default: local)'],
],
"Examples:"
. "\n {$this->name} --env=production"
);
}
Environment Awareness
Use APP_ENV for context-aware commands:
if (app()->environment('local')) {
$this->warning('Running in local mode; dry run enabled.');
}
Testing Commands Mock the CLI system in tests:
$cli = new \Adhocore\Cli\Cli();
$cli->addCommand(new MyCommand());
$cli->run(['command:name', '--option=value']);
Performance Optimization For heavy CLI tools, lazy-load dependencies:
private $repository;
public function getRepository() {
return $this->repository ??= new UserRepository();
}
Cross-Platform Path Handling
Use DIRECTORY_SEPARATOR or realpath() for paths:
$path = realpath($this->argument('path'));
Signal Handling Gracefully handle interrupts (Ctrl+C):
\Adhocore\Cli\Signal::register(function() {
$this->error('Operation cancelled.');
exit(1);
});
How can I help you explore Laravel packages today?