UT3 · Guía para resolver las prácticas paso a paso

Esta guía no trae el código de las prácticas. Te enseña cómo pensar cada una: por dónde empezar, en qué trozos dividirla, qué preguntarte y cómo comprobar que funciona. El código lo escribes tú.

Úsala así:

  1. Lee primero la parte 1 (el método) y la parte 2 (los patrones). Sirven para todas las prácticas.
  2. Cuando vayas a hacer una práctica, abre su ficha en la parte 3 y síguela con el cuaderno al lado.
  3. Si te atascas, vuelve al patrón que se cita en la ficha.

Si te bloqueas

Atascarse es normal: es parte de aprender a programar. Lo que no funciona es quedarse mirando la pantalla. Si llevas más de 15 minutos sin avanzar, vuelve al cuaderno, escribe en una frase qué intentas hacer y qué ocurre, y pregunta con esa frase.


1. El método: seis pasos para cualquier ejercicio

Casi todos los bloqueos vienen de empezar a escribir código demasiado pronto. Antes de abrir VS Code, haz los pasos 1 a 4 en el cuaderno.

Paso 1. Lee y subraya

Lee el enunciado dos veces. En la segunda, subraya:

  • los datos que da el usuario (normalmente, los campos del formulario);
  • lo que hay que mostrar;
  • las condiciones: «obligatorio», «entre 1 y 30», «solo si…», «si no…».

Mira también la plantilla: los atributos name del formulario te dicen cómo se llaman los datos que te llegan.

Paso 2. Haz la tabla Entrada – Proceso – Salida

Dibuja en el cuaderno una tabla de tres columnas:

Entrada (¿qué tengo?) Proceso (¿qué hago?) Salida (¿qué muestro?)
Los campos del formulario y sus name Validar, calcular, buscar, recorrer… Mensajes, resultados, tablas…

Si no sabes rellenar alguna columna, todavía no has entendido el problema. Vuelve a leer el enunciado antes de seguir.

Paso 3. Divide el problema en trozos pequeños

Un problema grande da miedo; cinco problemas pequeños, no. Casi todas las prácticas de esta unidad se dividen en los mismos trozos:

  1. Saber si el formulario se ha enviado.
  2. Recoger los datos.
  3. Validarlos y apuntar los errores.
  4. Si no hay errores, calcular el resultado.
  5. Mostrar el formulario (con lo que escribió el usuario), los errores y el resultado.

Escribe la lista en el cuaderno y ve tachando cada trozo cuando funcione.

Paso 4. Escribe el pseudocódigo

El pseudocódigo es el programa escrito en castellano, con la estructura de un programa: SI … ENTONCES, PARA CADA …, MIENTRAS …. No tiene que compilar; tiene que ser claro.

SI se ha enviado el formulario ENTONCES
    recoger número
    SI número no es entero positivo ENTONCES
        apuntar error
    SI NO
        calcular letra
FIN SI

Después, haz una traza: elige unos datos de prueba y sigue el pseudocódigo a mano, línea a línea, apuntando el valor de cada variable. Si la traza da el resultado correcto, el algoritmo está bien y solo queda traducirlo a PHP.

Paso 5. Programa poco a poco y comprueba cada trozo

No escribas todo de golpe. Escribe un trozo, guarda, recarga el navegador y comprueba que hace lo que esperas. Para ver qué contiene una variable, usa:

echo '<pre>';
var_dump($_POST);   // o la variable que quieras inspeccionar
echo '</pre>';

Cuando el trozo funcione, borra la línea de depuración y pasa al siguiente.

Lee los mensajes de error

Un mensaje como Warning: Undefined array key "altura" in … on line 12 te dice qué pasa (esa clave no existe en el array) y dónde (línea 12). Léelo entero antes de cambiar nada.

Paso 6. Prueba con casos pensados y haz commit

Prueba siempre tres tipos de casos:

Tipo de caso Ejemplo Qué compruebas
Normal Un dato correcto y típico Que el cálculo es correcto
Límite El valor más pequeño y el más grande permitidos, una lista vacía… Que no fallas «por uno»
Erróneo Letras donde van números, campo vacío, valor fuera de rango, formulario manipulado Que la validación lo detecta

Cada ficha de la parte 3 trae una tabla de casos de prueba. Cuando todos pasen, haz el commit (P05: dibuja triángulo).


2. Patrones que se repiten

Un patrón es una forma de resolver un tipo de problema que aparece una y otra vez. Si reconoces el patrón, ya tienes media práctica hecha. En cada ficha se indica cuáles usar.

Patrón A · Estructura de una página PHP

Separa la página en dos zonas:

┌──────────────────────────────────────────┐
│ ZONA DE LÓGICA (PHP, antes del DOCTYPE)  │
│  · inicializar variables                 │
│  · ¿se ha enviado? → recoger, validar,   │
│    calcular                              │
├──────────────────────────────────────────┤
│ ZONA DE PRESENTACIÓN (HTML)              │
│  · solo muestra variables ya calculadas  │
│  · if / foreach con sintaxis alternativa │
└──────────────────────────────────────────┘

Regla de oro: en la zona de HTML no se calcula nada; solo se muestra. Si te ves haciendo cuentas en mitad de una <table>, sube ese cálculo a la zona de lógica y guarda el resultado en una variable.

Inicializa al principio todas las variables que usará el HTML ($errores = [], $resultado = null, los valores de los campos a ''). Así la página funciona igual la primera vez que se abre, cuando todavía no se ha enviado nada.

Patrón B · ¿Se ha enviado el formulario?

En una página autoprocesada hay dos momentos: la primera visita (formulario vacío) y el envío. Distínguelos preguntando por el método de la petición ($_SERVER['REQUEST_METHOD']), como en la teoría (apartado 4).

En una página de resultados separada (…-res.php), si no se ha llegado por POST, redirige al formulario con header('Location: …') seguido de exit. Recuerda que header() tiene que ir antes de cualquier salida HTML, incluso antes de un espacio en blanco.

Patrón C · Validar y acumular errores

No muestres el primer error y te pares. Revisa todos los campos y apunta cada error en un array:

errores ← lista vacía
SI campo1 no es válido ENTONCES errores['campo1'] ← "mensaje"
SI campo2 no es válido ENTONCES errores['campo2'] ← "mensaje"
…
SI errores está vacío ENTONCES
    calcular y preparar el resultado
FIN SI

Usar el nombre del campo como clave te permite, después, mostrar cada error debajo de su campo (P09) o todos juntos (P12).

Para validar números, usa filter_input() o filter_var() con FILTER_VALIDATE_INT o FILTER_VALIDATE_FLOAT. Devuelven false si el dato no es válido, y admiten la opción min_range / max_range para los rangos. Compara siempre con === false, porque el número 0 también se evalúa como falso.

Patrón D · Formulario persistente

Después de enviar, el formulario debe mostrar lo que escribió el usuario:

Control Qué haces
text, number Pon en value el dato recibido, escapado con htmlspecialchars()
radio, checkbox Escribe checked solo en la opción cuyo value coincide con lo recibido
select Escribe selected solo en la <option> cuyo value coincide con lo recibido
password Nunca se vuelve a mostrar

Cuando haya muchas opciones, no compares una a una: genera las opciones con un bucle a partir de un array y compara dentro del bucle. Si lo haces en varias prácticas, crea una pequeña función que devuelva 'checked' o ''.

Patrón E · Escapar todo lo que se muestra

Todo dato que venga del usuario y vaya a aparecer en la página pasa por htmlspecialchars(). Si lo escribes muchas veces, crea una función corta h() que lo haga (la pide la P12, pero puedes usarla desde la P01).

Patrón F · Tabla de consulta en lugar de muchos if

Si tienes que traducir un valor a otro (un código a un texto, una divisa a su tasa, una opción a su ajuste), no encadenes if. Usa:

  • un array asociativo clave => valor, si los datos se usan en varios sitios (P07, P11, P12);
  • una expresión match, si solo es una traducción puntual (P01).

Ventaja: para añadir una opción nueva solo cambias el array, no la lógica.

Patrón G · Lista blanca

No te fíes de lo que llega del formulario: el usuario puede modificar el HTML desde el navegador y enviar cualquier cosa. Comprueba que el valor recibido está entre los permitidos antes de usarlo:

  • in_array($valor, $permitidos) si tienes una lista de valores (P08);
  • array_key_exists($valor, $array) o isset($array[$valor]) si los valores son claves de un array (P07, P11, P12).

Patrón H · Acumulador, contador, máximo y mínimo

Cuatro cosas que se calculan recorriendo una lista:

suma ← 0                       contador ← 0
PARA CADA x EN lista           PARA CADA x EN lista
    suma ← suma + x                SI x cumple la condición ENTONCES
FIN PARA                               contador ← contador + 1
                                   FIN SI
                               FIN PARA

máximo ← primer elemento
PARA CADA x EN lista
    SI x > máximo ENTONCES máximo ← x
FIN PARA

Fíjate en el valor inicial: la suma y el contador empiezan en 0; el máximo y el mínimo empiezan en el primer elemento de la lista (no en 0: si todas las notas fueran negativas, o todas fueran mayores que 0 al buscar el mínimo, fallaría).

Patrón I · Bucles anidados

Cuando el resultado tiene filas y columnas, piensa en dos bucles:

  • el exterior recorre las filas;
  • el interior recorre los elementos de cada fila.

La clave es descubrir de qué depende el número de vueltas del bucle interior. A veces es fijo; a veces depende de la fila en que estás (como en el triángulo de la P05).

Patrón J · «Cada N elementos» con el módulo

El operador % da el resto de una división. Sirve para hacer algo cada N elementos: si numeras desde 1, i % N == 0 es cierto en el elemento N, 2N, 3N…, y (i - 1) % N == 0 es cierto en el elemento 1, N+1, 2N+1… Úsalo en la P06 para saber cuándo abrir y cuándo cerrar una fila.


3. Fichas de las prácticas

Cada ficha tiene la misma estructura: qué tienes y qué quieres, estrategia (los trozos en orden), preguntas para el cuaderno, casos de prueba y errores típicos.


P01 · Formulario de búsqueda de canciones

Patrones: B, E, F · Plantillas: ut03p01.html (ya hecho) y ut03p01-res.php

Entrada Proceso Salida
texto, buscar_en, genero por POST Traducir buscar_en a un texto legible Lista con los tres datos y botón Nueva búsqueda

Estrategia

  1. En ut03p01-res.php, antes del DOCTYPE, comprueba si se ha llegado por POST. Si no, redirige al formulario (patrón B). Pruébalo abriendo ut03p01-res.php directamente.
  2. Recoge los tres campos. Usa el operador ?? para dar un valor por defecto si alguno no llega.
  3. Traduce el valor interno de buscar_en (cancion, album, ambos) a un texto con match (patrón F). Piensa qué hace match si llega un valor que no está en la lista: añade un caso default.
  4. Sustituye los textos de ejemplo de la plantilla por tus variables, escapadas (patrón E).

Casos de prueba

Caso Resultado esperado
rock and roll, Ambos, Rock Se muestran los tres datos
Escribe <b>hola</b> en el texto Se ve literalmente <b>hola</b>, no «hola» en negrita
Abre ut03p01-res.php en una pestaña nueva Vuelves al formulario
Errores típicos
  • «Cannot modify header information – headers already sent»: hay algo (HTML, un espacio, una línea en blanco) antes de header(). Pon el bloque PHP en la primera línea del fichero.
  • Olvidar exit después de header(): el resto de la página se sigue ejecutando.

P02 · Catálogo de controles de formulario

Patrones: B, E · Plantillas: ut03p02.php (ya hecho) y ut03p02-res.php

Entrada Proceso Salida
9 controles distintos, uno de ellos file Recoger cada uno según su tipo Tabla con lo que llega de cada control

Estrategia

  1. Antes de programar nada, envía el formulario con var_dump($_POST) y var_dump($_FILES) en la página de resultados. Mira cómo llega cada control: ¿es una cadena?, ¿un array?, ¿no aparece?
  2. Apunta en el cuaderno, para cada control, si llega como cadena o como array, y qué pasa si no lo rellenas.
  3. Recoge uno a uno. Los que llegan como array (extras[], idiomas[]) se recogen con ?? [] y se muestran unidos con implode().
  4. Para el fichero, todo está en $_FILES['fichero']: busca en la teoría (apartado 4.3.1) qué claves tiene (name, size, type, error…). Comprueba antes si se ha subido algo.
  5. La contraseña no se muestra: genera tantos • como caracteres tenga (str_repeat() y strlen()), o un número fijo.
  6. El comentario se escapa primero y después se pasa por nl2br(). Piensa por qué el orden importa.

Casos de prueba

Caso Resultado esperado
Rellenar todo Cada fila muestra su valor
No marcar ningún checkbox ni idioma Aparece un texto como «Ninguno», sin warnings
No elegir fichero «No se ha enviado ningún fichero», sin warnings
Comentario de tres líneas Se ven tres líneas
Errores típicos
  • Undefined array key "extras": si no se marca ningún checkbox, la clave no existe. Usa ?? [].
  • $_FILES vacío: falta enctype="multipart/form-data" en el <form>.
  • Si haces nl2br() antes de htmlspecialchars(), los <br> se escapan y se ven como texto.

P03 · Calculadora de la letra del DNI

Patrones: A, B, C, D, E · Plantilla: ut03p03.php (autoprocesada)

Entrada Proceso Salida
numero por POST Validar entero positivo; calcular el resto entre 23; buscar la letra DNI de 8 cifras con su letra, o un error

Estrategia

  1. Cuaderno primero: haz el cálculo con tu DNI (lo pide el enunciado). Divide entre 23, apunta el resto y busca esa posición en la cadena TRWAGMYFPDXBNJZSQVHLCKE. Ojo: la primera letra es la posición 0.
  2. Escribe la función calcularLetraDNI() y pruébala sola, sin formulario, con un echo y un número fijo. Cuando dé Z para 12345678, sigue.
  3. Monta la zona de lógica (patrón A): inicializa $error, $numero y $resultado.
  4. Si se ha enviado (patrón B), valida con FILTER_VALIDATE_INT y min_range (patrón C).
  5. Si es válido, llama a tu función y construye el DNI completo rellenando con ceros (str_pad(), con STR_PAD_LEFT).
  6. En el HTML, muestra el error solo si existe y el resultado solo si existe. Haz el campo persistente (patrón D).
  7. Botón Borrar: piensa por qué un enlace a la propia página la deja vacía (es una petición GET).

Pregunta para el cuaderno

Un DNI tiene como máximo 8 cifras. ¿Deberías aceptar 123456789? Decide qué haces y añade la regla a la validación.

Casos de prueba

Entrada Resultado esperado
12345678 12345678Z
1 00000001R
87654321 87654321X
0, -5, abc, 12.5, vacío Mensaje de error
Errores típicos
  • Coger la letra con resto + 1 o resto - 1: las cadenas empiezan en la posición 0.
  • if (!$numero) para detectar errores: falla con el número 0. Usa === false.

P04 · Ecuación de segundo grado

Patrones: A, B, C, D · Plantilla: ut03p04.php

Entrada Proceso Salida
a, b, c Validar; calcular discriminante; decidir el caso Dos soluciones con 2 decimales, o «no hay soluciones reales»

Estrategia

  1. Cuaderno: escribe la fórmula x = (−b ± √(b² − 4ac)) / 2a y resuelve a mano el ejemplo del enunciado.
  2. Valida los tres coeficientes con FILTER_VALIDATE_FLOAT (patrón C). Después, comprueba aparte que a no es 0. ¿Por qué? Piensa qué pasa en la fórmula si a vale 0.
  3. Calcula el discriminante en una variable.
  4. Decide con un if: negativo → aviso; cero o positivo → dos soluciones (sqrt()).
  5. Formatea con number_format() (2 decimales, coma decimal).
  6. Haz persistentes los tres campos.
SI discriminante < 0 ENTONCES
    resultado ← "sin soluciones reales"
SI NO
    x1 ← (−b + raíz(discriminante)) / (2·a)
    x2 ← (−b − raíz(discriminante)) / (2·a)
FIN SI

Casos de prueba

a b c Resultado esperado
1 −5 6 x₁ = 3,00 · x₂ = 2,00
2 −7 3 x₁ = 3,00 · x₂ = 0,50
1 2 1 x₁ = −1,00 · x₂ = −1,00 (discriminante 0)
1 0 1 No hay soluciones reales
0 3 2 Error: a no puede ser 0
hola 1 1 Error: no es un número
Errores típicos
  • Escribir -b + sqrt($d) / 2 * $a: sin paréntesis, PHP divide antes de sumar y multiplica por a en vez de dividir. Pon paréntesis en el numerador y en el denominador.
  • Con FILTER_VALIDATE_FLOAT, un 0 es válido pero se evalúa como falso. Compara con === false.

P05 · Dibuja un triángulo

Patrones: A, C, D, I · Plantilla: ut03p05.php

Entrada Proceso Salida
altura (1–30), caracter Dos bucles anidados que construyen las filas El triángulo dentro de <pre>

Estrategia

  1. Cuaderno: dibuja el triángulo de altura 4 y numera las filas. Responde: en la fila 1, ¿cuántos caracteres hay?, ¿y en la 2?, ¿y en la fila i? Ahí tienes el límite del bucle interior.
  2. Haz la traza que pide el enunciado para altura 3.
  3. Valida la altura con FILTER_VALIDATE_INT, min_range 1 y max_range 30. Si el carácter llega vacío, usa *.
  4. Construye todo el dibujo en una variable (en la zona de lógica), concatenando, y muéstrala en el <pre> al final (patrón A).
dibujo ← ""
PARA fila DESDE 1 HASTA altura
    PARA columna DESDE 1 HASTA ¿…?        ← lo has deducido en el cuaderno
        añadir carácter y un espacio a dibujo
    FIN PARA
    añadir salto de línea a dibujo
FIN PARA
Para ir más allá

Cuando funcione, intenta el triángulo invertido (la fila 1 es la más larga) o una pirámide centrada. Solo cambian los límites de los bucles… y en la pirámide aparece un tercer bucle para los espacios.

Casos de prueba

Entrada Resultado esperado
Altura 4, * El triángulo del enunciado
Altura 1 Un solo carácter
Altura 30 30 filas, la última con 30 caracteres
Altura 0, 31, abc Error
Carácter < Se dibuja < (escápalo)
Errores típicos
  • Las filas salen todas iguales: el bucle interior no depende de la fila.
  • Sale todo en una sola línea: falta el salto de línea "\n" al terminar cada fila, o no estás dentro de <pre>.
  • El salto de línea sale como \n literal: lo has escrito entre comillas simples.

P06 · Tabla numerada dinámica

Patrones: A, C, D, J · Plantilla: ut03p06.php

Entrada Proceso Salida
num_casillas, casillas_por_fila Un bucle de 1 a total; abrir y cerrar filas con %; rellenar la última Tabla HTML

Estrategia

  1. Cuaderno: dibuja la tabla para 12 casillas y 5 por fila. Rodea con un círculo los números donde empieza cada fila (1, 6, 11) y los números donde acaba (5, 10). ¿Qué tienen en común respecto al 5? (patrón J).
  2. Escribe primero un bucle que genere todas las celdas sin filas. Comprueba que salen los 12 números.
  3. Añade la apertura de <tr>: ¿en qué números se abre una fila?
  4. Añade el cierre de </tr>: ¿en qué números se cierra?
  5. Al terminar el bucle, mira si la última fila quedó abierta. Si es así, calcula cuántas celdas vacías faltan, añádelas y cierra la fila.

Pregunta para el cuaderno

Con 12 casillas y 5 por fila faltan 3 celdas. Con 10 casillas y 5 por fila no falta ninguna. Escribe una expresión con % que dé el número de celdas que faltan y comprueba que funciona en los dos casos.

Casos de prueba

Total Por fila Resultado esperado
12 5 3 filas; la última: 11, 12 y tres vacías
10 5 2 filas completas, sin celdas vacías
3 5 1 fila: 1, 2, 3 y dos vacías
7 1 7 filas de una celda
0, −2, abc — Error
Errores típicos
  • Una fila vacía de más al final: cierras la fila y luego, al rellenar, la vuelves a cerrar. Rellena solo si la última fila quedó incompleta.
  • Abre el inspector del navegador (F12) y mira el HTML generado: es la forma más rápida de ver si los <tr> cuadran.

P07 · Conversor de divisas

Patrones: C, D, F, G · Plantilla: ut03p07.php

Entrada Proceso Salida
cantidad, de, a Pasar a euros y de euros a destino, usando $tasas 340.00 EUR = 368.90 USD

Estrategia

  1. Cuaderno: las tasas dicen «cuántas unidades de esa divisa vale 1 euro». Resuelve a mano: ¿cuántos euros son 100 USD?, ¿y cuántos yenes son esos euros? Deduce la regla: para pasar a euros, ¿multiplicas o divides?
  2. Crea el array $tasas (patrón F).
  3. Valida la cantidad (número no negativo) y comprueba que de y a son claves de $tasas (patrón G).
  4. Calcula en dos pasos, con una variable intermedia para los euros. Así cada paso se entiende y se puede comprobar.
  5. Formatea con sprintf() y %.2f.
  6. Genera las <option> de los select con un foreach sobre $tasas y añade selected a la elegida (patrón D). Así no repites el código en los dos select.
euros ← cantidad / tasa[de]
resultado ← euros · tasa[a]

Casos de prueba

Entrada Resultado esperado
340 EUR → USD 340.00 EUR = 368.90 USD
100 USD → JPY 100.00 USD = 14746.54 JPY
1000 JPY → EUR 1000.00 JPY = 6.25 EUR
50 EUR → EUR 50.00 EUR = 50.00 EUR
Cantidad abc Error
Cambia con F12 el value de una opción a GBP Error de divisa no válida, sin warnings

P08 · La quiniela

Patrones: B, E, G, H · Plantillas: ut03p08.php y ut03p08-res.php

Entrada Proceso Salida
Array partido[1..10] con 1, X o 2 Simular cada partido, comparar, contar aciertos Tabla de 10 filas y total de aciertos

Estrategia

Formulario

  1. Coge la fila modelo de la plantilla y métela en un for de 1 a 10. Sustituye el número fijo por la variable del bucle en todos los sitios donde aparece (texto, name, id, for).
  2. Comprueba con F12 que los name quedan como partido[1], partido[2]… y que los id no se repiten.

Resultados

  1. Redirige si no se llega por POST (patrón B).
  2. Recoge el array partido con ?? [] y haz var_dump() para ver su forma.
  3. Prepara un array con los tres signos válidos. Te sirve para dos cosas: validar (patrón G) y simular el resultado eligiendo una posición al azar con rand().
  4. Recorre de 1 a 10. En cada vuelta: tu apuesta (o «SIN MARCAR» si no existe o no es válida), el resultado simulado y si coinciden. Si coinciden, suma un acierto (patrón H).
  5. Guarda cada fila en un array en la zona de lógica y, en el HTML, recórrelo con foreach para pintar la tabla (patrón A).

Casos de prueba

Caso Resultado esperado
Marcar los 10 10 filas; aciertos entre 0 y 10
No marcar ninguno 10 filas «SIN MARCAR», 0 aciertos, sin warnings
Cambiar con F12 un value a 3 Esa fila cuenta como no válida
Recargar la página de resultados Cambian los resultados simulados (es normal)

P09 · Formulario de alta validado con expresiones regulares

Patrones: A, C, D, E · Plantilla: ut03p09.php

Es la práctica más larga del nivel intermedio. No la abordes entera: hazla campo a campo.

Entrada Proceso Salida
nombre, documento, telefono, cp, email, clave, matricula Normalizar → validar → apuntar errores por campo Errores debajo de cada campo, o tabla de datos normalizados

Estrategia general

  1. Monta primero el esqueleto con un solo campo (el código postal, que es el más sencillo): recoger, normalizar, validar, mostrar el error debajo del campo y hacerlo persistente. Cuando funcione, copia la mecánica a los demás.
  2. Usa el array de errores con el nombre del campo como clave (patrón C). En el HTML, cada campo pregunta si existe su clave en $errores para añadir is-danger y el mensaje.
  3. Para cada campo sigue siempre el mismo orden: normalizar → validar → guardar el valor normalizado.

Cómo construir una expresión regular sin perderte

  1. Escribe en castellano la regla, trozo a trozo, de izquierda a derecha: «empieza por…, luego vienen…, y termina».
  2. Traduce cada trozo por separado: «una cifra» → \d; «exactamente 5» → {5}; «uno de estos caracteres» → […]; «esto o esto otro» → ( | ); «opcional» → ?.
  3. Ancla siempre con ^ al principio y $ al final. Sin anclas, 12345 también «encaja» dentro de abc12345xyz.
  4. Prueba la expresión aislada en una web como regex101.com (elige el modo PCRE, que es el de PHP) con los ejemplos válidos y no válidos de la tabla del enunciado, antes de ponerla en el código.

Pistas por campo

Campo Piensa en esto
Código postal Las dos primeras cifras van de 01 a 52. Divide ese rango en tramos que se puedan escribir con clases: ¿qué pasa si la primera cifra es 0?, ¿y si es 1, 2, 3 o 4?, ¿y si es 5? Cada tramo es una alternativa del grupo.
Teléfono Normaliza primero (quita espacios, guiones y puntos). Después, la expresión tiene un prefijo opcional y el número. Usa el grupo de captura de la pista del enunciado para quedarte solo con las 9 cifras.
DNI / NIE Normaliza (mayúsculas, sin guiones ni espacios). Una expresión comprueba el formato. Si encaja, calcula la letra con tu función de la P03 y compárala. Para el NIE, sustituye la X, Y o Z inicial por 0, 1 o 2 antes de calcular.
Nombre Las letras con tilde y la ñ no entran en [a-z]. Añádelas a la clase o usa \p{L} con el modificador u (UTF-8).
Correo filter_var() con FILTER_VALIDATE_EMAIL. Nada de expresiones regulares aquí.
Contraseña Cinco comprobaciones separadas (longitud, minúscula, mayúscula, número, símbolo). Cada una que falle añade su texto a una lista de requisitos pendientes. Al final, si la lista no está vacía, el error es esa lista unida con implode().
Matrícula Es opcional: si llega vacía, no hay error. Si no, normaliza y escribe la clase de consonantes enumerando las letras permitidas: el alfabeto sin A, E, I, O, U, Ñ ni Q.

Casos de prueba: usa directamente las columnas Válido y No válido de la tabla del enunciado, más estos:

Campo Entrada Resultado esperado
DNI 12.345.678-z Válido, se guarda 12345678Z
NIE X1234567L Válido
NIE X1234567A Error: letra incorrecta
Teléfono 0034-612.345.678 Válido, se muestra 612 345 678
CP 00123, 53000 Error
Contraseña abcdefgh Error que dice que faltan mayúscula, número y símbolo
Todo correcto — Tabla de datos; la contraseña no aparece
Errores típicos
  • Validar antes de normalizar: +34 612 345 678 falla porque tiene espacios.
  • Olvidar las anclas ^ y $.
  • Mostrar la contraseña en el value del campo después de enviar.
  • Las tildes no funcionan: falta el modificador u al final de la expresión (/…/u).

P10 · Gestión de calificaciones

Patrones: C, D, H · Plantilla: ut03p10.php

Entrada Proceso Salida
Arrays nombres[] y notas[] (4 cada uno) Validar parejas; crear nombre => nota; media, máximo, mínimo Resumen y tabla con Aprobado / Suspenso

Estrategia

  1. Envía el formulario con var_dump($_POST) y observa: llegan dos arrays paralelos. El alumno de la posición 0 de nombres tiene su nota en la posición 0 de notas.
  2. Recorre las posiciones de 0 a 3. En cada una, valida el nombre (no vacío tras trim()) y la nota (FILTER_VALIDATE_FLOAT con rango 0–10). Si falla, apunta el error indicando qué fila falla (patrón C). Si es correcta, añádela al array asociativo $alumnos.
  3. Escribe las tres funciones antes de usarlas y pruébalas con un array fijo escrito a mano, sin formulario.
    • calcular_media(): acumulador (patrón H), o array_sum() y count().
    • obtener_maximo() / obtener_minimo(): patrón H, pero recuerda también el nombre, no solo la nota. Recorre con foreach ($alumnos as $nombre => $nota).
  4. Solo si no hay errores, llama a las funciones y prepara los datos para el HTML.
  5. En la tabla, decide el estado y la clase de color con un if o con el operador ternario.
mejor_nombre ← primer nombre;  mejor_nota ← primera nota
PARA CADA (nombre, nota) EN alumnos
    SI nota > mejor_nota ENTONCES
        mejor_nota ← nota
        mejor_nombre ← nombre
    FIN SI
FIN PARA
devolver [nombre ← mejor_nombre, nota ← mejor_nota]

Casos de prueba

Datos Resultado esperado
Ana 7 · Luis 4,5 · Eva 9 · Pepe 6,5 Media 6,75 · Máx. Eva (9,00) · Mín. Luis (4,50) · Luis suspenso
Una nota 11 o -1 Error en esa fila
Un nombre vacío Error en esa fila
Nota 5 Aprobado (el límite es ≥ 5)
Errores típicos
  • Empezar el mínimo en 0: nunca encuentra una nota menor que 0. Empieza por el primer alumno (array_key_first() te da su nombre).
  • Escribir la nota con coma (7,5): FILTER_VALIDATE_FLOAT la rechaza. Decide si avisas al usuario o si cambias la coma por un punto antes de validar.
  • Para el caso de dos alumnos con el mismo nombre, mira la pregunta Para pensar del enunciado: ¿qué le pasa a un array asociativo si repites una clave?

P11 · Buscador de productos con filtros

Patrones: D, F, G · Plantillas: ut03p11.php, ut03p11-res.php y datos.php

Entrada Proceso Salida
categoria, precio_min, precio_max, solo_stock por GET Filtrar $productos con array_filter() Resumen de filtros y tabla de productos, o aviso

Estrategia

Formulario

  1. Incluye datos.php con require_once y comprueba con var_dump($categorias) que llega.
  2. Genera las <option> con un foreach sobre $categorias, después de la opción Todas.

Resultados

  1. Recoge los filtros de $_GET. Para cada uno, decide qué significa «no rellenado»: categoría vacía o Todas; precio vacío; checkbox sin marcar (no existe en $_GET).
  2. Valida la categoría con lista blanca contra $categorias (patrón G) y los precios como números.
  3. Antes de usar array_filter(), escribe la condición en pseudocódigo para un solo producto. Una buena estrategia es empezar suponiendo que el producto pasa y descartarlo si falla algún filtro activo:

    FUNCIÓN pasa_los_filtros(producto)
        SI hay filtro de categoría Y la categoría del producto es distinta ENTONCES devolver falso
        SI hay precio mínimo Y el precio del producto es menor ENTONCES devolver falso
        SI ¿… precio máximo …?
        SI ¿… solo con stock …?
        devolver verdadero
    FIN FUNCIÓN
    
  4. Convierte ese pseudocódigo en una función anónima y pásala a array_filter(). La función recibe un producto, pero los filtros están fuera de ella: por eso necesitas use (…) para «meterlos» dentro.

  5. Prepara los textos del resumen (por ejemplo, «Todas», «Sin límite»).
  6. En el HTML: si el array filtrado está vacío, aviso; si no, tabla con foreach. Precio con number_format() y €; stock en verde o Sin stock en rojo.

Casos de prueba

Filtros Productos esperados
Ninguno Los 9
Audio Auriculares Bluetooth y Altavoz portátil
Máximo 50 + solo stock Funda de silicona y Altavoz portátil
Informática + mínimo 200 Portátil 15" y Monitor 27"
Hogar + solo stock Robot aspirador
Mínimo 800 Ninguno: aviso
Categoría Juguetes escrita en la URL Error o se ignora el filtro, sin warnings
Errores típicos
  • Undefined variable $categoria dentro de la función anónima: falta use ($categoria, …).
  • Un precio mínimo de 0 desactiva el filtro sin querer: distingue «campo vacío» de «el número 0».
  • Las claves de array_filter() se conservan (pueden quedar 1, 3, 6…). Para recorrer con foreach no importa.

P12 · Reto: calculadora de esperanza de vida

Patrones: todos · Plantilla: ut03p12.php

Es el reto final: junta todo lo de la unidad. La clave está en diseñar bien el array antes de escribir una sola línea.

Entrada Proceso Salida
sexo, 4 preguntas de opción, altura, peso Validar con lista blanca; sumar ajustes; calcular IMC y buscar su tramo Total, desglose con signo y factor que más resta

Estrategia

  1. Diseña el array en el cuaderno. Dibuja su forma como un árbol antes de escribirlo en PHP. Por ejemplo:

    $preguntas
     ├── 'tabaco'
     │    ├── 'titulo'   → "¿Fumas o has fumado?"
     │    ├── 'ayuda'    → "Cuenta cigarrillos, puros y pipa."
     │    └── 'opciones'
     │         ├── 'nunca'      → [ 'texto' → "Nunca he fumado",   'ajuste' → 0 ]
     │         ├── 'ex_antes'   → [ 'texto' → "Lo dejé antes…",    'ajuste' → -1 ]
     │         └── …
     ├── 'actividad'
     │    └── …
     └── …
    

    Las claves de primer nivel serán los name de los radio; las claves de opciones, sus value. Respeta las que ya usa la plantilla (tabaco, nunca, ex_antes…). Haz lo mismo para la esperanza de vida base (hombre, mujer) y para los tramos del IMC (una lista ordenada de pares límite – ajuste).

  2. Escribe las funciones auxiliares h(), fmt() y marcado() y pruébalas aisladas. fmt() debe poner el signo + delante de los positivos.

  3. Genera el formulario desde el array: un foreach exterior para las preguntas (un fieldset cada una) y un foreach interior para las opciones (patrón I). Comprueba que, si añades una pregunta al array, aparece sin tocar el HTML.
  4. Valida con lista blanca: para cada pregunta, la opción recibida debe ser una clave de sus opciones (patrón G). Altura y peso, con rango. Acumula todos los errores (patrón C) y, si algo falla, usa un valor por defecto.
  5. Calcula:
    • Parte de la esperanza base según el sexo.
    • Recorre las preguntas: busca el ajuste de la opción elegida, súmalo al total y guarda una entrada en un array $desglose (factor, opción elegida, ajuste).
    • IMC: convierte la altura a metros. Recorre los tramos en orden y quédate con el primero cuyo límite sea mayor que el IMC. Añade su ajuste al total y al desglose.
    • Factor que más resta: recorre $desglose buscando el ajuste mínimo (patrón H).
  6. Muestra el resultado y el desglose con foreach, eligiendo la clase de color según el signo del ajuste.

Pregunta para el cuaderno

¿Qué pasa si ningún factor resta (todos los ajustes son 0 o positivos)? Decide qué mensaje muestras en ese caso.

Casos de prueba

Datos Resultado esperado
Mujer · nunca · 150–449 min · 0–10 · mediterránea · 172 cm, 70 kg (IMC 23,7) 86,5 + 1,6 + 2,5 = 90,6 años · no resta nada
Hombre · fuma · < 15 min · 21–35 · procesada · 175 cm, 95 kg (IMC 31,0) 81,4 − 10 − 1,8 − 1,5 − 2 − 3 = 63,1 años · lo que más resta: tabaco (−10,0)
Altura 100 y peso 300 Dos errores mostrados juntos
Cambiar con F12 un value a mucho Error de opción no válida
Errores típicos
  • Tramos del IMC desordenados: el primero que cumpla la condición no es el correcto. Ordénalos de menor a mayor límite.
  • El último tramo (IMC ≥ 40) no tiene límite superior: usa un límite muy grande, por ejemplo INF.
  • El formulario no recuerda lo elegido: marcado() compara con el valor recibido, pero en la primera visita no hay nada recibido. Decide qué opción va marcada por defecto.

4. Lista de comprobación antes de entregar

Repasa esta lista con cada práctica antes de hacer el commit final:

  • He hecho en el cuaderno la tabla E-P-S y el pseudocódigo (y la traza, si se pide).
  • La página funciona la primera vez que se abre, sin warnings.
  • He probado los casos normales, límite y erróneos de la ficha.
  • Todo lo que viene del usuario se muestra con htmlspecialchars().
  • Los números se validan con filter_input() o filter_var().
  • Los valores de radio, select y checkbox se comprueban con lista blanca.
  • El formulario conserva los datos (salvo la contraseña).
  • No hay cálculos en mitad del HTML: la lógica está arriba.
  • El código tiene comentarios que explican qué hace cada bloque.
  • He borrado los var_dump() de depuración.
  • He hecho el commit con un mensaje claro.