PHP no es solo un lenguaje de servidor web. Desde la línea de comandos, el intérprete php ejecuta scripts que pueden leer argumentos, procesar datos de STDIN, escribir en STDOUT y STDERR y comunicarse con el sistema operativo. Los scripts CLI son perfectos para tareas automatizadas, importaciones de datos, crons y herramientas de administración.
$argv y $argc: argumentos del script
$argv es un array con los argumentos pasados al script. $argv[0] siempre es el nombre del script. $argc es el número total de argumentos, incluido el nombre del script.
<?php
// Ejecutar: php saluda.php Ana 30
// $argv = ['saluda.php', 'Ana', '30']
// $argc = 3
if ($argc < 3) {
fwrite(STDERR, "Uso: php saluda.php <nombre> <edad>n");
exit(1); // código de salida distinto de 0 indica error
}
$nombre = $argv[1];
$edad = (int) $argv[2];
if ($edad < 0 || $edad > 150) {
fwrite(STDERR, "Edad no válida: $edadn");
exit(2);
}
echo "Hola, $nombre. Tienes $edad años.n";
exit(0); // éxito
?>
Leer STDIN con fgets()
STDIN es un flujo de lectura predefinido que conecta el script con la entrada estándar. Permite leer datos que el usuario escribe o que se pasan por pipe desde otro comando.
<?php
// Ejecutar: echo "Hola mundo" | php mayusculas.php
// O interactivo: php mayusculas.php (escribe y pulsa Ctrl+D)
// Leer línea a línea hasta EOF
while (($linea = fgets(STDIN)) !== false) {
echo strtoupper(trim($linea)) . "n";
}
// Leer todo STDIN de golpe (útil para JSON)
// $json = stream_get_contents(STDIN);
// $datos = json_decode($json, true);
// Pedir confirmación al usuario
echo "¿Continuar? (s/n): ";
$respuesta = trim(fgets(STDIN));
if (strtolower($respuesta) !== 's') {
echo "Cancelado.n";
exit(0);
}
echo "Continuando...n";
?>
getopt(): opciones con flags
getopt() procesa argumentos con formato Unix. Los argumentos cortos son letras con : si requieren valor. Los largos son palabras con :: si el valor es opcional.
<?php
// Ejecutar: php exportar.php -f csv --limite=100 --verbose
$opciones = getopt('f:v', ['formato:', 'limite::', 'verbose', 'ayuda']);
// -f necesita valor (-f csv)
// -v es un flag sin valor
// --formato= necesita valor (--formato=json)
// --limite= es opcional (--limite o --limite=50)
// --verbose y --ayuda son flags
if (isset($opciones['ayuda'])) {
echo "Uso: php exportar.php -f <formato> [--limite=N] [--verbose]n";
exit(0);
}
$formato = $opciones['f'] ?? $opciones['formato'] ?? 'csv';
$limite = isset($opciones['limite']) ? (int) ($opciones['limite'] ?: 100) : 100;
$verbose = isset($opciones['verbose']) || isset($opciones['v']);
if ($verbose) {
echo "Exportando en formato $formato, límite $limite registros.n";
}
?>
stream_isatty(): detectar si la salida va a una terminal
stream_isatty() permite detectar si la salida va a una terminal o está siendo redirigida a un fichero o pipe. Esto es útil para mostrar colores ANSI solo cuando la salida es una terminal interactiva.
<?php
function enTerminal(): bool
{
return function_exists('stream_isatty') && stream_isatty(STDOUT);
}
function colorear(string $texto, string $color): string
{
if (!enTerminal()) {
return $texto; // sin colores si la salida va a fichero o pipe
}
$codigos = ['rojo' => '31', 'verde' => '32', 'amarillo' => '33', 'azul' => '34'];
$codigo = $codigos[$color] ?? '0';
return "e[{$codigo}m{$texto}e[0m";
}
function ok(string $msg): void { echo colorear("? $msg", 'verde') . "n"; }
function error(string $msg): void { fwrite(STDERR, colorear("? $msg", 'rojo') . "n"); }
function info(string $msg): void { echo colorear("? $msg", 'azul') . "n"; }
info('Iniciando proceso...');
ok('Conexión a base de datos establecida.');
error('No se pudo enviar el email de confirmación.');
?>
Separar STDOUT de STDERR
<?php
// STDOUT: resultado normal del script (puede redirigirse a fichero)
// STDERR: mensajes de error y diagnóstico (normalmente visible en terminal)
// Buena práctica: mensajes de progreso a STDERR, datos a STDOUT
// Así el usuario puede hacer: php generar.php > datos.csv 2> errores.log
$procesados = 0;
$errores = 0;
foreach ($registros as $registro) {
try {
$csv = procesarRegistro($registro);
fwrite(STDOUT, $csv . "n"); // dato útil a STDOUT
$procesados++;
} catch (Exception $e) {
fwrite(STDERR, "Error en registro #{$registro['id']}: " . $e->getMessage() . "n");
$errores++;
}
}
fwrite(STDERR, "Proceso completado: $procesados OK, $errores errores.n");
exit($errores > 0 ? 1 : 0);
?>
La documentación oficial sobre PHP en línea de comandos incluye la lista completa de opciones del intérprete, el uso de shebangs para scripts ejecutables directamente, el servidor HTTP de desarrollo integrado y las diferencias de comportamiento entre modo CLI y modo web.
