Saltar a contenido

Lab 03 — Arrays y bucles foreach en PHP

Campo Valor
Módulo Implantación de Aplicaciones Web (IMW)
Ciclo Administración de Sistemas Informáticos en Red (ASIR)
Unidad UT4 — PHP en servidor
Criterios de evaluación (CE) d (estructuras de control: foreach)
Requisitos Labs 01 y 02 completados
Agrupación Individual
Herramientas Navegador + editor de texto + terminal
Apuntes https://ichigar.codeberg.page/imw/recursos/ut4_php_03_arrays_y_listas/
Tiempo estimado 90–120 minutos

Antes de empezar

Qué vamos a hacer

Los arrays en PHP son colecciones de valores. Son el tipo de dato más usado en una aplicación web: listas de incidencias, opciones de un formulario, resultados de una búsqueda… todo pasa por arrays.

En este lab practicarás los tres tipos principales de arrays y cómo recorrerlos con foreach:

  1. Arrays indexados — listas de valores con índice numérico (0, 1, 2…).
  2. Arrays asociativos — pares clave/valor, como los diccionarios o los objetos JSON.
  3. Arrays de arrays — listas de registros (una lista de incidencias, por ejemplo).

Al terminar tendrás un fichero listado.php con una tabla HTML generada a partir de un array de 5 incidencias. Es la base directa de los labs 04 (condicionales) y 08 (persistencia en JSON).

Antes de empezar: comprueba que gestor.local sigue funcionando

Desde el navegador de tu Windows, visita http://gestor.local. Debes ver la página del Lab 01 (tu index.php personalizado).

Si el navegador dice "no se puede resolver el nombre" o similar, es que ha pasado tiempo desde los labs anteriores y algo se ha desconfigurado. Causas habituales:

  • La IP del servidor ha cambiado (DHCP): ejecuta ip a | grep "inet " en la VM para ver la IP actual y edita C:\Windows\System32\drivers\etc\hosts en tu Windows (como administrador) para ajustar la línea de gestor.local.
  • Apache no está arrancado: en el servidor, sudo systemctl start apache2.

Hasta que http://gestor.local no responda, no sigas con este lab.


Cómo se entrega este lab

ZIP con esta estructura exacta:

apellido_nombre_lab03/
├── gestor/
│   └── public/
│       ├── arrays.php
│       ├── listado.php
│       └── catalogo.php
├── capturas/
│   ├── paso1_lista_simple.png
│   ├── paso2_incidencia_asociativa.png
│   ├── paso3_tabla_incidencias.png
│   └── paso5_catalogo.png
└── RESPUESTAS.md

Nombres prescritos

Los nombres de ficheros y carpetas son obligatorios y exactos. El script de corrección busca estos nombres concretos.

Cómo preparar el ZIP (Windows o Linux/macOS):

  1. Crea la carpeta apellido_nombre_lab03/ (minúsculas, sin espacios ni acentos) y reproduce la estructura: los 3 ficheros PHP los crearás en el servidor durante los pasos del lab y debes copiarlos a tu carpeta de entrega antes de hacer el ZIP; guarda también las 4 capturas con el nombre exacto, y crea RESPUESTAS.md.
  2. Comprime:
    • Windows: clic derecho sobre la carpeta → Enviar aCarpeta comprimida (en zip).
    • Linux/macOS: zip -r apellido_nombre_lab03.zip apellido_nombre_lab03/.
  3. Verifica que al abrir el ZIP aparece una sola carpeta raíz apellido_nombre_lab03/.

Paso 1 — Arrays indexados y foreach

Conceptos

  • Un array indexado es una lista de valores con índice numérico. El primer elemento está en la posición 0.
  • Se crea con corchetes: $arr = ['baja', 'media', 'alta'];.
  • Se añade al final con $arr[] = 'critica';. Los corchetes vacíos no son un error: significan "el siguiente índice libre", y PHP lo asigna automáticamente.
  • Se cuenta el número de elementos con count($arr).
  • foreach recorre todos los elementos uno a uno.
  • Sintaxis foreach (...): ... endforeach: dentro de bloques HTML usamos esta forma en lugar de las llaves { ... }. Es más legible cuando se mezclan PHP y HTML porque la etiqueta de cierre dice claramente qué bloque se cierra. Cada foreach necesita su endforeach: si te lo saltas, PHP intenta interpretar el resto del fichero como parte del bucle y la página deja de funcionar.

Objetivo. Crear un array con varias prioridades y mostrarlas como una lista HTML <ul>.

Tarea. Crea ~/gestor/public/arrays.php:

<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');

$prioridades = ['baja', 'media', 'alta'];
$prioridades[] = 'critica';   // añade al final
?>
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <title>Arrays indexados</title>
</head>
<body>
<h1>Niveles de prioridad</h1>
<p>Total: <?= count($prioridades) ?> niveles.</p>

<ul>
<?php foreach ($prioridades as $p): ?>
  <li><?= htmlspecialchars($p) ?></li>
<?php endforeach ?>
</ul>
</body>
</html>

Visita http://gestor.local/arrays.php. Debes ver una lista con 4 prioridades.

Pista si no aparece nada

Comprueba que cada foreach tiene su endforeach al final. Si te saltas uno, la página entera deja de funcionar.

Responde en RESPUESTAS.md, sección ## Paso 1.

Añade una quinta prioridad al array ('urgente' por ejemplo) y recarga. ¿Cuántos elementos aparecen ahora? ¿Qué valor tiene count($prioridades)?

Captura requerida. Después de añadir la 5ª prioridad del ejercicio anterior, guarda en capturas/paso1_lista_simple.png una captura con la lista visible. Debe verse:

  • La URL http://gestor.local/arrays.php.
  • La línea "Total: 5 niveles."
  • La lista <ul> con los 5 elementos (baja, media, alta, critica, urgente).

Verificación.

  • ~/gestor/public/arrays.php existe y carga en el navegador.
  • La lista muestra las 5 prioridades (tras añadir la 5ª en el ejercicio).
  • He guardado capturas/paso1_lista_simple.png con los 5 elementos.

Paso 2 — Arrays asociativos

Conceptos

  • Un array asociativo se diferencia del indexado del Paso 1 en que las claves son texto en lugar de números. En el Paso 1 accedías con $prioridades[0] (índice numérico); aquí accederás con $incidencia['titulo'] (clave de texto). La notación es la misma, lo que cambia es el contenido entre corchetes.
  • Se crea con la sintaxis 'clave' => valor. El símbolo => se lee como "apunta a": 'titulo' => 'Impresora' se lee "la clave titulo apunta al valor Impresora".
  • Las claves distinguen mayúsculas: $inc['titulo'] y $inc['Titulo'] son claves distintas.
  • Es el equivalente PHP de un diccionario en Python o un objeto JSON. Lo usaremos para representar cada incidencia en el proyecto: un array con claves id, titulo, descripcion, prioridad, estado, etc.

Objetivo. Ampliar arrays.php para mostrar los datos de una única incidencia representada como array asociativo.

Tarea. Modifica ~/gestor/public/arrays.php. Dentro del bloque PHP que ya tienes, justo después de la línea $prioridades[] = 'critica'; y antes del ?>, añade:

1
2
3
4
5
6
7
8
// Una incidencia como array asociativo
$incidencia = [
    'id'          => 'inc001',
    'titulo'      => 'Impresora sin papel',
    'descripcion' => 'La impresora de la sala 201 no tiene papel.',
    'prioridad'   => 'alta',
    'estado'      => 'pendiente',
];

Al final del <body>, justo antes de la etiqueta </body> de cierre (debe quedar después del <ul> de prioridades del Paso 1), añade este bloque:

1
2
3
4
5
6
7
8
<h2>Ficha de la incidencia</h2>
<ul>
  <li><strong>ID:</strong> <?= htmlspecialchars($incidencia['id']) ?></li>
  <li><strong>Título:</strong> <?= htmlspecialchars($incidencia['titulo']) ?></li>
  <li><strong>Descripción:</strong> <?= htmlspecialchars($incidencia['descripcion']) ?></li>
  <li><strong>Prioridad:</strong> <?= htmlspecialchars($incidencia['prioridad']) ?></li>
  <li><strong>Estado:</strong> <?= htmlspecialchars($incidencia['estado']) ?></li>
</ul>

Guarda y recarga.

Pista

Si te equivocas en una clave (ej. $incidencia['titulos'] en plural), PHP muestra un aviso "Undefined array key" si tienes display_errors activo. Si no ves el aviso y en su lugar la página sale en blanco o con HTTP ERROR 500, lo más probable es que display_errors siga apagado en php.ini: vuelve al Paso 5 del lab A1 — Depuración: ver los errores de PHP — y actívalo. Una vez hecho, no tendrás que volver a tocarlo en los próximos labs.

Responde en RESPUESTAS.md, sección ## Paso 2.

¿Qué diferencia fundamental hay entre un array indexado (el del Paso 1) y un array asociativo (el del Paso 2)? Cita un escenario práctico para cada uno. (Piensa: ¿cuándo te interesa acceder por posición como $arr[0] y cuándo por nombre como $arr['titulo']? ¿Qué tipo usarías para una lista simple de prioridades y cuál para los datos de una persona?)

Captura requerida. Guarda en capturas/paso2_incidencia_asociativa.png una captura con la ficha visible. Debe verse:

  • La URL http://gestor.local/arrays.php.
  • La sección "Ficha de la incidencia" con los 5 campos (ID, Título, Descripción, Prioridad, Estado).

Verificación.

  • arrays.php tiene el array asociativo $incidencia con al menos 5 claves.
  • La ficha aparece debajo de la lista del Paso 1.
  • He guardado capturas/paso2_incidencia_asociativa.png.

Paso 3 — Array de arrays: lista de incidencias en una tabla

Conceptos

  • En aplicaciones reales, normalmente tienes muchos registros del mismo tipo: una lista de incidencias, una lista de usuarios, una lista de pedidos. Se representan como arrays de arrays asociativos.
  • Se recorren con foreach, y dentro del bucle accedes a cada campo con la notación $elemento['clave'].
  • Variable de iteración: en foreach ($incidencias as $inc):, la variable $inc toma el valor de cada elemento en cada vuelta del bucle. Dentro del bucle, $inc representa la incidencia "actual" (un array asociativo), y accedes a sus claves con $inc['id'], $inc['titulo'], etc. Después del endforeach, $inc ya no se usa. El nombre $inc es solo una elección — podríamos haberla llamado $x o $item, pero conviene un nombre que indique qué representa.
  • La estructura HTML natural para presentarlos es una tabla <table>.

Objetivo. Crear un fichero nuevo listado.php con una lista de al menos 5 incidencias y mostrarlas en una tabla HTML.

Tarea. Crea ~/gestor/public/listado.php:

<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');

$incidencias = [
    ['id' => 'inc001', 'titulo' => 'Impresora sin papel',      'prioridad' => 'alta',  'estado' => 'pendiente'],
    ['id' => 'inc002', 'titulo' => 'Proyector sin imagen',     'prioridad' => 'media', 'estado' => 'en_proceso'],
    ['id' => 'inc003', 'titulo' => 'Wifi caído en aula 201',   'prioridad' => 'alta',  'estado' => 'resuelto'],
    ['id' => 'inc004', 'titulo' => 'Teclado de recepción roto','prioridad' => 'baja',  'estado' => 'pendiente'],
    ['id' => 'inc005', 'titulo' => 'Actualizar antivirus',     'prioridad' => 'media', 'estado' => 'resuelto'],
];
?>
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <title>Listado de incidencias</title>
  <style>
    table { border-collapse: collapse; }
    th, td { border: 1px solid #999; padding: 4px 10px; text-align: left; }
    th { background: #eee; }
  </style>
</head>
<body>
<h1>Listado de incidencias</h1>
<p>Total: <?= count($incidencias) ?> incidencias.</p>

<table>
  <thead>
    <tr>
      <th>ID</th>
      <th>Título</th>
      <th>Prioridad</th>
      <th>Estado</th>
    </tr>
  </thead>
  <tbody>
  <?php foreach ($incidencias as $inc): ?>
    <tr>
      <td><?= htmlspecialchars($inc['id']) ?></td>
      <td><?= htmlspecialchars($inc['titulo']) ?></td>
      <td><?= htmlspecialchars($inc['prioridad']) ?></td>
      <td><?= htmlspecialchars($inc['estado']) ?></td>
    </tr>
  <?php endforeach ?>
  </tbody>
</table>
</body>
</html>

Visita http://gestor.local/listado.php. Debes ver una tabla con las 5 filas.

El bloque <style> es opcional

El <style> con CSS dentro del <head> se incluye solo para que la tabla sea legible (bordes, padding, fondo de cabecera). Es un detalle estético: si lo quitas, la tabla sigue siendo válida. Los siguientes ficheros del lab no requieren CSS.

Pista sobre el foreach dentro de la tabla

Observa dónde están el <?php foreach ...: ?> y el <?php endforeach ?>: fuera de <tr> y </tr>. Es decir, por cada iteración del bucle se genera un <tr> completo. Un error habitual es meter el foreach dentro del <tr> y generar tablas rotas.

Responde en RESPUESTAS.md, sección ## Paso 3.

Añade una 6ª incidencia al array $incidencias y recarga. ¿Has tenido que tocar el bloque del foreach para que aparezca la nueva fila? ¿Por qué?

La 6ª incidencia es solo para responder a la pregunta. Puedes dejarla en el array o quitarla — el requisito mínimo de entrega son 5 incidencias.

Captura requerida. Guarda en capturas/paso3_tabla_incidencias.png una captura con la tabla visible. Debe verse:

  • La URL http://gestor.local/listado.php.
  • La cabecera con "Total: N incidencias."
  • La tabla con sus 4 columnas y todas las filas.

Verificación.

  • ~/gestor/public/listado.php existe y carga.
  • El array $incidencias tiene al menos 5 elementos.
  • La tabla muestra correctamente todas las filas.
  • He guardado capturas/paso3_tabla_incidencias.png.

Paso 4 — Guardar el total en una variable reutilizable

Conceptos

  • Guardar un valor en una variable reutilizable hace el código más claro y mantenible. En el Paso 3 llamabas a count($incidencias) directamente dentro del HTML; en aplicaciones reales es habitual reutilizar el total en varios sitios (cabecera, pie, mensaje "X de Y elementos"...). Si el cálculo cambia mañana — por ejemplo, contar solo las abiertas — solo tocas una línea.
  • Esta refactorización es además la forma natural de tener el total disponible para los condicionales del Lab 04: allí decidiremos qué pintar según si hay incidencias o no.

Objetivo. Ampliar listado.php para guardar el total en una variable reutilizable.

Tarea 1 — Declarar $total_incidencias. Modifica ~/gestor/public/listado.php. Añade al bloque PHP inicial, después del array $incidencias:

$total_incidencias = count($incidencias);

Tarea 2 — Usar la variable en el contador. En el HTML, cambia la línea del Paso 3 que tenía:

<p>Total: <?= count($incidencias) ?> incidencias.</p>

por esta otra que usa la variable ya calculada:

<p>Total: <?= $total_incidencias ?> incidencias.</p>

Guarda y recarga http://gestor.local/listado.php. La página se ve exactamente igual que en el Paso 3: el cambio es interno, no visual. Por eso este paso no requiere captura nueva.

Responde en RESPUESTAS.md, sección ## Paso 4.

¿Qué ventaja tiene guardar el total en $total_incidencias frente a escribir count($incidencias) cada vez que necesites el número? Piensa en el escenario de tener que mostrar el total en dos sitios distintos de la página, o en cómo afectaría a tu código si mañana cambiaras el criterio (contar solo abiertas, por ejemplo).

Verificación.

  • listado.php declara la variable $total_incidencias = count($incidencias) en el bloque PHP inicial.
  • El <p>Total:</p> del Paso 3 usa ahora $total_incidencias en lugar de count() inline.
  • listado.php pasa php -l sin errores.

Paso 5 — Aplica lo aprendido: catálogo de equipos del taller

Qué es este paso

Los cuatro pasos anteriores han sido guiados: te he dado el código y tú lo has tecleado, probado y respondido. En este quinto paso no hay código de referencia. Lo que hay son requisitos, y tú aplicas lo aprendido en los pasos 1 a 4 para cumplirlos.

Vas a aplicar exactamente el mismo patrón que en los Pasos 2, 3 y 4, pero con un contexto distinto: en lugar de incidencias, equipos de un taller de reparaciones. Los tipos de array, la tabla HTML, el foreach, count() y htmlspecialchars() funcionan igual — solo cambian los nombres de las variables y los campos.

Objetivo. Crear un fichero nuevo catalogo.php que muestre una ficha del taller y una tabla con sus equipos, aplicando todo lo visto en los pasos 1 a 4.

Tarea. Crea ~/gestor/public/catalogo.php cumpliendo los requisitos siguientes.

Requisitos obligatorios de contenido:

  1. Un array asociativo $taller con al menos 3 campos de texto:
    • nombre (por ejemplo 'Reparaciones Chema').
    • ciudad (por ejemplo 'Las Palmas').
    • responsable (tu nombre, o uno inventado).
  2. Un array de arrays $equipos con al menos 6 equipos, cada uno con 4 claves:
    • id ('eq001', 'eq002'…).
    • modelo (por ejemplo 'Lenovo ThinkPad T480').
    • ubicacion (por ejemplo 'Banco 2', 'Almacén').
    • estado (uno de estos cuatro valores: 'operativo', 'averiado', 'en_reparacion', 'retirado').
  3. Una variable calculada:
    • $total_equipos → guarda el número total de equipos usando count($equipos) (como en el Paso 4 con $total_incidencias).

Requisitos de presentación HTML:

  • Crea una variable $titulo con el texto "Catálogo de equipos de [nombre del taller]", construida con concatenación o interpolación a partir de $taller['nombre'] (como viste en el Lab 02 Paso 4), y muéstrala en un <h1> usando <?= htmlspecialchars($titulo) ?>.
  • Una ficha del taller con <ul> (como en el Paso 2 de este lab) mostrando los 3 campos del array asociativo.
  • Un <h2> con el texto "Total: N equipos", donde N es la variable $total_equipos.
  • Una tabla <table> con los equipos, con al menos las columnas ID, Modelo, Ubicación, Estado. Recórrela con foreach como en el Paso 3.

Requisitos de calidad del código:

  • error_reporting(E_ALL) y ini_set('display_errors', '1') al principio del fichero.
  • Al menos 2 comentarios: uno sobre cada uno de los dos arrays (qué representan y qué datos contienen).
  • htmlspecialchars() en todos los valores de texto que muestres en el HTML.

Visita http://gestor.local/catalogo.php para comprobar el resultado.

Pista sobre la concatenación del <h1>

Puedes usar interpolación con comillas dobles (la v2 del Lab 02 Paso 4):

$titulo = "Catálogo de equipos de {$taller['nombre']}";

O el operador punto:

$titulo = 'Catálogo de equipos de ' . $taller['nombre'];

Ambas valen. Elige la que te resulte más cómoda.

Responde en RESPUESTAS.md, sección ## Paso 5.

  1. ¿Qué diferencias hay entre tu $taller y tu $equipos? Indica de qué tipo es cada uno (indexado, asociativo, array de arrays) y por qué has elegido ese tipo para cada caso.
  2. Imagina que quisieras calcular cuántos equipos están en estado 'operativo'. Sin escribir el código, describe en un par de frases qué necesitarías para hacerlo: ¿te sirve lo que ya sabes de foreach, o te faltaría alguna herramienta nueva?

Captura requerida. Guarda en capturas/paso5_catalogo.png una captura del navegador visitando http://gestor.local/catalogo.php. Debe verse:

  • La URL http://gestor.local/catalogo.php.
  • El <h1> con el título construido por concatenación.
  • La ficha del taller con los 3 campos.
  • El <h2> con el total de equipos.
  • La tabla con al menos 6 equipos y sus 4 columnas.

Verificación.

  • ~/gestor/public/catalogo.php existe y carga en el navegador.
  • Tiene un array asociativo $taller con al menos 3 campos.
  • Tiene un array de arrays $equipos con al menos 6 equipos.
  • Hay una variable $titulo construida con concatenación o interpolación a partir de $taller['nombre'].
  • La variable $total_equipos se calcula con count($equipos) (no es un número hardcoded como $total_equipos = 6;).
  • La página muestra la ficha del taller, el total de equipos y la tabla.
  • Todos los valores de texto se muestran con htmlspecialchars().
  • He guardado capturas/paso5_catalogo.png.

Ampliación opcional

Para alumnos que terminen pronto

Prueba a:

  • Añadir una columna "Nº" en la tabla con el número de fila (1, 2, 3…). Pista: foreach ($incidencias as $i => $inc) te da tanto el índice $i como el valor $inc.
  • Recorrer las claves y valores de $incidencia del Paso 2 con foreach ($incidencia as $clave => $valor) y generar una tabla de 2 columnas (clave / valor) automáticamente, sin listar los campos uno a uno.

Estas ampliaciones no se corrigen.


Checklist final de entrega

Antes de subir el ZIP al Campus:

  • He creado la carpeta apellido_nombre_lab03/ con mi apellido y nombre reales.
  • Dentro hay gestor/public/arrays.php, listado.php y catalogo.php.
  • Dentro hay capturas/ con los 4 PNG (paso1_lista_simple.png, paso2_incidencia_asociativa.png, paso3_tabla_incidencias.png, paso5_catalogo.png).
  • Dentro hay RESPUESTAS.md con las 5 secciones (## Paso 1 a ## Paso 5) rellenas.
  • Los 3 ficheros PHP pasan php -l sin errores.
  • El ZIP se llama apellido_nombre_lab03.zip y al descomprimir aparece una sola carpeta raíz.
  • He subido el ZIP al Campus antes de la fecha límite.

Anexo — Plantilla de RESPUESTAS.md

Crea RESPUESTAS.md dentro de apellido_nombre_lab03/:

# Respuestas — Lab 03 Arrays y foreach

**Nombre:** Apellido, Nombre
**Fecha:** YYYY-MM-DD

## Paso 1

(Tu respuesta: qué pasa al añadir una 5ª prioridad y valor de `count($prioridades)`.)

## Paso 2

(Tu respuesta: diferencia entre array indexado y asociativo, y un escenario práctico de cada uno.)

## Paso 3

(Tu respuesta: ¿has tocado el `foreach` al añadir la 6ª incidencia? ¿Por qué?)

## Paso 4

(Tu respuesta: ventaja de guardar el total en `$total_incidencias` frente a escribir `count($incidencias)` cada vez. Piensa en mostrar el total en dos sitios y en el escenario de cambiar mañana el criterio de cálculo.)

## Paso 5

1. (Tu respuesta: diferencias entre `$taller` y `$equipos`, tipo de cada uno y por qué.)
2. (Tu respuesta: descripción informal de qué necesitarías para contar equipos operativos y si las herramientas de este lab te bastan o no.)