Tema
Bucles
Los bucles repiten código: mostrar 50 productos, recorrer los resultados de una consulta, generar una tabla. En esta lección verás for, while, do-while y, sobre todo, foreach, el bucle que más usarás en PHP.
Concepto
Un bucle es como una lista de tareas en una fábrica: "por cada caja que llegue, ponle una etiqueta". En lugar de escribir la misma instrucción 100 veces, la escribes una vez y le dices cuántas veces (o hasta cuándo) repetirla.
| Bucle | Úsalo cuando... |
|---|---|
foreach | Recorres un array u objeto iterable (lo más común) |
for | Sabes cuántas repeticiones necesitas o necesitas un índice numérico |
while | Repites mientras se cumpla una condición, sin saber cuántas veces |
do-while | Igual que while, pero debe ejecutarse al menos una vez |
for
php
<?php
// for (inicio; condición; incremento)
for ($i = 1; $i <= 5; $i++) {
echo "Vuelta número $i<br>";
}
// Contar hacia atrás de 10 en 10
for ($i = 100; $i >= 0; $i -= 10) {
echo $i . ' ';
}
// 100 90 80 70 60 50 40 30 20 10 0Ejemplo útil: generar las opciones de un <select> de años.
php
<?php $anioActual = (int) date('Y'); ?>
<label for="anio">Año de nacimiento</label>
<select name="anio" id="anio">
<?php for ($anio = $anioActual; $anio >= $anioActual - 100; $anio--): ?>
<option value="<?= $anio ?>"><?= $anio ?></option>
<?php endfor; ?>
</select>while
Se repite mientras la condición sea verdadera. Útil cuando no sabes cuántas vueltas darás.
php
<?php
// ¿Cuántos años tardo en duplicar mi ahorro al 8 % anual?
$ahorro = 10000;
$meta = 20000;
$anios = 0;
while ($ahorro < $meta) {
$ahorro *= 1.08; // Crece 8 %
$anios++;
}
echo "Tardarás $anios años. Tendrás $" . number_format($ahorro, 2);
// Tardarás 10 años. Tendrás $21,589.25Lo verás mucho al leer resultados de base de datos:
php
<?php
// (Lo veremos a fondo en la lección de PDO)
while ($fila = $consulta->fetch()) {
echo $fila['nombre'] . '<br>';
}
// fetch() devuelve false cuando ya no hay filas, y el bucle termina.do-while
Ejecuta el bloque primero y luego pregunta. Siempre corre al menos una vez.
php
<?php
// Generar un código aleatorio que no empiece con 0
do {
$codigo = (string) random_int(0, 999999);
$codigo = str_pad($codigo, 6, '0', STR_PAD_LEFT); // Rellena con ceros a 6 dígitos
} while ($codigo[0] === '0');
echo "Tu código: $codigo";foreach: el rey de PHP
Recorre cada elemento de un array sin preocuparte por índices.
php
<?php
$frutas = ['manzana', 'pera', 'mango'];
// Sólo el valor
foreach ($frutas as $fruta) {
echo "Me gusta el/la $fruta<br>";
}
// Índice y valor
foreach ($frutas as $indice => $fruta) {
echo ($indice + 1) . ". $fruta<br>";
}Con arrays asociativos (clave => valor):
php
<?php
$precios = [
'Café americano' => 35,
'Capuchino' => 48,
'Té chai' => 42,
];
foreach ($precios as $producto => $precio) {
echo "$producto: $$precio<br>";
}Con arrays de arrays (el formato típico de una consulta a base de datos):
php
<?php
$productos = [
['id' => 1, 'nombre' => 'Teclado', 'precio' => 650, 'stock' => 8],
['id' => 2, 'nombre' => 'Mouse', 'precio' => 320, 'stock' => 0],
['id' => 3, 'nombre' => 'Monitor', 'precio' => 3200, 'stock' => 3],
];
?>
<table border="1" cellpadding="6">
<thead>
<tr><th>Producto</th><th>Precio</th><th>Estado</th></tr>
</thead>
<tbody>
<?php foreach ($productos as $p): ?>
<tr>
<td><?= htmlspecialchars($p['nombre']) ?></td>
<td>$<?= number_format($p['precio'], 2) ?></td>
<td><?= $p['stock'] > 0 ? 'Disponible' : 'Agotado' ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>Destructuring en foreach
php
<?php
$coordenadas = [[19.43, -99.13], [20.67, -103.35]];
foreach ($coordenadas as [$lat, $lng]) {
echo "Lat: $lat, Lng: $lng<br>";
}
$usuarios = [
['nombre' => 'Ana', 'email' => 'ana@mail.com'],
['nombre' => 'Beto', 'email' => 'beto@mail.com'],
];
foreach ($usuarios as ['nombre' => $nombre, 'email' => $email]) {
echo "$nombre ($email)<br>";
}Modificar elementos: por referencia
Por defecto, foreach trabaja con una copia de cada elemento. Para modificar el array original usa &:
php
<?php
$precios = [100, 200, 300];
foreach ($precios as &$precio) {
$precio *= 1.16; // Añade IVA al original
}
unset($precio); // ¡IMPORTANTE! Rompe la referencia al terminar
print_r($precios); // [116, 232, 348]Error común
No hacer unset($precio) tras un foreach por referencia. La variable sigue apuntando al último elemento, y si luego la reutilizas en otro foreach, sobrescribirás ese elemento sin darte cuenta. Muchas veces es más claro usar array_map (lo verás en Arrays).
break y continue
php
<?php
$numeros = [4, 7, -1, 12, 0, 9];
// continue: salta a la siguiente vuelta
foreach ($numeros as $n) {
if ($n <= 0) {
continue; // Ignora negativos y cero
}
echo "$n ";
}
// 4 7 12 9
echo '<br>';
// break: sale del bucle por completo
$buscado = 12;
foreach ($numeros as $i => $n) {
if ($n === $buscado) {
echo "Encontré $buscado en la posición $i";
break; // Ya no hace falta seguir buscando
}
}break 2 sale de dos bucles anidados a la vez:
php
<?php
$matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
foreach ($matriz as $f => $fila) {
foreach ($fila as $c => $valor) {
if ($valor === 5) {
echo "El 5 está en fila $f, columna $c";
break 2;
}
}
}Bucles anidados: tabla de multiplicar
php
<?php
declare(strict_types=1);
$tamano = 10;
?>
<table border="1" cellpadding="4" style="border-collapse: collapse; text-align: center">
<?php for ($fila = 1; $fila <= $tamano; $fila++): ?>
<tr>
<?php for ($col = 1; $col <= $tamano; $col++): ?>
<!-- Resaltamos la diagonal (cuadrados perfectos) -->
<td style="<?= $fila === $col ? 'background:#ffe08a' : '' ?>">
<?= $fila * $col ?>
</td>
<?php endfor; ?>
</tr>
<?php endfor; ?>
</table>Bucles infinitos
Si la condición de un while nunca se vuelve falsa, el script se cuelga hasta que PHP lo corta por tiempo (max_execution_time, 30 s por defecto). Asegúrate siempre de que algo dentro del bucle acerque la condición a su fin.
Buena práctica
No hagas consultas a la base de datos dentro de un bucle si puedes evitarlo (el famoso problema "N+1"). Trae todos los datos de una vez y luego recórrelos. Tu sitio será mucho más rápido.
Ejercicios
- Imprime los números del 1 al 30, pero sustituye los múltiplos de 3 por "Fizz", los de 5 por "Buzz" y los de ambos por "FizzBuzz".
- Dado un array de calificaciones
[8, 9.5, 6, 10, 7.5], calcula el promedio conforeach(sin usararray_sum). - Recorre este array y genera una lista HTML sólo con los usuarios activos:
[['nombre' => 'Ana', 'activo' => true], ['nombre' => 'Luis', 'activo' => false], ['nombre' => 'Sofía', 'activo' => true]].
Ver solución
php
<?php
for ($i = 1; $i <= 30; $i++) {
echo match (true) {
$i % 15 === 0 => 'FizzBuzz',
$i % 3 === 0 => 'Fizz',
$i % 5 === 0 => 'Buzz',
default => $i,
};
echo '<br>';
}php
<?php
$calificaciones = [8, 9.5, 6, 10, 7.5];
$suma = 0;
foreach ($calificaciones as $c) {
$suma += $c;
}
$promedio = $suma / count($calificaciones);
echo 'Promedio: ' . round($promedio, 2); // 8.2php
<?php
$usuarios = [
['nombre' => 'Ana', 'activo' => true],
['nombre' => 'Luis', 'activo' => false],
['nombre' => 'Sofía', 'activo' => true],
];
?>
<ul>
<?php foreach ($usuarios as $u): ?>
<?php if (!$u['activo']) continue; ?>
<li><?= htmlspecialchars($u['nombre']) ?></li>
<?php endforeach; ?>
</ul>Resumen
foreachrecorre arrays; es el bucle más usado en PHP.forcuando conoces el número de vueltas;whilecuando depende de una condición.do-whilegarantiza al menos una ejecución.continuesalta a la siguiente vuelta;breaksale del bucle (break 2de dos niveles).- Para modificar el array en un
foreach, usa&y luegounset(). - En plantillas:
foreach: ... endforeach;yfor: ... endfor;. - Evita bucles infinitos y consultas a la base de datos dentro de bucles.