Argumentos de la línea de órdenes en PHP: $argv y getopt

Argumentos en PHP: PHP no es solo para la web. Los programas de terminal, las tareas programadas y los guiones de mantenimiento reciben argumentos, y PHP trae lo necesario para leerlos bien: $argv y getopt.

Lo básico: $argv

<?php
declare(strict_types=1);

echo "Programa: $argv[0]\n";
echo "Argumentos: ", $argc - 1, "\n";
foreach (array_slice($argv, 1) as $i => $arg) {
    echo "  ", $i + 1, ": $arg\n";
}
php argv.php uno 'dos con espacios' --tres=3Programa: argv.php
Argumentos: 3
  1: uno
  2: dos con espacios
  3: --tres=3

$argv es un array con el nombre del programa y cada argumento, tal como los separó el shell: lo que va entre comillas llega como un solo argumento. $argc es cuántos hay. Solo existen al ejecutar PHP en la terminal, no en una página web.

Opciones con getopt

Para opciones como -f csv o --formato=csv, PHP trae getopt. Este es un programa completo, con ayuda, valores por omisión y errores:

<?php
declare(strict_types=1);

const AYUDA = <<<TXT
Uso: php convertir.php [opciones] <archivo>

  -f, --formato=FORMATO   json o csv (por omisión, json)
  -v, --verbose           más detalles
  -h, --help              esta ayuda

TXT;

$opciones = getopt('f:vh', ['formato:', 'verbose', 'help'], $resto);

if (isset($opciones['h']) || isset($opciones['help'])) {
    fwrite(STDOUT, AYUDA);
    exit(0);
}

$formato = $opciones['formato'] ?? $opciones['f'] ?? 'json';
$verbose = isset($opciones['v']) || isset($opciones['verbose']);
$archivos = array_slice($argv, $resto);

if (!in_array($formato, ['json', 'csv'], true)) {
    fwrite(STDERR, "Formato desconocido: $formato\n");
    exit(2);
}
if ($archivos === []) {
    fwrite(STDERR, "Falta el archivo. Prueba con --help\n");
    exit(2);
}

foreach ($archivos as $archivo) {
    echo "Convertiría $archivo a $formato", $verbose ? " (modo detallado)" : "", "\n";
}
exit(0);

Así responde a distintas llamadas:

php convertir.php --help   (código de salida 0)Uso: php convertir.php [opciones] <archivo>

  -f, --formato=FORMATO   json o csv (por omisión, json)
  -v, --verbose           más detalles
  -h, --help              esta ayuda
php convertir.php -f csv datos.txt   (código de salida 0)Convertiría datos.txt a csv
php convertir.php --formato=json -v a.txt b.txt   (código de salida 0)Convertiría a.txt a json (modo detallado)
Convertiría b.txt a json (modo detallado)
php convertir.php --formato=xml datos.txt   (código de salida 2)Formato desconocido: xml
php convertir.php -v   (código de salida 2)Falta el archivo. Prueba con --help

El primer argumento de getopt describe las opciones cortas: f: lleva un valor obligatorio, v y h no. El segundo, las largas, con la misma regla de los dos puntos. Las opciones sin valor aparecen en el array con valor false: por eso se comprueban con isset. El tercer argumento, $resto, recibe la posición donde terminan las opciones, y desde ahí $argv tiene los archivos.

Salida, errores y códigos de salida

Tres costumbres de los programas de terminal que PHP permite seguir:

  • Los resultados, por STDOUT; los errores, por STDERR con fwrite(STDERR, ...). Así, quien redirige la salida a un archivo (php convertir.php > salida.json) sigue viendo los errores en pantalla.
  • Un código de salida con exit(): 0 si todo fue bien, otro número si no. Los guiones de shell, cron y la integración continua lo usan para saber si algo falló. Un error fatal de PHP sale con 255.
  • Una ayuda con -h y --help, y un mensaje que la mencione cuando el uso es incorrecto.
Programas más grandes. Para herramientas con subórdenes, colores y preguntas al usuario, el componente symfony/console (con Composer) lo hace casi todo. Es el que usan Composer y Laravel para sus propias órdenes.

Resumen

ParaUsa
Todos los argumentos$argv y $argc
Opciones con nombregetopt('f:vh', ['formato:', 'verbose', 'help'], $resto)
Lo que queda tras las opcionesarray_slice($argv, $resto)
Avisar de un errorfwrite(STDERR, ...)
Terminar con un códigoexit(0) o exit(2)

Todo el código de esta página se ejecutó con PHP 8.5.12 en un contenedor limpio, sin acceso a la red, antes de publicarla; las salidas y los mensajes de error están copiados de esa ejecución.