FORMULARIOS, VALIDACIÓN, PRG Y FLASH MESSAGES EN PHP¶
Material de consulta para el Lab 06. Cubre los conceptos que aplicarás en el lab: enviar datos de cliente a servidor con formularios HTML, leerlos en PHP con $_GET y $_POST, validarlos en el servidor, evitar el problema del reenvío con el patrón PRG (Post-Redirect-Get), y mostrar mensajes de confirmación de una sola lectura con flash messages apoyados en sesiones de PHP.
Cliente y servidor: cómo viajan los datos¶
Hasta ahora has visto páginas en las que el servidor genera HTML y el navegador lo pinta. Con los formularios introducimos el flujo en sentido contrario: el navegador envía datos al servidor. Esa petición que el navegador envía con datos lleva siempre dos piezas clave:
- Un método HTTP: principalmente
GEToPOST. - Un conjunto de pares clave/valor: el nombre de cada campo del formulario y el valor que el usuario ha escrito o seleccionado.
PHP recoge esos pares automáticamente y los pone a tu disposición en dos arrays superglobales: $_GET y $_POST.
El método GET¶
Con method="get", los datos del formulario se añaden a la URL como querystring (la parte que va detrás del ?):
PHP los pone en $_GET:
El operador
??(que ya viste con los parámetros sueltos en la URL) sigue siendo igual de útil aquí: si el campo no se envió, te quedas con la cadena vacía y no salta el avisoUndefined index.
GET es adecuado para acciones que no modifican datos en el servidor: buscar, filtrar, navegar. La URL con los parámetros se puede compartir por chat, guardar como favorito y recargar tantas veces como quieras sin efectos secundarios. Por eso decimos que GET es idempotente: repetir la misma petición da el mismo resultado.
El método POST¶
Con method="post", los datos viajan en el cuerpo de la petición HTTP, no en la URL. PHP los pone en $_POST:
POST se usa para acciones que sí modifican datos: crear, editar, borrar. La URL no cambia, así que el formulario no se puede guardar como favorito ni compartir. Eso es deseable: no quieres que recargar la página del navegador vuelva a ejecutar la acción.
Comparación rápida¶
| GET | POST | |
|---|---|---|
| Datos en | URL (querystring) | Cuerpo de la petición |
| Visible en la barra de direcciones | Sí | No |
| Se puede marcar como favorito | Sí | No |
| Límite de tamaño | ~2000 caracteres | Sin límite práctico |
| Uso correcto | Buscar, filtrar, navegar | Crear, modificar, borrar |
| Idempotente | Sí | No |
Mala práctica: usar GET para modificar datos
Una página que borre algo accesible con un enlace tipo <a href="eliminar.php?id=42">Eliminar</a> parece cómoda, pero es un agujero. Cualquiera puede hacer un enlace así en otra web; basta con que tu usuario lo pulse mientras está logueado para borrar el recurso. Las acciones que modifican datos van siempre por POST, dentro de un <form>.
Detectar el método de la petición¶
Cuando un mismo fichero PHP sirve tanto la página vacía (GET inicial) como el procesamiento del formulario (POST con datos), necesitas saber con cuál de las dos peticiones estás:
El array $_SERVER es otra superglobal que PHP rellena automáticamente con información de la petición. REQUEST_METHOD vale literalmente 'GET' o 'POST'.
Validación en el servidor¶
Por qué la validación del cliente no basta¶
Un <input type="email" required> muestra un mensaje del navegador si el campo va vacío, pero eso es solo una ayuda visual: cualquiera puede abrir las DevTools y quitar el required o enviar la petición directamente con curl desde la terminal sin pasar por el HTML. La única validación que no se puede saltar es la que hace el servidor.
Regla: el navegador valida para mejorar la experiencia. El servidor valida para garantizar que los datos son correctos. Lo segundo es obligatorio; lo primero es opcional.
Patrón típico: recoger → sanear → validar → procesar¶
El flujo estándar tiene cuatro fases:
- Recoger del array correspondiente (
$_GET/$_POST). - Sanear con
trim()para quitar espacios al inicio y al final. - Validar y acumular errores en un array.
- Procesar si no hay errores, o re-mostrar el formulario si los hay.
El tercer argumento
truedein_array()activa la comparación estricta (===). Sin él,in_array('0', ['baja', 'media', 'alta'])daríatruepor las conversiones automáticas de PHP, y eso es lo que tratamos de evitar.
Re-mostrar el formulario conservando los valores¶
Si la validación falla, la página vuelve a mostrar el formulario. No conserves los valores en el value "porque sí": el alumno que rellena 8 campos y se le borran todos al fallar uno cierra la pestaña y no vuelve. Conserva siempre lo que escribió:
El value="<?= htmlspecialchars(...) ?>" repuebla el campo. El if (isset($errores[...])) muestra el mensaje de error junto al campo que lo provoca. htmlspecialchars() sigue siendo imprescindible: el usuario puede haber tecleado caracteres especiales (<, >, ").
Mala práctica: imprimir $_POST['campo'] directamente en el value
value="<?= $_POST['titulo'] ?>" parece más corto, pero deja la página abierta a inyectar HTML o JavaScript desde el formulario. Pasa siempre por htmlspecialchars(), igual que con cualquier otra salida que venga del usuario.
El patrón PRG (Post-Redirect-Get)¶
El problema que resuelve¶
Cuando un formulario POST se procesa y la página responde con el resultado, el usuario está viendo una URL que internamente "es" un POST. Si recarga, el navegador pregunta "¿Reenviar el formulario?": si dice que sí, se ejecuta otra vez la acción. En una página que crea registros, eso significa duplicados sin que el usuario haya hecho nada raro.
Cómo funciona PRG¶
PRG (Post - Redirect - Get) es un patrón de tres pasos que rompe el ciclo:
- El navegador envía el POST con los datos.
- El servidor procesa, valida y guarda. Si todo va bien, responde con una redirección HTTP 302 a una URL distinta.
- El navegador sigue la redirección haciendo un GET a esa URL. La página final que ve el usuario es la respuesta de ese GET.
A partir de ese momento, recargar la página solo repite el GET, que no modifica nada. El POST queda atrás.
Dos detalles que no son opcionales:
header('Location: ...')debe llamarse antes de enviar cualquier salida (antes del primerecho, antes del primerrequirede plantilla, antes de cualquier HTML suelto fuera de<?php ?>). Si ya se envió algo al navegador, PHP avisa con "Cannot modify header information - headers already sent" y la redirección no funciona.exit;justo después delheader(). Sin él, PHP sigue ejecutando el resto del script aunque el navegador vaya a redirigir, y eso puede ejecutar dos veces lógica que solo debía correr una.
Mala práctica: olvidar exit tras header('Location: ...')
El navegador ve la cabecera y redirige, pero el servidor sigue ejecutando código: puede llegar a guardar dos veces, registrar logs erróneos o lanzar errores en pasos posteriores que no esperaban llegar a ejecutarse. La pareja header(...) + exit; se escribe siempre junta.
Sesiones y $_SESSION¶
HTTP es un protocolo sin estado: cada petición es independiente y el servidor no recuerda nada de la anterior. Las sesiones de PHP son el mecanismo estándar para guardar datos asociados a un usuario entre peticiones.
Cuando llamas a session_start():
- PHP genera un identificador de sesión único y lo envía al navegador en una cookie llamada
PHPSESSID. - El navegador devuelve esa cookie en cada petición posterior.
- PHP usa esa cookie para cargar los datos asociados a esa sesión y los pone en
$_SESSION, que se comporta como un array normal.
session_start() tiene que llamarse antes de cualquier salida, por la misma razón que header(): PHP envía la cookie de sesión en las cabeceras HTTP, y si ya salió HTML al navegador, las cabeceras ya están cerradas.
Mala práctica: llamar a session_start() después de HTML
Si tienes HTML, un echo o incluso un espacio en blanco fuera de <?php ?> antes de session_start(), recibirás el mismo aviso de headers already sent. La línea va arriba del todo del script, sin nada delante.
Flash messages¶
Qué resuelven¶
Después de un POST + redirect, el usuario llega a la página de destino y no sabe si su acción tuvo efecto. Un flash message es un mensaje breve que se muestra una sola vez y desaparece: confirma la acción, avisa de un error de proceso, o informa de cualquier cosa que el usuario debe ver justo después de redirigir.
La mecánica:
- El paso 1 (el POST) guarda el mensaje en
$_SESSIONjusto antes del redirect. - El paso 3 (el GET de la URL destino) lee el mensaje, lo borra y lo pinta. Si el usuario recarga, el mensaje ya no está.
Guardar un flash antes de redirigir¶
Guardar el flash como array con tipo y msg permite estilarlo distinto según sea de éxito o de error (success, error, info...) sin tener que duplicar lógica.
Leer y borrar el flash en la página destino¶
unset() elimina la clave del array. Si se nos olvida, el mensaje aparecería en cada página hasta que la sesión expire.
Pintar el flash¶
Con dos clases CSS (flash-exito con fondo verde, flash-error con fondo rojo) tienes mensajes vistosos sin escribir lógica nueva.
Resumen del flujo completo¶
Un formulario que crea algo y avisa al usuario tiene este ciclo de cinco pasos:
- GET de la página del formulario → el servidor manda el
<form>vacío. - El usuario rellena los campos y envía con POST.
- El servidor sanea, valida y, si todo está bien, guarda los datos, guarda el flash en
$_SESSIONy responde con redirect (302) a la URL de destino. - El navegador hace GET de la URL de destino.
- La página destino lee y borra el flash, lo pinta una vez y muestra el contenido normal.
Si la validación falla en el paso 3, el servidor no redirige: re-pinta el formulario con los valores escritos y los errores marcados, listo para que el usuario corrija y reenvíe.