El componente Console de Symfony facilita la creación de comandos CLI en PHP con argparse automático, formateo de salida y barras de progreso. Funciona como librería independiente sin necesitar el framework completo.
Instalación
composer require symfony/console
Estructura básica de un comando
<?php
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
class SaludarCommand extends Command
{
protected static $defaultName = 'app:saludar';
protected function configure(): void
{
$this->setDescription('Saluda al usuario por nombre');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('<info>Hola, mundo!</info>');
return Command::SUCCESS;
}
}
?>
Argumentos obligatorios
<?php
use SymfonyComponentConsoleInputInputArgument;
class SaludarCommand extends Command
{
protected static $defaultName = 'app:saludar';
protected function configure(): void
{
$this
->setDescription('Saluda a un usuario')
->addArgument('nombre', InputArgument::REQUIRED, 'Nombre del usuario')
->addArgument('apellido', InputArgument::OPTIONAL, 'Apellido', 'Pérez');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$nombre = $input->getArgument('nombre');
$apellido = $input->getArgument('apellido');
$output->writeln("Hola, $nombre $apellido");
return Command::SUCCESS;
}
}
?>
Uso: php app.php app:saludar Ana García
Opciones con valores por defecto
<?php
use SymfonyComponentConsoleInputInputOption;
protected function configure(): void
{
$this
->addOption('formato', 'f', InputOption::VALUE_REQUIRED, 'Formato salida (json|txt)', 'txt')
->addOption('verbose', 'v', InputOption::VALUE_NONE, 'Mostrar más información')
->addOption('limite', 'l', InputOption::VALUE_OPTIONAL, 'Límite de resultados', 10);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$formato = $input->getOption('formato');
$verbose = $input->getOption('verbose');
$limite = (int) $input->getOption('limite');
if ($verbose) {
$output->writeln("Formato: $formato | Límite: $limite");
}
return Command::SUCCESS;
}
?>
Uso: php app.php app:exportar --formato=json -v --limite=50
SymfonyStyle para formatear la salida
<?php
use SymfonyComponentConsoleStyleSymfonyStyle;
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title('Exportar productos');
$io->section('Leyendo base de datos...');
$io->note('Esta operación puede tardar varios minutos');
$io->success('Exportación completada: 1.250 productos');
$io->warning('3 productos omitidos por precio negativo');
$io->error('Conexión a la BD fallida');
// Tabla
$io->table(
['ID', 'Nombre', 'Precio'],
[
[1, 'Teclado', '49,99 '],
[2, 'Ratón', '29,99 '],
]
);
// Pregunta interactiva
$confirmar = $io->confirm('¿Continuar con la exportación?', true);
if (!$confirmar) {
return Command::SUCCESS;
}
return Command::SUCCESS;
}
?>
Barra de progreso
<?php
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$items = range(1, 500);
$progressBar = $io->createProgressBar(count($items));
$progressBar->setFormat(' %current%/%max% [%bar%] %percent:3s%% | %elapsed:6s%/%estimated:-6s%');
$progressBar->start();
foreach ($items as $item) {
// Simula trabajo
usleep(5000);
$progressBar->advance();
}
$progressBar->finish();
$io->newLine(2);
$io->success('Proceso completado');
return Command::SUCCESS;
}
?>
Aplicación con varios comandos
<?php
// app.php
require 'vendor/autoload.php';
use SymfonyComponentConsoleApplication;
$app = new Application('Mi herramienta CLI', '1.0.0');
$app->add(new SaludarCommand());
$app->add(new ExportarCommand());
$app->run();
?>
Ejecuta php app.php list para ver todos los comandos disponibles con su descripción.
Errores comunes
- Command::FAILURE vs excepción: devuelve
Command::FAILURE(valor 1) para indicar error sin lanzar excepción. Symfony capturará cualquier excepción no controlada y mostrará el stack trace en modo verbose. - Argumentos opcionales antes de obligatorios: no se puede añadir un
REQUIREDdespués de unOPTIONAL; PHP lanzará unaLogicExceptional registrar el comando. - Progress bar y writeln mezclados: llama a
$progressBar->clear()antes de$output->writeln()para evitar solapamientos visuales.
