$_POST: valida en el servidor, escapa al imprimir y, tras guardar, redirige (patrón PRG)
para
evitar envíos duplicados.
Los formularios son la puerta de entrada de datos a cualquier aplicación: registros, logins, comentarios, pedidos. Y también son la puerta de entrada de la mayoría de los problemas de seguridad. En el libro Programación Full-Stack les dedicamos un capítulo completo porque todo lo que viene después (login, CRUD, tienda online) se apoya en procesarlos bien.
GET vs POST: cuándo usar cada uno
| GET | POST | |
|---|---|---|
| Dónde viajan los datos | En la URL: ?q=php&page=2 |
En el cuerpo de la petición |
| Se puede guardar como favorito | Sí | No |
| Queda en el historial y en los logs | Sí | No (la URL no lleva los datos) |
| Úsalo para | Buscar, filtrar, paginar | Crear, modificar, borrar, login |
Regla práctica: si la acción cambia algo en el servidor, usa POST. Y recuerda que POST no cifra nada: lo que protege los datos en tránsito es HTTPS.
Las superglobales de PHP
Son arrays que PHP llena automáticamente y que están disponibles en cualquier parte del código:
$_GET: parámetros de la URL.$_POST: campos enviados conmethod="post".$_FILES: archivos subidos (requiereenctype="multipart/form-data").$_SESSION: datos que persisten entre páginas para un mismo usuario.$_COOKIE: cookies que envía el navegador.$_SERVER: información de la petición: método, IP, URL, cabeceras.
La clave de cada campo es el atributo name del input, no su id.
Ejemplo completo: formulario de contacto
Un solo archivo que muestra el formulario, lo procesa y vuelve a mostrar los valores si hay errores:
<?php
$errores = [];
$nombre = '';
$email = '';
$mensaje = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 1. Leer con valor por defecto y quitar espacios
$nombre = trim($_POST['nombre'] ?? '');
$email = trim($_POST['email'] ?? '');
$mensaje = trim($_POST['mensaje'] ?? '');
// 2. Validar
if ($nombre === '' || mb_strlen($nombre) > 80) {
$errores['nombre'] = 'Escribe tu nombre (máx. 80 caracteres).';
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errores['email'] = 'El email no es válido.';
}
if (mb_strlen($mensaje) < 10) {
$errores['mensaje'] = 'El mensaje debe tener al menos 10 caracteres.';
}
// 3. Si todo está bien: procesar y redirigir
if (!$errores) {
// guardar en la base de datos, enviar email, etc.
header('Location: contacto.php?enviado=1');
exit;
}
}
function e(string $valor): string
{
return htmlspecialchars($valor, ENT_QUOTES, 'UTF-8');
}
?>
<?php if (isset($_GET['enviado'])): ?>
<p class="ok">¡Gracias! Te responderemos pronto.</p>
<?php endif; ?>
<form method="post" novalidate>
<label for="nombre">Nombre</label>
<input id="nombre" name="nombre" value="<?= e($nombre) ?>" required>
<?php if (isset($errores['nombre'])): ?>
<small class="error"><?= e($errores['nombre']) ?></small>
<?php endif; ?>
<label for="email">Email</label>
<input id="email" name="email" type="email" value="<?= e($email) ?>" required>
<?php if (isset($errores['email'])): ?>
<small class="error"><?= e($errores['email']) ?></small>
<?php endif; ?>
<label for="mensaje">Mensaje</label>
<textarea id="mensaje" name="mensaje" required><?= e($mensaje) ?></textarea>
<?php if (isset($errores['mensaje'])): ?>
<small class="error"><?= e($errores['mensaje']) ?></small>
<?php endif; ?>
<button type="submit">Enviar</button>
</form>
Puntos importantes del ejemplo:
- El operador
??evita el aviso “Undefined array key” si el campo no llegó. - Los valores se vuelven a imprimir en los inputs, así el usuario no pierde lo que escribió.
- La función
e()escapa todo lo que se imprime, incluidos los atributosvalue.
Validar y sanear: no son lo mismo
- Validar es comprobar que el dato cumple las reglas y rechazarlo si no: un email con formato correcto, una edad entre 18 y 120.
- Escapar es convertir el dato para un contexto concreto al usarlo: con
htmlspecialchars()al imprimir en HTML y con consultas preparadas al enviarlo a SQL.
Un error frecuente es “limpiar” los datos al recibirlos con strip_tags o
addslashes y guardarlos ya modificados. Es mejor guardar el dato original (validado) y
escaparlo en el momento de usarlo, según dónde se use. Para SQL, la respuesta siempre es la que vemos en
el artículo sobre inyección SQL.
// Validaciones útiles con filter_var
$edad = filter_var($_POST['edad'] ?? '', FILTER_VALIDATE_INT, [
'options' => ['min_range' => 18, 'max_range' => 120],
]);
if ($edad === false) {
$errores['edad'] = 'Edad no válida.';
}
$web = filter_var($_POST['web'] ?? '', FILTER_VALIDATE_URL);
// Listas cerradas: compara contra los valores permitidos
$paises = ['AR', 'MX', 'CO', 'CL', 'ES'];
if (!in_array($_POST['pais'] ?? '', $paises, true)) {
$errores['pais'] = 'Selecciona un país de la lista.';
}
El patrón PRG: adiós al “¿reenviar formulario?”
Si después de procesar un POST muestras directamente la página de éxito, al recargar el navegador volverá a enviar los datos: un comentario duplicado, un pedido doble. El patrón Post/Redirect/Get lo evita:
- Post: el formulario envía los datos.
- Redirect: el servidor los procesa y responde con
header('Location: ...'). - Get: el navegador carga la página de destino con una petición GET normal.
Es exactamente lo que hace el ejemplo con contacto.php?enviado=1. No olvides el
exit después de header(): sin él, el script sigue ejecutándose.
Errores comunes
- Confiar en la validación de HTML o JavaScript:
requiredytype="email"se saltan desde las DevTools en segundos. - “Headers already sent”: llamaste a
header()después de imprimir algo, aunque sea un espacio antes de<?php. - Usar
$_REQUEST: mezcla GET, POST y cookies. Sé explícito. - Formularios sin protección CSRF: para acciones sensibles, añade un token aleatorio guardado en la sesión y compruébalo al recibir el POST.
Formularios, sesiones y cookies a fondo
El libro Programación Full-Stack completa este tema con sesiones y cookies, y lo aplica en el sistema de login y en sus tres proyectos reales.
Comprar en Amazon