symfony/console
Symfony Console component for building beautiful, testable CLI apps in PHP. Define commands and arguments, render styled output, handle prompts and hidden input, and integrate easily into frameworks or standalone scripts with strong tooling and docs.
Installation:
composer require symfony/console:^8.1
Laravel 10+ includes Symfony Console 8.1+ by default. For compatibility, ensure your composer.json aligns with Laravel’s dependencies.
First Command:
php artisan make:command SendEmail
This generates a scaffold with:
handle(): Core logic execution.configure(): Defines CLI signature (arguments/options).Run It:
php artisan send:email
Key Files to Explore:
app/Console/Kernel.php: Command registration.vendor/symfony/console/: Core classes (Command, Input, Output).artisan CLI (extends symfony/console/Application).symfony/polyfill-php85 for PHP 8.5+ fixes and hardened error handling (see #64246).Replace a Bash script with a Laravel command to send emails:
// app/Console/Commands/SendEmail.php
protected $signature = 'send:email {recipient} {--subject=Hello} {--body=Default body}';
protected $description = 'Send an email to a user';
public function handle()
{
$this->info("Sending email to {$this->argument('recipient')}...");
// Logic here (e.g., Mail::send())
$this->success('Email sent!');
}
Run with:
php artisan send:email user@example.com --subject="Welcome" --body="Thanks!"
configure():
$this->argument('id', 'The user ID')
->option('force', 'Bypass confirmation', 'f');
Validator or custom logic:
if (!$this->option('force') && !$this->confirm('Proceed?')) {
return;
}
Output methods:
$this->text('Normal text');
$this->line('<info>Colored text</info>');
$this->table(['Headers'], $data);
VERBOSITY_* constants:
if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) {
$this->debug('Debug info');
}
$name = $this->ask('Your name?');
$confirm = $this->confirm('Continue?', false);
$choice = $this->choice('Pick one', ['A', 'B'], 'A');
$password = $this->secret('Password?');
$progress = $this->output->createProgressBar($total);
for ($i = 0; $i < $total; $i++) {
$progress->advance();
}
$progress->finish();
$indicator = $this->output->createProgressIndicator('Processing...');
$indicator->start();
sleep(2);
$indicator->finish();
Fix in v8.1.0-BETA3: Hardened ProgressIndicator to prevent race conditions during concurrent output (see #64246).$user = $this->container->get('App\Models\User')->find($id);
handle():
event(new EmailSent($user));
$command = new SendEmail();
$command->setLaravel($app);
$command->handle();
$this->artisan('send:email', ['recipient' => 'test@example.com'])
->expectsQuestion('Proceed?', 'yes')
->assertExitCode(0);
php artisan user:create).$this->dispatch(new SendEmailJob($email));
SymfonyStyle for reusable UI:
$style = new SymfonyStyle($this->input, $this->output);
$style->title('My Tool');
Argument Parsing Quirks:
-- to bypass argument parsing:
php artisan --hidden command:name
-f vs --force).Output Corruption:
\n consistently.OUTPUT_RAW mode:
$this->output->write($binaryData, false, OutputInterface::OUTPUT_RAW);
ConsoleSectionOutput now handles concurrent writes more robustly (see #64246).Signal Handling:
SIGINT (Ctrl+C) gracefully:
$this->output->getErrorOutput()->write("\nExiting...\n");
exit(0);
Testing Gotchas:
$tester->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
$this->artisan('command')->expectsQuestion('?', 'answer');
Performance:
advance() calls in loops.$this->output->setDecorated(false);
php artisan --env=local --verbose command
$this->output->writeln(sprintf('<comment>Input:</comment> %s', print_r($this->input->getArguments(), true)));
getApplication() in favor of dependency injection.OutputFormatter for JSON/CSV output:
$formatter = new JsonOutputFormatter();
$this->output->setFormatter($formatter);
abstract class BaseCommand extends Command {
protected function log($message) { /* ... */ }
}
Shell for cross-platform commands:
$shell = new Shell();
$shell->execute('ls -la');
Artisan for global options in app/Console/Kernel.php:
protected $commands = [Commands\SendEmail::class];
$this->app->bind('command.send.email', function () {
return new SendEmail($mailer);
});
schedule() in handle() for delayed execution:
$this->dispatch(new SendEmailJob($email));
symfony/polyfill-php85 is installed.How can I help you explore Laravel packages today?