Installation
composer require drjele/symfony-command
Add the package to your composer.json under require or require-dev depending on your use case.
Basic Usage
Create a new command by extending DrJele\Command\Command (or DrJele\Command\ConsoleCommand for Symfony Console integration):
use DrJele\Command\Command;
class MyCommand extends Command
{
protected function configure(): void
{
$this->setName('app:my-command')
->setDescription('A simple command example');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('Hello from MyCommand!');
return Command::SUCCESS;
}
}
Registering the Command
For Symfony, register the command in config/services.yaml:
services:
App\Command\MyCommand:
tags: ['console.command']
For Laravel, bind the command in a service provider:
$this->app->bind(Command::class, function ($app) {
return new MyCommand();
});
First Use Case Run the command via CLI:
php bin/console app:my-command
Command Lifecycle
configure(): Define command metadata (name, description, arguments, options).execute(): Core logic. Use $input for arguments/options, $output for I/O.initialize(): Pre-execution setup (e.g., dependency injection).interact(): For interactive prompts (e.g., confirmation dialogs).Argument/Option Handling
Use Symfony’s Argument and Option classes for validation:
protected function configure(): void
{
$this->addArgument('name', InputArgument::REQUIRED, 'Your name')
->addOption('verbose', 'v', InputOption::VALUE_NONE, 'Enable verbose mode');
}
Dependency Injection
Inject services via constructor or initialize():
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
Output Formatting
Leverage $output methods:
$output->writeln('<info>Info message</info>');
$output->section('Section Title');
$output->table(['Header'], [['Data']]);
Subcommands Group related commands under a parent:
// Parent command
$this->addCommand(new SubCommand1());
$this->addCommand(new SubCommand2());
Event Listeners
Attach listeners to command events (e.g., CommandEvent::START, CommandEvent::END):
$this->getDispatcher()->addListener(CommandEvent::START, function (CommandEvent $event) {
// Pre-execution logic
});
Progress Bars
Use Symfony’s ProgressBar for long-running tasks:
$progress = new ProgressBar($output, 100);
for ($i = 0; $i < 100; $i++) {
$progress->advance();
}
Laravel Integration
Artisan::call() to trigger commands programmatically:
$exitCode = Artisan::call('app:my-command', ['name' => 'John']);
Illuminate\Console\Command and merge with DrJele\Command\Command for hybrid usage.Symfony Integration
DrJele\Command\ConsoleCommand for Symfony’s Command base class.ContainerAwareCommand for DI containers.Testing Commands
Mock InputInterface and OutputInterface:
$input = $this->createMock(InputInterface::class);
$output = $this->createMock(OutputInterface::class);
$command = new MyCommand();
$command->run($input, $output);
Reusable Command Logic Extract shared logic into traits or base classes:
trait HandlesUsers
{
public function getUser(string $id): User
{
// Shared logic
}
}
Output Buffering
OutputInterface buffers output by default. Use $output->flush() to force immediate display:
$output->writeln('Processing...');
$output->flush();
Argument/Option Validation
execute() to avoid silent failures:
if (!$input->getArgument('name')) {
throw new \RuntimeException('Name is required.');
}
Dependency Injection in execute()
execute(). Use constructor injection or initialize().Command Naming Conflicts
Symfony vs. Laravel Quirks
tags: ['console.command'].app/Console/Kernel.php.Progress Bar Interruption
try-catch blocks to handle interruptions:
try {
$progress->finish();
} catch (\Exception $e) {
$progress->setMessage('Failed: ' . $e->getMessage());
}
Verbose Output Enable verbose mode to debug:
php bin/console app:my-command -vvv
Logging
Use Symfony’s LoggerInterface for structured logs:
$this->logger->debug('Debug message', ['context' => 'key' => 'value']);
Command Events Debug event listeners by dumping the event object:
$this->getDispatcher()->addListener(CommandEvent::START, function (CommandEvent $event) {
var_dump($event->getCommand()->getName());
});
Custom Command Events Dispatch custom events for extensibility:
$this->getDispatcher()->dispatch(new MyCustomEvent($input, $output));
Command Helpers
Create helper classes for shared functionality (e.g., CommandHelper::validateEmail()).
Interactive Prompts
Use Symfony’s QuestionHelper for advanced interactions:
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('Continue? [y/N] ', false);
if (!$helper->ask($input, $output, $question)) {
return Command::FAILURE;
}
Command Metadata Extend metadata with custom attributes:
$this->setDescription('Command with custom metadata')
->setAttribute('category', 'utils');
Command Groups Organize commands into logical groups for better CLI navigation:
php bin/console list app
How can I help you explore Laravel packages today?