1. Contenidos
1.1 El formulario HTML
Un formulario HTML es un conjunto de controles que permite al usuario introducir datos y enviarlos al servidor. El elemento principal es <form>, que agrupa los campos y define cómo y a dónde se envían.
action: URL del script que procesará los datos.
method: método HTTP de envío, normalmente get o post.
enctype: tipo de codificación, necesario para subir archivos (multipart/form-data).
- Cada control debe tener un atributo
name que actuará como clave en el array recibido.
Ejemplo de formulario básico
<form action="procesar.php" method="post">
<label for="nombre">Nombre:</label>
<input type="text" id="nombre" name="nombre" />
<label for="edad">Edad:</label>
<input type="number" id="edad" name="edad" />
<input type="submit" value="Enviar" />
</form>
Documentación oficial: Formularios (php.net).
1.2 Métodos GET y POST
PHP recibe los datos del formulario a través de superglobales, dependiendo del método utilizado:
- GET: los datos se envían como parte de la URL (cadena de consulta). Son visibles, se pueden marcar y tienen un límite de tamaño. Adecuado para búsquedas o filtros.
- POST: los datos se envían en el cuerpo de la petición. No son visibles en la URL y no tienen un límite tan estricto. Adecuado para envío de datos sensibles o modificaciones.
Formulario con GET
<form action="buscar.php" method="get">
<input type="text" name="q" />
<input type="submit" value="Buscar" />
</form>
Al enviarlo, la URL resultante sería similar a buscar.php?q=php.
Formulario con POST
<form action="login.php" method="post">
<input type="text" name="usuario" />
<input type="password" name="clave" />
<input type="submit" value="Entrar" />
</form>
Importante: nunca se deben utilizar enlaces GET para operaciones que modifiquen datos (borrar, actualizar), porque podrían ejecutarse por error al recargar o al compartir la URL.
Documentación oficial: $_GET (php.net) y $_POST (php.net).
1.3 Recepción de datos en PHP
Los datos enviados por un formulario se reciben en PHP mediante las superglobales $_GET y $_POST, que son arrays asociativos cuyas claves coinciden con los atributos name de los controles.
Ejemplo de recepción con POST
<?php
// procesar.php
$nombre = $_POST['nombre'] ?? '';
$edad = $_POST['edad'] ?? '';
echo "Hola, $nombre. Tienes $edad años.";
?>
Es recomendable comprobar si un campo ha sido enviado antes de usarlo, bien con isset(), bien con el operador ??.
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['nombre'])) {
$nombre = $_POST['nombre'];
echo "Nombre recibido: $nombre";
}
}
?>
La variable $_SERVER['REQUEST_METHOD'] permite saber si la petición es GET o POST, lo cual es útil para procesar el formulario solo cuando se ha enviado.
Documentación oficial: $_SERVER (php.net).
1.4 Validación y saneamiento de datos
Los datos recibidos de un formulario nunca son de confianza. Antes de utilizarlos, es imprescindible validarlos (comprobar que cumplen las reglas esperadas) y sanearlos (eliminar o escapar caracteres peligrosos).
trim(): elimina espacios al principio y al final.
htmlspecialchars(): escapa caracteres especiales para evitar inyección de HTML.
filter_var(): valida y sanea según un filtro (email, URL, entero, etc.).
is_numeric(), is_string(), etc.: comprueban el tipo.
preg_match(): valida con expresiones regulares.
Ejemplo de validación
<?php
$email = trim($_POST['email'] ?? '');
$edad = $_POST['edad'] ?? '';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "El correo no es válido.";
} elseif (!is_numeric($edad) || $edad < 0 || $edad > 120) {
echo "La edad no es válida.";
} else {
// Uso seguro del dato
$emailSeguro = htmlspecialchars($email);
echo "Datos correctos: $emailSeguro, $edad años.";
}
?>
La función filter_var() admite numerosos filtros: FILTER_VALIDATE_INT, FILTER_VALIDATE_FLOAT, FILTER_VALIDATE_URL, FILTER_SANITIZE_STRING (obsoleto desde PHP 8.1), FILTER_SANITIZE_EMAIL, entre otros.
Documentación oficial: filter_var (php.net) y Filtros (php.net).
1.5 Campos de texto y contraseña
Los campos <input type="text"> y <textarea> envían cadenas de texto. Los campos <input type="password"> también envían texto, pero ocultan los caracteres en pantalla.
Ejemplo
<form action="registro.php" method="post">
<label for="usuario">Usuario:</label>
<input type="text" id="usuario" name="usuario" />
<label for="clave">Contraseña:</label>
<input type="password" id="clave" name="clave" />
<label for="comentario">Comentario:</label>
<textarea id="comentario" name="comentario"></textarea>
<input type="submit" value="Registrar" />
</form>
<?php
$usuario = trim($_POST['usuario'] ?? '');
$clave = $_POST['clave'] ?? '';
$comentario = trim($_POST['comentario'] ?? '');
// La contraseña debe tratarse con password_hash(), nunca mostrarse
$hash = password_hash($clave, PASSWORD_DEFAULT);
echo "Usuario: " . htmlspecialchars($usuario) . "\n";
echo "Comentario: " . htmlspecialchars($comentario) . "\n";
?>
Seguridad: las contraseñas nunca deben almacenarse en texto plano. Se debe usar password_hash() para guardarlas y password_verify() para comprobarlas.
Documentación oficial: password_hash (php.net) y password_verify (php.net).
1.6 Casillas de verificación y botones de opción
Las casillas (checkbox) permiten seleccionar varias opciones y los botones de opción (radio) solo una. Para que varios radios formen un grupo excluyente, deben compartir el mismo name.
Ejemplo con checkbox y radio
<form action="preferencias.php" method="post">
<p>Aficiones:</p>
<label><input type="checkbox" name="aficiones[]" value="leer" /> Leer</label>
<label><input type="checkbox" name="aficiones[]" value="musica" /> Música</label>
<label><input type="checkbox" name="aficiones[]" value="deporte" /> Deporte</label>
<p>Turno:</p>
<label><input type="radio" name="turno" value="manana" /> Mañana</label>
<label><input type="radio" name="turno" value="tarde" /> Tarde</label>
<input type="submit" value="Guardar" />
</form>
Al usar name="aficiones[]", PHP recibe un array con las opciones marcadas.
<?php
$aficiones = $_POST['aficiones'] ?? [];
$turno = $_POST['turno'] ?? '';
echo "Aficiones seleccionadas:\n";
foreach ($aficiones as $a) {
echo "- " . htmlspecialchars($a) . "\n";
}
echo "Turno: " . htmlspecialchars($turno) . "\n";
?>
Documentación oficial: Preguntas frecuentes sobre formularios (php.net).
1.7 Listas desplegables y selección múltiple
El elemento <select> genera una lista desplegable. Con el atributo multiple permite seleccionar varias opciones, y en ese caso el name debe terminar en [].
Ejemplo de select simple y múltiple
<form action="ciudades.php" method="post">
<label for="ciudad">Ciudad:</label>
<select id="ciudad" name="ciudad">
<option value="madrid">Madrid</option>
<option value="sevilla">Sevilla</option>
<option value="bilbao">Bilbao</option>
</select>
<label for="lenguajes">Lenguajes:</label>
<select id="lenguajes" name="lenguajes[]" multiple>
<option value="php">PHP</option>
<option value="java">Java</option>
<option value="python">Python</option>
</select>
<input type="submit" value="Enviar" />
</form>
<?php
$ciudad = $_POST['ciudad'] ?? '';
$lenguajes = $_POST['lenguajes'] ?? [];
echo "Ciudad: " . htmlspecialchars($ciudad) . "\n";
echo "Lenguajes: " . implode(", ", array_map('htmlspecialchars', $lenguajes)) . "\n";
?>
Documentación oficial: $_POST (php.net).
1.8 Subida de archivos
Para subir archivos, el formulario debe usar method="post" y enctype="multipart/form-data". El archivo se recibe en la superglobal $_FILES.
Formulario de subida
<form action="subir.php" method="post" enctype="multipart/form-data">
<label for="archivo">Selecciona un archivo:</label>
<input type="file" id="archivo" name="archivo" />
<input type="submit" value="Subir" />
</form>
Procesamiento en PHP
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_FILES['archivo']) && $_FILES['archivo']['error'] === UPLOAD_ERR_OK) {
$nombreTemporal = $_FILES['archivo']['tmp_name'];
$nombreOriginal = basename($_FILES['archivo']['name']);
$destino = __DIR__ . '/subidas/' . $nombreOriginal;
if (move_uploaded_file($nombreTemporal, $destino)) {
echo "Archivo subido correctamente: " . htmlspecialchars($nombreOriginal);
} else {
echo "Error al mover el archivo.";
}
} else {
echo "No se ha subido ningún archivo o ha habido un error.";
}
}
?>
El array $_FILES['archivo'] contiene las claves name, type, size, tmp_name y error. Es imprescindible validar el tamaño, el tipo y el nombre del archivo antes de guardarlo.
Seguridad: nunca se debe confiar en el nombre original del archivo. Conviene generar un nombre nuevo, comprobar la extensión y limitar el tamaño máximo permitido.
Documentación oficial: Subida de archivos (php.net) y $_FILES (php.net).
1.9 Autoprocesamiento del formulario
Es habitual que el mismo archivo que muestra el formulario sea el que lo procese. Para ello se comprueba el método de la petición y, si es POST, se procesan los datos; si no, se muestra el formulario.
Ejemplo de autoprocesamiento
<?php
$errores = [];
$nombre = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$nombre = trim($_POST['nombre'] ?? '');
if ($nombre === '') {
$errores[] = "El nombre no puede estar vacío.";
}
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Formulario con validación</title>
</head>
<body>
<?php if (!empty($errores)): ?>
<ul>
<?php foreach ($errores as $e): ?>
<li><?= htmlspecialchars($e) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php if ($_SERVER['REQUEST_METHOD'] === 'POST' && empty($errores)): ?>
<p>Hola, <?= htmlspecialchars($nombre) ?>. Formulario procesado correctamente.</p>
<?php else: ?>
<form action="" method="post">
<label for="nombre">Nombre:</label>
<input type="text" id="nombre" name="nombre" value="<?= htmlspecialchars($nombre) ?>" />
<input type="submit" value="Enviar" />
</form>
<?php endif; ?>
</body>
</html>
Este patrón evita tener que crear dos archivos distintos y permite mostrar los errores junto al formulario.
1.10 Buenas prácticas y seguridad
El tratamiento de formularios es uno de los puntos más sensibles de una aplicación web. Algunas recomendaciones básicas son:
- Validar siempre en el servidor, aunque también se valide en el cliente con JavaScript.
- Usar
htmlspecialchars() al mostrar datos introducidos por el usuario para evitar XSS.
- Escapar o parametrizar las consultas SQL para evitar inyección SQL.
- No confiar en el método de envío: comprobar
$_SERVER['REQUEST_METHOD'].
- Almacenar contraseñas con
password_hash().
- Limitar el tamaño y el tipo de los archivos subidos.
- Usar HTTPS para proteger los datos en tránsito.
Recuerda: el navegador puede ser manipulado. Cualquier dato que llegue al servidor debe ser tratado como potencialmente peligroso hasta que se demuestre lo contrario.
Documentación oficial: Seguridad en PHP (php.net).