En este post te voy a mostrar a leer y a parsear los argumentos de la línea de comandos, los mismos que se le pasan a un script de PHP; para ello veremos la variable $argv
y la función getopt
.
Podemos obtener los argumentos a través de su nombre o a través de su índice en un arreglo; la primera opción es la que nos va a interesar más.
PHP puede ejecutarse en la línea de comandos, y así como cualquier otro programa que se ejecuta por comandos, se le pueden pasar argumentos.
Argumentos de la línea de comandos
Como sabemos, los archivos de PHP pueden ejecutarse desde la terminal. La sintaxis es:
php archivo.php
Y a ese script le podemos pasar argumentos. Por ejemplo:
php archivo.php --nombre="luis"
O algo como:
php archivo.php luis
El número de argumentos puede variar, y los mismos se usan para indicar opciones del programa que usamos.
Acceder a los argumentos por su número
Los argumentos están en la variable $argv
en forma de arreglo; y el número de argumentos está en la variable $argc
.
Siempre habrá al menos un argumento incluso si el usuario no lo especifica, y es el nombre del archivo.
Es decir, en $argv[0]
estará el nombre del script. Y para la siguiente ejecución:
php archivo.php
$argv[0]
será igual a archivo.php
.
Si pasamos más argumentos (separados por espacios) los mismos serán colocados en ese orden en el arreglo. Veamos un ejemplo:
<?php
echo "Hay $argc argumentos\n";
foreach ($argv as $argumento) {
echo "Recibido un argumento: $argumento \n";
}
/*
Salida:
Hay 3 argumentos
Recibido un argumento: argumentos.php
Recibido un argumento: Hola
Recibido un argumento: mundo
*/
Así que para validar el número de argumentos podemos usar $argc
. Y para acceder a los argumentos, usar $argv[1]
, $argv[2]
, etcétera.
PHP y getopt
Hay otra manera más bonita de acceder a los argumentos; y digo bonita porque es más cómoda para nosotros y para quien use nuestro script. Aquí lo explico y dejo ejemplos abajo.
Se trata de la función getopt
, la cual devuelve un arreglo de las opciones, dependiendo de las letras que le pasemos.
Es decir, se invoca así:
$argumentos = getopt("a:b::c");
No te preocupes, te lo explico.
La función va a tomar cada letra y la va a tomar como una opción. Pero esa letra tiene 3 significados.
- Si tiene dos puntos junto a ella, significa que el argumento tendrá un valor. Por ejemplo, podría ser:
php archivo.php -a "valor"
- En caso de que tenga dos puntos junto a ella, significa que tendrá un valor opcional. Por ejemplo, podría ser:
php archivo.php -b "otro valor opcional"
- Finalmente, si no tiene nada junto a ella, significa que es una bandera de
sí
yno
, o un booleano. Por ejemplo:php archivo.php -c
, ya que no lleva valor, solo se indica o no su presencia.
Por cierto, esta función permite también los argumentos largos en forma de arreglo (podría ser php archivo.php --nombre "Luis Cabrera"
) como segundo argumento, eso lo veremos abajo.
Para saber si el usuario pasó o no pasó los argumentos, usamos isset o empty; es un trabajo con comparaciones y arreglos.
En resumen, las opciones cortas son con un guión y las largas con dos guiones. Ambas opciones soportan la sintaxis de bandera, con valor, o con valor opcional.
Cabe mencionar que en los argumentos con getopt no podemos acceder al nombre del script, pero sí con $argv
y esa variable siempre está disponible (o al menor en un escenario común)
Ahora veamos algunos ejemplos.
Ejemplo de getopt
Imaginemos un script para enviar un correo electrónico en donde se debe especificar:
- El correo del destinatario
- El asunto
- El mensaje
- Una bandera opcional que indica si el correo debería ser guardado en una base de datos
La forma de invocar al script podría ser así:
# Sin guardar:
php correo.php -d parzibyte@gmail.com -a Ayuda -m "Necesito un poco de ayuda con este script de PHP"
# Guardar
php correo.php -d parzibyte@gmail.com -a Ayuda -m "Necesito un poco de ayuda con este script de PHP" -g
De esta manera podríamos hacer unos fabulosos scripts con PHP y los argumentos de la línea de comandos.
Ahora veamos el script:
<?php
// Recuerda:
// Si tiene : entonces debe tener un valor
// Si tiene :: entonces su valor es opcional
// Si no tiene : ni :: entonces quiere decir que la opción es una bandera, no un valor
// Las opciones son:
// -d destinatario
// -a Asunto del correo
// -m El mensaje del correo
// -g Si se especifica la opción, el correo será guardado
$argumentos = getopt("d:a:m:g");
// Las opciones deben estar establecidas
// Si no, salimos e indicamos el modo de uso
if (
!isset($argumentos["d"])
||
!isset($argumentos["a"])
||
!isset($argumentos["m"])
) {
exit("Modo de uso:
-d destinatario
-a Asunto del correo
-m El mensaje del correo
-g Si se especifica la opción, el correo será guardado");
}
// Hasta aquí todas las opciones están bien establecidas
$destinatario = $argumentos["d"];
$asunto = $argumentos["a"];
$mensaje = $argumentos["m"];
$guardar = isset($argumentos["g"]);
echo "Enviar correo" . PHP_EOL;
echo "Destinatario: $destinatario" . PHP_EOL;
echo "Asunto: $asunto" . PHP_EOL;
echo "Mensaje: $mensaje" . PHP_EOL;
echo "¿Guardar?: " . ($guardar ? "Sí" : "No") . PHP_EOL;
La mayor parte del script es validación. Si un dato obligatorio no está establecido, salimos del script e indicamos su forma de uso.
Como ves, las opciones del destinatario, mensaje y asunto son obligatorias con valor, en cambio, la opción de guardar es una bandera (no nos importa su valor, solo comprobamos si existe o no con isset
).
Para acceder a los datos accedemos a $argumentos["opción"];
La salida es:
Usar getopt con argumentos largos
Como lo dije, getopt
acepta un segundo argumento en forma de arreglo que indica los argumentos que se pueden pasar de forma larga usando dos guiones.
De este modo podemos hacer nuestros scripts más bonitos, pues las opciones serán más expresivas.
Veamos otro ejemplo que usa getopt de PHP para leer ambas opciones:
<?php
// Recuerda:
// Si tiene : entonces debe tener un valor
// Si tiene :: entonces su valor es opcional
// Si no tiene : ni :: entonces quiere decir que la opción es una bandera, no un valor
// Las opciones son:
// -d destinatario
// -a Asunto del correo
// -m El mensaje del correo
// -g Si se especifica la opción, el correo será guardado
$argumentos = getopt("d:a:m:g", array(
"destinatario:",
"asunto:",
"mensaje:",
"guardar",
));
// Las opciones deben estar establecidas
// Si no, salimos e indicamos el modo de uso
if (
!(isset($argumentos["d"]) || isset($argumentos["destinatario"]))
||
!(isset($argumentos["a"]) || isset($argumentos["asunto"]))
||
!(isset($argumentos["m"]) || isset($argumentos["mensaje"]))
) {
exit("Modo de uso:
-d --destinatario Correo del destinatario
-a --asunto Asunto del correo
-m --mensaje El mensaje del correo
-g --guardar Si se especifica la opción, el correo será guardado");
}
// Hasta aquí todas las opciones están bien establecidas
$destinatario = isset($argumentos["d"]) ? $argumentos["d"] : $argumentos["destinatario"];
$asunto = isset($argumentos["a"]) ? $argumentos["a"] : $argumentos["asunto"];
$mensaje = isset($argumentos["m"]) ? $argumentos["m"] : $argumentos["mensaje"];
$guardar = isset($argumentos["g"]) || isset($argumentos["guardar"]);
echo "Enviar correo" . PHP_EOL;
echo "Destinatario: $destinatario" . PHP_EOL;
echo "Asunto: $asunto" . PHP_EOL;
echo "Mensaje: $mensaje" . PHP_EOL;
echo "¿Guardar?: " . ($guardar ? "Sí" : "No") . PHP_EOL;
Ahora estamos tomando ambas opciones o tipos de argumentos (si no existe la versión corta, buscamos la larga). Esta manera es más específica y amigable con el usuario final.
La ejecución ahora puede ser usando la opción corta como -d
o --destinatario
, y lo mismo para los otros argumentos.
php correo2.php --destinatario parzibyte@gmail.com -a Ayuda -m "Necesito ayuda con esta app" --guardar
php correo2.php --destinatario=parzibyte@gmail.com -a Ayuda -m "Necesito ayuda con esta app" --guardar
php correo2.php --destinatario=parzibyte@gmail.com --asunto "Ayuda JS" -m "Necesito ayuda con esta app" --guardar
Al llamarlo se ve la siguiente salida:
Como puedes ver, los argumentos largos pueden ser pasados con un signo de igual, separándolos con un espacio y también encerrando el argumento entre comillas, en caso de que este lleve espacios.