Tras bastante tiempo de estancia en el canal #php_para_torpes del iRC-Hispano (www.php-hispano.net) y tras haber ojeado código de mucha gente, una cosa de las que me molesta es el mal uso de los bucles (y del explode). Con este documento trato de explicar el funcionamiento de los diferentes bucles, ademas de e que situación usar uno o otro.
Este documento ha sido realizado por Iván Arias para #php_para_torpes del iRC-Hispano y www.php-hispano.net. Si encontraste este documento en otro lado seguramente sean en la página de un lamer. Si tienes alguna duda/sugerencia/corrección o cualquier cosa, puedes contactar conmigo en el chat (mi nick es {Arias}) o enviarme un mail a arias@elleondeoro.com
Los bucles son estructuras de control del flujo del programa que nos premiten ejecutar un fragmento de código varias veces. Por ejemplo para aplicar una función a cada elemento de un array (si descontamos el uso de las funciones de tratamiento de arrays), o leer de un Resource mientras queden elementos.
El bucle while es quizas el más usado por la gente, aunque la mayoría de las veces veces su uso no es el adecuado. La estructura del bucle es esta:
while (condicion) Bloque de codigo
El bloque de codigo se ejecutará mientras la condición se cumpla y puede ser una instrucción, o una serie de instrucciones entre llaves. La condición es una expresión booleana, si se evalua true el bucle continuará su ejecucion hasta que la expresión cambie y de false. De no cambiar nunca el bucle se ejecutaría infinitamente.
PHP, a la hora de evaluar una condición, si esta se trata de una variable, evaluará siempre a true salvo que tenga el valor null, 0, false o la variable sea un array vacio.
El bucle while evalua la condición antes de entrar, si la condición fuera false en un principio, el código no llegaría a ejecutarse. Algún ejemplo de uso del bucle while seria:
Eliminar los espacios al principio de una cadena:
<?php
$cadena = " hola";
while ($cadena{0} == " ") // Se ejecutará mientras el primer caracter de la cadena sea un espacio
$cadena = substr($cadena, 1); // Eliminamos el primer caracter de la cadena
?>
Meter en un array los archivos de un directorio:
<?php
$dir = opendir("directorio"); // Abrimos el directorio
$archivos = array();
while ($file = readdir($dir)) // Mientras se lean archivos...
if (is_file("directorio/".$file)) // ...y sean archivos...
$archivos[] = $file; // ...los metemos en el array
?>
Puede ser algo confusa la condición del úlimo ejemplo. Cuando evaluas una asignación, la variable toma el valor requerido y se evalua esta variable como vimos anteriormente. Es decir, $file tomaria el valor retornado por readdir y php evaluaría el valor de esta variable.
Esta manera de leer del directorio no es la mas correcta, como vimos anteriormente PHP evalua a false varias cosas, si tuvieramos por ejemplo un directorio llamado "0" el bucle se pararía. La solución a este problema es facil, sólo queremos que pare el bucle cuando $file tome el valor false, por lo que tenemos que comparar el valor obtenido con false. La manera correcta de hacer esto sería:
<?php
$dir = opendir("directorio"); // Abrimos el directorio
$archivos = array();
while (($file = readdir($dir)) !== false) // Mientras se lean archivos...
if (is_file("directorio/".$file)) // ...y sean archivos...
$archivos[] = $file; // ...los metemos en el array
?>
Este bucle sólo se diferencia del while en una pequeña cosa, el while evalua la condición al principio y puede llegar a no ejecutarse; el do/while evalua la condición despues de ejecutar el código, con lo cual el código al menos se ejecutará una vez. La estructura del bucle es la siguente:
do Bloque de código while (Condicion);
Al igual que en el while el Bloque de código puede ser una instrucción o varias metidas entre llaves, y se ejecutará mientras la condición sea cierta. Un ejemplo de uso puede ser:
Leer ordenes de la consola
<?php
$consola = fopen("php://stdin", "r"); // Abrimos para leer de la consola de php
do { // Inicio del bucle
echo "Introcude una accion:\n";
$read = trim(fread($consola, 512));
} while ($read != "salir"); // Repetimos mientras no se lea la orden de salir
echo "Saliendo...";
fclose($consola);
?>
Un ejemplo de uso incorrecto de un bucle while sería:
<?php
$consola = fopen("php://stdin", "r"); // Abrimos para leer de la consola de php
$read = "";
while ($read != "salir") {
echo "Introcude una accion:\n";
$read = trim(fread($consola, 512));
}
echo "Saliendo...";
fclose($consola);
?>
<?php
$consola = fopen("php://stdin", "r"); // Abrimos para leer de la consola de php
echo "Introcude una accion:\n";
$read = trim(fread($consola, 512));
while ($read != "salir") {
echo "Introcude una accion:\n";
$read = trim(fread($consola, 512));
}
echo "Saliendo...";
fclose($consola);
?>
Siempre que tengamos un bucle while, y tengamos que dar un valor a una variable de tal manera que la condición del while evalue a true, o poner el codigo del bucle fuera para que se ejecute una vez, lo más seguro es que necesitemos usar do/while en vez de while.
Este es mi bucle preferido. La mayoría de la gente piensa que este bucle sólo sirve para hacer algo un determinado número de veces, pero se equivoca. El comportamiento de este bucle es identico al del bucle while, la diferencia reside en que el bucle for además se puede usar un contador. La estructura del bucle es la siguiente:
for (Expresión inicial; Condición; Expresión incremental) Bloque de codigo
Antes de ejecutar el bucle, se ejecuta la expresión inicial, en la cual podemos dar valor inicial a una variable, resetear un array, abrir un recurso... A continuación se evalua la condición de la misma manera que en los demás bucles, de ser cierta se ejecuta el bloque de código, y por último se ejecuta la expresión incremental.
Así teóricamente suena muy complicado, pero en la práctica es facil de usar. Veamos unos ejemplos aclaratorios:
Crear un array con 10 numeros aleatorios
<?php
for ($i=0, $numeros = array(); $i<10; $i++) { // El bucle se ejecutaria 10 veces
$numeros[$i] = rand(); // $i tomará valores desde 0 hasta 9, ambos inclusive
}
?>
Mostrar un array por pantalla
<?php
for ($i=0; $i<count($array); $i++) // El bucle se ejecuta tantas veces como posiciones tenga el array
echo $array[$i]."\n"; // Con el contador accedemos a la posición del array correspondiente
?>
Otro metodo de mostrar un array, si el array tubiese algun valor que PHP evaluase a false el bucle pararía
<?php
for (reset($array); $valor=current($array); next($array)) // Reiniciamos el array, almacenamos el valor actual en $valor y avanzamos a la siguiente posición
echo $valor."\n";
?>
Asi arreglamos el problema anterior
<?php
for (reset($array); list(,$valor)=each($array); ) // No necesitamos expresion incremental dado que each ya avanza en el array
echo $valor."\n";
?>
Leer el contenido de un archivo a un string
<?php
$string = "";
for ($archivo=fopen("archivo.txt", "r"); !feof($archivo); $string .= fread($archivo, 512));
fclose($archivo);
?>
Como podemos observar, el bucle for es el más versatil de todos. Cuando tengamos necesidad de usa un bucle while con un contador, lo mas seguro es que necesitemos usar un bucle for.
El bucle foreach es un bucle propio de php utilizado para recorrer arrays de una manera más facil. La construcción de este bucle admite dos variantes:
foreach(array as valor) Bloque de código foreach(array as clave => valor) Bloque de código
Este bucle ya es bastante diferente a los demás, tanto array, como clave y valor son variables. En cada iteración del bucle, clave y valor cambiarán de valor, clave contendrá la clave de la posición actual del array y valor contendrá su valor.
Veamos algun ejemplo practico:
Mostrar un array por pantalla, podemos observar que el código es más sencillo que si usasemos un bucle for
<?php
foreach($array as $valor)
echo $valor."<br>";
?>
Si ademas qusiesemos mostrar la posición en la que se encuentra
<?php
foreach($array as $clave => $valor)
echo "[".$clave."] ".$valor."\n";
?>
Los equivalentes a estas construcciones con un bucle for serían:
<?php
for (reset($array); list(, $valor) = each($array); )
echo $valor."\n";
?>
y
<?php
for (reset($array); list($clave, $valor) = each($array); )
echo "[".$clave."] ".$valor."\n";
?>
respectivamente
Break y continue son palabras reservadas que sirven para alterar la ejecución de un bucle.
Break sirve para cortar la ejecución de un bucle, tras ejecutar esta sentecia el bucle finalizará su ejecución y se ejecutarán las instrucciones a continuación del bucle. Desde un bucle interno podemos interrumpir la ejecución de bucles anidados externos, esdecir, si tubieramos dos bucles for anidados, usando "break 2;" desde el bucle interno, detendriamos la ejecución de ambos bucles.
Continue sólo corta la iteración actual del bucle, pero no detiene la ejecución del mismo, cuando usemos este instrucción, el bucle dejara de ejecutarse con el valor actual y empezará a ejecutarse con el siguiente valor (si lo hubiere). De la misma manera que break, continue puede usarse desde bucles anidados para interrupir la ejecución de bucles más externos.
Algun ejemplo de uso puede ser:
Buscar una posicion de un array
<?php
for ($i=0; $i<count($array); $i++) // Recorremos el array
if ($array[$i] == $busqueda) // Si encontramos el elemento...
break; // Paramos el array y $i tendrá la posición del elemento encontrado
if ($i < count($array)) // Comprobamos que haya encontrado algo
echo "Elemento encontrado en la posición: ".$i;
else
echo "Elemento no encontrado";
?>
Imprimir los número pares menores de 100, una manera cutre pero que sirva de ejemplo :P
<?php
for ($i=0; $i<100; $i++) {
if ($i%2 != 0)
continue;
echo $i."\n";
}
?>
Un bucle infinito no es ningun tipo especial de bucle, simplemente es un bucle cuya condición nunca se hace false, con lo que su ejecución es (teóricamente) infinita. Suelen ser frutos de un bucle mal construido.
Algún ejemplo de bucle infinito puede ser
<?php
while (true)
echo "Soy un bucle infinito\n";
for (;;)
echo "Yo también\n";
?>
Bucle infinito?
<?php
for ($i=0; $i<1000; $i=rand(0, 1000))
echo "Soy un bucle, puedo ejecutarme muchas veces pero por estadística algún día dejaré de ejecutarme... Aunque puede que no :P\n";
?>
Hacer un bucle infinito, y luego detener su ejecución con un break suele ser una chapuza, si necesitas hacer esto seguramente la condicion del break sea lo que necesitas para construir la condición del bucle.
Me gustaría repasar las conclusiones obtenidas, sobre todo para esa gente que usa el while para todo: