Symfony Console en PHP: crear comandos CLI con input, output y barras de progreso

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 REQUIRED después de un OPTIONAL; PHP lanzará una LogicException al registrar el comando.
  • Progress bar y writeln mezclados: llama a $progressBar->clear() antes de $output->writeln() para evitar solapamientos visuales.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP