Aquí se juntan las dos unidades. El ciclo escribe una vez el HTML que se repite, y PHP lo multiplica. El resultado que recibe el navegador es HTML normal, como si lo hubieras escrito a mano.

La sintaxis para vistas: for (…): y endfor;

Igual que el if en if, elseif y else, los ciclos tienen una forma con dos puntos pensada para envolver HTML.

Con llavesCon dos puntos
for (…) { … }for (…): … endfor;
while (…) { … }while (…): … endwhile;
foreach (…) { … }foreach (…): … endforeach;
<ul class="list-disc list-inside">
  <?php for ($i = 1; $i <= 3; $i++): ?>
    <li>Elemento <?= $i ?></li>
  <?php endfor; ?>
</ul>

Lo que recibe el navegador:

<ul class="list-disc list-inside">
      <li>Elemento 1</li>
      <li>Elemento 2</li>
      <li>Elemento 3</li>
  </ul>

La indentación del resultado sale rara, y no importa

Los espacios que quedan antes de cada <?php se copian en cada vuelta, por eso el HTML resultante queda con sangrías irregulares. El navegador las ignora. Lo que importa es que tu archivo .php esté bien indentado.

Incorrecto: armar el HTML con echo y concatenación

<?php
echo "<ul>";
for ($i = 1; $i <= 3; $i++) {
    echo "<li class=\"py-1\">Elemento " . $i . "</li>";
}
echo "</ul>";

Funciona, pero las etiquetas quedan dentro de comillas: VSCode no las autocompleta, Prettier no las ordena, y cada atributo necesita comillas escapadas.

Correcto: HTML afuera, PHP solo en lo que cambia

<ul>
  <?php for ($i = 1; $i <= 3; $i++): ?>
    <li class="py-1">Elemento <?= $i ?></li>
  <?php endfor; ?>
</ul>

Las opciones de un select

Es el caso más útil en un formulario. Cien años escritos a mano son cien oportunidades de equivocarse; con un ciclo es una línea.

<?php
date_default_timezone_set("America/Punta_Arenas");
$anioActual = (int) date("Y");
?>
 
<label for="anio">Año de nacimiento</label>
<select id="anio" name="anio">
  <option value="">Selecciona un año</option>
  <?php for ($anio = $anioActual; $anio >= 1960; $anio--): ?>
    <option value="<?= $anio ?>"><?= $anio ?></option>
  <?php endfor; ?>
</select>

Lo que recibe el navegador (resumido):

<select id="anio" name="anio">
  <option value="">Selecciona un año</option>
  <option value="2026">2026</option>
  <option value="2025">2025</option>
  …
  <option value="1960">1960</option>
</select>

Incorrecto: option sin value

<option><?= $anio ?></option>

Funciona porque el navegador envía el texto visible, pero si después cambias el texto (por ejemplo, a “Año 2026”), cambia también el dato que llega al servidor. Igual que en la Unidad 1, cada <option> lleva su value.

Dejar una opción seleccionada

Para que una opción aparezca elegida, se le agrega el atributo selected. Dentro del ciclo, un if (…): decide en cada vuelta si esta opción es la elegida:

<?php $elegido = 3; ?>
 
<select name="cantidad">
  <?php for ($n = 1; $n <= 5; $n++): ?>
    <?php if ($n === $elegido): ?>
      <option value="<?= $n ?>" selected><?= $n ?></option>
    <?php else: ?>
      <option value="<?= $n ?>"><?= $n ?></option>
    <?php endif; ?>
  <?php endfor; ?>
</select>
<select name="cantidad">
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3" selected>3</option>
          <option value="4">4</option>
          <option value="5">5</option>
  </select>

En la Parte 6 esto se usa para que, si el formulario tiene un error, el usuario no pierda lo que ya había elegido.

Una tabla con filas alternadas

Dos ciclos anidados: el de afuera recorre las filas y el de adentro las columnas. Con % se pinta una fila sí y otra no.

<table class="border-collapse">
  <caption class="font-bold mb-2">Tabla del 1 al 5</caption>
  <?php for ($fila = 1; $fila <= 5; $fila++): ?>
    <?php
    $fondo = "";
    if ($fila % 2 === 0) {
        $fondo = "bg-gray-100";
    }
    ?>
    <tr class="<?= $fondo ?>">
      <?php for ($col = 1; $col <= 5; $col++): ?>
        <td class="border border-gray-300 px-3 py-1 text-right"><?= $fila * $col ?></td>
      <?php endfor; ?>
    </tr>
  <?php endfor; ?>
</table>

Cómo se recorren los ciclos anidados

 $fila = 1 -> $col = 1, 2, 3, 4, 5   ->   1   2   3   4   5
 $fila = 2 -> $col = 1, 2, 3, 4, 5   ->   2   4   6   8  10   (gris)
 $fila = 3 -> $col = 1, 2, 3, 4, 5   ->   3   6   9  12  15
 $fila = 4 -> $col = 1, 2, 3, 4, 5   ->   4   8  12  16  20   (gris)
 $fila = 5 -> $col = 1, 2, 3, 4, 5   ->   5  10  15  20  25

Por cada vuelta del ciclo de afuera, el de adentro hace todas sus vueltas. 5 filas × 5 columnas = 25 celdas.

Correcto: calcular la clase arriba, imprimirla abajo

La clase de cada fila se decide en un bloque PHP antes del <tr>: $fondo parte vacía y solo en las filas pares cambia a "bg-gray-100". En el HTML solo se imprime <?= $fondo ?>. Así la etiqueta queda corta y la decisión se lee como un if normal.

Las filas de una tabla real vienen de datos

Esta tabla se calcula. Las tablas de un sitio (productos, inscritos, notas) se generan desde un arreglo con foreach, que es lo que viene en la Parte 5. La estructura es la misma: un ciclo por las filas y otro por las columnas.

Pruébalo

  • Genera una lista <ul> de diez elementos con for (…): y endfor;
  • Genera un <select> con los años desde el actual hasta 1960 y revisa en F12 que cada opción tiene value
  • Deja una opción seleccionada con un if (…): dentro del ciclo
  • Haz la tabla de multiplicar del 1 al 10 con filas alternadas
  • Error provocado: borra el endfor; y lee el error

Errores comunes

SíntomaCausaSolución
syntax error, unexpected end of fileFalta el endfor; (o endwhile;, endforeach;)Cada ciclo con : necesita su cierre
Sale una sola opciónEl <option> quedó fuera del cicloEl HTML que se repite va entre for (…): y endfor;
Todas las opciones salen con selectedLa condición del if es siempre verdadera, o usaste =Compara con ===
syntax error, unexpected token "<"Escribiste HTML dentro de un bloque PHPCierra el bloque con ?> antes del HTML y vuelve a abrirlo después

Checkpoint

Intenta responder antes de desplegar cada pregunta.