Procesamiento de formularios PHP: Guía completa
Aprende a manejar formularios en PHP: recibir datos con $_POST y $_GET, validar entradas, evitar XSS e inyección SQL con ejemplos prácticos.
El manejo de formularios es una de las tareas más comunes en cualquier aplicación web: una página de registro, un formulario de contacto, un cuadro de búsqueda o un flujo de pago envían la entrada del usuario al servidor, donde PHP la lee, la verifica y actúa en consecuencia. Esta guía recorre el ciclo completo — desde el formulario HTML en el navegador hasta el PHP que recibe, valida y reutiliza de forma segura los datos enviados.
Al finalizar, comprenderás cómo funcionan $_POST y $_GET, cómo validar la entrada en el servidor, cómo mantener el formulario "sticky" tras un error y cómo evitar los dos agujeros de seguridad clásicos (XSS e inyección SQL).
Cómo funciona el envío de un formulario
El envío de un formulario es una única solicitud HTTP del navegador al servidor:
- El usuario rellena los campos y hace clic en Enviar.
- El navegador empaqueta los valores de los campos según el
methoddel formulario y los envía a la URL indicada en elaction. - PHP en el servidor recibe los valores en un array superglobal —
$_POSTo$_GET— y tu script los procesa.
Dado que los datos provienen del navegador del usuario, nunca puedes confiar en ellos. Cualquiera puede modificar los valores de los campos, eliminar campos o enviar la solicitud sin usar tu formulario. La validación en el servidor no es opcional — los atributos required y type="email" del lado del cliente son solo una comodidad.
Creación de un formulario HTML
Todo formulario comienza en HTML. El formulario contiene campos de entrada — cuadros de texto, campos de correo electrónico, botones de opción, casillas de verificación — y un botón de envío. Dos atributos lo controlan todo:
action— la URL que recibe los datos (omítela para reenviar a la misma página).method—postoget(consulta la comparación a continuación).
<form action="form_processing.php" method="post">
<p>
<label for="name">Name:</label>
<input type="text" id="name" name="name" />
</p>
<p>
<label for="email">Email:</label>
<input type="email" id="email" name="email" />
</p>
<input type="submit" value="Submit" />
</form>El atributo name de cada campo es la clave que lees en el servidor: name="email" se convierte en $_POST['email']. Un campo sin name nunca se envía. Siempre empareja cada campo con una <label> para garantizar la accesibilidad.
POST vs. GET
El atributo method decide cómo viajan los datos y qué array los recibe.
method="post" | method="get" | |
|---|---|---|
| Ubicación de los datos | Cuerpo de la solicitud HTTP | Se añaden a la URL como cadena de consulta |
| Array PHP | $_POST | $_GET |
| Visible en la URL | No | Sí (?name=Ann&email=...) |
| Marcable / compartible | No | Sí |
| Adecuado para | Contraseñas, todo lo que modifica datos | Filtros de búsqueda, paginación — solicitudes de solo lectura |
Usa POST para todo lo que crea, actualiza o elimina datos, o que contiene valores sensibles. Usa GET para búsquedas y filtros que quieras que los usuarios puedan marcar como favoritos. Ambos llegan al superglobal correspondiente; el resto de esta guía usa POST.
Lectura de los datos enviados
Cuando el formulario hace un POST, PHP rellena $_POST con los nombres de los campos como claves y el texto enviado como valores. Lee cada campo, pero protégete contra claves faltantes con el operador de fusión null ?? para que un campo ausente devuelva una cadena vacía en lugar de una advertencia:
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';Comprueba $_SERVER['REQUEST_METHOD'] para que el código de procesamiento solo se ejecute en un envío real, no cuando la página se abre por primera vez:
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
// ...validate and use the values
}Validación de la entrada
La validación responde una pregunta: ¿es aceptable este valor? Acumula cada problema en un array $errors para poder mostrarle al usuario todos los errores a la vez, en lugar de detenerte en el primero. Usa filter_var() con FILTER_VALIDATE_EMAIL para verificar correctamente el formato del correo electrónico:
$errors = [];
if (trim($name) === '') {
$errors[] = "Name is required";
}
if (trim($email) === '') {
$errors[] = "Email is required";
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "Invalid email format";
}
if (!empty($errors)) {
foreach ($errors as $error) {
echo htmlspecialchars($error) . "<br>";
}
}Usamos trim() para que un campo que solo contiene espacios siga contando como vacío, y luego filter_var() para confirmar que el correo tiene la forma [email protected]. Si $errors no está vacío, se muestran los mensajes; de lo contrario, los datos están limpios y listos para usar. Para una cobertura más profunda sobre campos obligatorios y reglas de correo/URL, consulta validación de formularios PHP y validación de campos obligatorios.
Escapar la salida: prevenir XSS
Fíjate en la llamada a htmlspecialchars() anterior. Siempre que imprimas un valor que proviene del usuario, debes escaparlo, o un atacante puede enviar etiquetas <script> que se ejecuten en los navegadores de otros visitantes — un ataque de cross-site scripting (XSS). htmlspecialchars() convierte <, >, & y las comillas en entidades HTML inofensivas:
$dangerous = '<script>alert("xss")</script>';
echo htmlspecialchars($dangerous);
// <script>alert("xss")</script>El navegador ahora muestra el texto literalmente en lugar de ejecutarlo. Escapa siempre en la salida, sin excepción.
Mantener el formulario sticky
Si la validación falla, vuelve a mostrar el formulario con los valores del usuario ya rellenos — tener que volver a escribir todo es frustrante. Devuelve cada valor en su atributo value, escapado:
<input
type="text"
name="name"
value="<?php echo htmlspecialchars($name ?? ''); ?>"
/>Como la misma página renderiza el formulario y lo procesa, establece action="" (reenviar a sí misma) y el campo se repopula automáticamente tras un envío fallido.
Poniéndolo todo junto
Aquí tienes una única página de autoprocesamiento que renderiza el formulario, valida en POST, muestra errores, mantiene el formulario sticky y confirma el éxito — el patrón que siguen la mayoría de los formularios de contacto reales:
<?php
$name = $email = '';
$errors = [];
$success = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
if ($name === '') {
$errors[] = 'Name is required';
}
if ($email === '') {
$errors[] = 'Email is required';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Invalid email format';
}
if (!$errors) {
$success = true; // here you would save to a database or send an email
}
}
?>
<?php if ($success): ?>
<p>Thanks, <?php echo htmlspecialchars($name); ?>!</p>
<?php else: ?>
<?php foreach ($errors as $e): ?>
<p style="color:red"><?php echo htmlspecialchars($e); ?></p>
<?php endforeach; ?>
<form action="" method="post">
Name:
<input type="text" name="name"
value="<?php echo htmlspecialchars($name); ?>"><br>
Email:
<input type="email" name="email"
value="<?php echo htmlspecialchars($email); ?>"><br>
<input type="submit" value="Submit">
</form>
<?php endif; ?>Almacenar datos de forma segura: prevenir la inyección SQL
Una vez que los datos son válidos, a menudo los guardas en una base de datos. Nunca insertes la entrada del usuario directamente en una cadena SQL — eso abre la puerta a la inyección SQL. Usa una sentencia preparada para que la base de datos trate los valores como datos, nunca como comandos:
$mysqli = new mysqli('localhost', 'user', 'pass', 'app');
$stmt = $mysqli->prepare(
'INSERT INTO contacts (name, email) VALUES (?, ?)'
);
$stmt->bind_param('ss', $name, $email);
$stmt->execute();Los marcadores de posición ? y bind_param() mantienen los valores separados de la consulta, por lo que una entrada como '; DROP TABLE contacts; -- se almacena como texto inofensivo en lugar de ejecutarse.
Lista de verificación de seguridad
Un script de procesamiento de formularios seguro sigue una rutina breve y repetible:
- Valida cada campo en el servidor — nunca confíes solo en las comprobaciones del navegador.
- Escapa en la salida con
htmlspecialchars()para detener el XSS. - Usa sentencias preparadas para cada consulta a la base de datos para detener la inyección SQL.
- Hashea las contraseñas con
password_hash()antes de almacenarlas — nunca almacenes texto plano. - Añade un token CSRF a los formularios que cambian estado para que las solicitudes no puedan falsificarse desde otros sitios.
- Añade CAPTCHA a los formularios públicos para reducir los envíos automáticos de spam.
Capítulos relacionados
- Manejo de formularios PHP — los conceptos básicos para recibir datos de formularios.
- PHP $_POST — el superglobal en profundidad.
- Validación de formularios PHP — patrones de validación completos.
- Superglobales PHP —
$_POST,$_GET,$_SERVERy más.