Command
The Command class is the base class for all CLI commands in Yalla.
Class Definition
namespace Yalla\Commands;
abstract class Command
{
protected string $name;
protected string $description;
protected array $arguments = [];
protected array $options = [];
abstract public function execute(array $input, Output $output): int;
}Properties
$name
protected string $nameThe name of the command used to invoke it from the CLI.
$description
protected string $descriptionA brief description of what the command does, shown in help and list outputs.
$arguments
protected array $arguments = []Array of registered arguments for the command.
$options
protected array $options = []Array of registered options for the command.
Abstract Methods
execute()
abstract public function execute(array $input, Output $output): intThe main execution method that must be implemented by all commands.
Parameters
$input(array): Parsed input containing command, arguments, and options$output(Output): Output instance for displaying results
Returns
int: Exit code (0 for success, non-zero for error)
Public Methods
getName()
public function getName(): stringReturns the command name.
getDescription()
public function getDescription(): stringReturns the command description.
getArguments()
public function getArguments(): arrayReturns all registered arguments.
getOptions()
public function getOptions(): arrayReturns all registered options.
Protected Methods
addArgument()
protected function addArgument(
string $name,
string $description,
bool $required = false
): selfAdds an argument to the command.
Parameters
$name(string): The argument name$description(string): Description of the argument$required(bool): Whether the argument is required
Returns
self: For method chaining
Example
$this->addArgument('file', 'Input file path', true);
$this->addArgument('output', 'Output file path', false);addOption()
protected function addOption(
string $name,
?string $shortcut,
string $description,
$default = null
): selfAdds an option to the command.
Parameters
$name(string): The option name (long format)$shortcut(?string): Single character shortcut (can be null)$description(string): Description of the option$default(mixed): Default value if option is not provided
Returns
self: For method chaining
Example
$this->addOption('force', 'f', 'Force overwrite', false);
$this->addOption('verbose', 'v', 'Verbose output', false);
$this->addOption('timeout', 't', 'Timeout in seconds', 30);getArgument()
protected function getArgument(
array $input,
string $name,
$default = null
)Gets an argument value from the input.
Parameters
$input(array): The parsed input array$name(string): The argument name or index$default(mixed): Default value if argument is not found
Returns
mixed: The argument value or default
Example
$file = $this->getArgument($input, 'file');
$output = $this->getArgument($input, 'output', 'output.txt');getOption()
protected function getOption(
array $input,
string $name,
$default = null
)Gets an option value from the input.
Parameters
$input(array): The parsed input array$name(string): The option name$default(mixed): Default value if option is not found
Returns
mixed: The option value or default
Example
$force = $this->getOption($input, 'force', false);
$timeout = $this->getOption($input, 'timeout', 30);Creating a Custom Command
Basic Example
<?php
use Yalla\Commands\Command;
use Yalla\Output\Output;
class DeployCommand extends Command
{
public function __construct()
{
$this->name = 'deploy';
$this->description = 'Deploy application to server';
$this->addArgument('environment', 'Target environment', true);
$this->addOption('branch', 'b', 'Git branch to deploy', 'main');
$this->addOption('force', 'f', 'Force deployment', false);
}
public function execute(array $input, Output $output): int
{
$env = $this->getArgument($input, 'environment');
$branch = $this->getOption($input, 'branch', 'main');
$force = $this->getOption($input, 'force', false);
$output->info("Deploying branch '$branch' to $env");
if ($force) {
$output->warning('Force mode enabled');
}
// Deployment logic here...
$output->success('Deployment completed successfully!');
return 0;
}
}Advanced Example
class BackupCommand extends Command
{
public function __construct()
{
$this->name = 'backup';
$this->description = 'Create a backup of the database';
// Multiple arguments
$this->addArgument('database', 'Database name', true);
$this->addArgument('destination', 'Backup destination', false);
// Various option types
$this->addOption('compress', 'c', 'Compress backup', false);
$this->addOption('format', 'f', 'Backup format (sql|json)', 'sql');
$this->addOption('exclude', 'e', 'Tables to exclude', []);
$this->addOption('quiet', 'q', 'Suppress output', false);
}
public function execute(array $input, Output $output): int
{
$database = $this->getArgument($input, 'database');
$destination = $this->getArgument($input, 'destination', "./backups/{$database}.sql");
// Validate format
$format = $this->getOption($input, 'format', 'sql');
if (!in_array($format, ['sql', 'json'])) {
$output->error("Invalid format: $format");
return 1;
}
// Check if destination exists
$dir = dirname($destination);
if (!is_dir($dir)) {
$output->error("Directory does not exist: $dir");
return 1;
}
$quiet = $this->getOption($input, 'quiet', false);
if (!$quiet) {
$output->info("Starting backup of database: $database");
}
try {
// Perform backup
$this->performBackup($database, $destination, $format);
if (!$quiet) {
$output->success("Backup saved to: $destination");
}
return 0;
} catch (\Exception $e) {
$output->error('Backup failed: ' . $e->getMessage());
return 1;
}
}
private function performBackup(string $db, string $dest, string $format): void
{
// Backup implementation
}
}Input Structure
The $input array passed to execute() has this structure:
[
'command' => 'deploy',
'arguments' => ['production', 'v2.0'],
'options' => [
'force' => true,
'branch' => 'release',
'f' => true, // Shortcut also included
'b' => 'release' // Shortcut also included
]
]Exit Codes
Commands should return appropriate exit codes:
0: Success1: General error2: Misuse of command126: Command cannot execute127: Command not found130: Script terminated by Ctrl+C
Best Practices
1. Constructor Setup
Always define name, description, arguments, and options in the constructor:
public function __construct()
{
$this->name = 'command:name';
$this->description = 'Clear and helpful description';
// Define all arguments and options here
}2. Input Validation
Validate input early in the execute method:
public function execute(array $input, Output $output): int
{
$file = $this->getArgument($input, 'file');
if (!$file) {
$output->error('File argument is required');
return 1;
}
if (!file_exists($file)) {
$output->error("File not found: $file");
return 1;
}
// Continue with valid input...
}3. Error Handling
Always handle exceptions and provide helpful error messages:
try {
$this->riskyOperation();
} catch (\Exception $e) {
$output->error('Operation failed: ' . $e->getMessage());
return 1;
}4. Progress Feedback
For long-running operations, provide progress feedback:
$items = $this->getItems();
$total = count($items);
foreach ($items as $i => $item) {
$output->progressBar($i + 1, $total);
$this->processItem($item);
}Testing Commands
test('command executes successfully', function () {
$command = new MyCommand();
$input = [
'command' => 'my:command',
'arguments' => ['arg1'],
'options' => ['opt' => 'value']
];
$output = Mockery::mock(Output::class);
$output->shouldReceive('success')->once();
$result = $command->execute($input, $output);
expect($result)->toBe(0);
});See Also
- Application - Main application class
- Output - Output formatting
- CommandRegistry - Command registration