Formularios en PHP
Contenidos
Introducción
Un programa necesita interactuar con el usuario. Para ello dos de las típicas instruccines son:
- Leer valores proporcionados por el usuario.
- Mostrar los resultados obtenidos.
En PHP ya conocemos echo y print, que permiten generar contenido en la respuesta que el servidor envía al navegador. Además de estas primitivas hay otras funciones prìntf

Cómo leer datos del usuario
Estamos desarrollando una aplicación web: esto tiene implicaciones, como ya hemos comentado. el código PHP se ejecuta en el servidor cuando el navegador solicita un recurso.
Por eso, no podemos detener un script PHP que se está ejecutando en el servidor, esperando que el usuario escriba en el navegador, como haríamos en una aplicación de escritorio.
Pero tenemos la necesidad de que un usuario, pueda enviar valores para la ejecución de un script.
Solución (hay más opciones):
El navegador envía los valores junto con una nueva petición HTTP, y esto lo vamos a conseguir con los formulario HTML.
- El usuario rellena los campos del formulario (
input,select,textarea...). - Al pulsar el botón de envío (
submit), el navegador realiza una petición al servidor. - Cada campo enviado aporta un nombre y un valor; el nombre se indica con el atributo
name. - PHP permite consultar los datos mediante
$_GETo$_POST, según el método empleado.
Creando un formulario
- Un formulario es un elemento HTML, contenedor
- Se delimitada por
<form>, - Su objetivo es contener otros elementos de entrada de valores
- El usuario puede introducir o seleccionar valores para enviarlos al servidor.
De los atributos de atributos de form , nos interesan especialmente dos:
action: URL del recurso al que se enviará el formulario, por ejemplodatos.php.method: método HTTP utilizado para el envío, habitualmenteGEToPOST.
El atributo action
<form action="datos.php" method="POST">
<!-- Campos del formulario -->
<button type="submit">Enviar</button>
</form>Si indicamos datos.php, el navegador resuelve esa ruta respecto de la URL de la página actual. También podemos utilizar una ruta absoluta, como /pruebas/datos.php.
Actividad
Ping-pong entre páginas
Crea dos archivos, pin.php y pon.php. Ambos tendrán un botón de envío: desde pin.php iremos a pon.php y desde pon.php volveremos a pin.php.
Posible solución
pin.php
<!doctype html>
<html lang="es">
<head><meta charset="UTF-8"><title>Pin</title></head>
<body>
<form action="pon.php">
<input type="submit" value="Ir a pon">
</form>
</body>
</html>pon.php
<!doctype html>
<html lang="es">
<head><meta charset="UTF-8"><title>Pon</title></head>
<body>
<form action="pin.php">
<input type="submit" value="Ir a pin">
</form>
</body>
</html>El atributo method: GET o POST
- GET: los datos del formulario se codifican normalmente en la cadena de consulta de la URL (
?nombre=...). - POST: los datos se envían normalmente en el cuerpo de la petición HTTP.
<form action="datos.php" method="GET">...</form>
<form action="datos.php" method="POST">...</form>Si no se indica method, el valor predeterminado es GET.
<form action="datos.php" method="GET">
<label for="nombre">Nombre</label>
<input type="text" id="nombre" name="nombre" value="María">
<label for="apellido">Apellido</label>
<input type="text" id="apellido" name="apellido" value="Ruiz">
<input type="submit" value="Enviar">
</form>Al enviarlo, el navegador solicitará una URL similar a:
datos.php?nombre=Mar%C3%ADa&apellido=RuizEl signo ? inicia los parámetros y & separa las parejas nombre=valor. El navegador codifica los caracteres especiales para incluirlos en la URL.

El atributo name identifica el dato enviado al servidor; id identifica el elemento dentro del documento HTML y permite, por ejemplo, asociarlo a un <label> o seleccionarlo desde JavaScript. El atributo value establece su valor inicial.
POST no cifra los datos ni es intrínsecamente más seguro que GET. Para proteger la información durante el transporte debemos utilizar HTTPS. No envíes contraseñas ni otros secretos en la URL: pueden quedar registrados en el historial y en los registros del servidor.
Podemos inspeccionar los datos enviados desde las herramientas de desarrollo del navegador, en la pestaña Red / Network.

Otro atributo importante es enctype, que determina cómo se codifica el cuerpo de un formulario. Para subir archivos suele utilizarse enctype="multipart/form-data" junto con method="POST"; no es un mecanismo de cifrado.
Elementos del formulario
Los formularios pueden contener input, button, select, textarea y otros elementos. Algunos tipos habituales de input son text, password, email, date, radio, checkbox, hidden, file y submit.
type: tipo de control.name: nombre con el que se envía su valor.value: valor inicial o valor asociado al control.type="hidden": campo que no se muestra visualmente, pero cuyo valor se envía con el formulario. No es secreto ni fiable por sí mismo: el usuario puede modificarlo.
Consulta la documentación de formularios de MDN.
Formulario para solicitar un número<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Tabla de multiplicar</title>
</head>
<body>
<form action="tabla.php" method="GET">
<label for="numero">Inserta un número</label>
<input type="text" id="numero" name="numero">
<input type="submit" value="Enviar">
</form>
</body>
</html>En tabla.php podemos leer el dato recibido mediante $_GET['numero']. Como aún no sabemos si existe, conviene comprobarlo antes de utilizarlo:
<?php
$numero = $_GET['numero'] ?? '';
echo 'Valor introducido: ' . htmlspecialchars($numero, ENT_QUOTES, 'UTF-8');Obtener datos de un formulario
PHP dispone de las variables superglobales $_GET y $_POST, que son arrays asociativos. Sus índices corresponden a los atributos name de los controles enviados.
<form action="resuelve.php" method="POST">
<input type="text" name="nombre">
<input type="submit" value="Enviar">
</form>En resuelve.php:
<?php
$nombre = $_POST['nombre'] ?? '';Existe también $_REQUEST, pero no se recomienda para estos ejemplos: puede combinar datos procedentes de GET, POST y cookies, según la configuración, y hace menos explícito su origen.
Comprobar si una variable existe
En PHP es importante distinguir entre una variable inexistente, una variable con valor null y una variable que contiene un valor considerado «vacío».
Pregunta
¿Qué ocurre con `$edad;`?
Escribir únicamente $edad; no declara ni inicializa la variable. Intentar leer una variable indefinida genera un aviso (Warning en PHP actual). Para inicializarla a nulo debemos escribir $edad = null;.
Las funciones y construcciones más útiles son:
| Comprobación | Significado |
|---|---|
isset($variable) | true si está definida y su valor no es null |
empty($variable) | true si no existe o contiene un valor considerado vacío |
is_null($variable) | true si su valor es null; requiere que la variable exista para evitar avisos |
<?php
$variable = null;
var_dump(isset($variable)); // false
var_dump(empty($variable)); // true
var_dump(is_null($variable)); // true
$variable = 0;
var_dump(isset($variable)); // true
var_dump(empty($variable)); // true
var_dump(is_null($variable)); // falseempty() considera vacíos, entre otros, "", 0, 0.0, "0", false, null y []. No confundas un campo vacío con un campo cuyo valor es cero.
Actividad
Probando valores
Modifica $variable en el ejemplo anterior y prueba con null, "", 0, "0", true y false. Compara los resultados de isset(), empty() e is_null().
Actividad
Comprobar un dato del formulario
Realiza una página con una caja de texto. Si el usuario no introduce ningún valor, muéstrale un aviso; en caso contrario, muestra el valor recibido. El texto "0" debe aceptarse como valor válido.
Posible solución: form1.php
<?php
$mensaje = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$valor = $_POST['valor'] ?? '';
if (trim($valor) === '') {
$mensaje = 'Debes introducir un valor';
} else {
$mensaje = 'Has introducido: ' . $valor;
}
}
?>
<!doctype html>
<html lang="es">
<head><meta charset="UTF-8"><title>Formulario</title></head>
<body>
<p><?= htmlspecialchars($mensaje, ENT_QUOTES, 'UTF-8') ?></p>
<form action="form1.php" method="POST">
<fieldset>
<legend>Inserta datos</legend>
<input type="text" name="valor">
<input type="submit" value="Enviar">
</fieldset>
</form>
</body>
</html>Validar valores en el servidor
Aunque el navegador valide los campos, siempre debemos comprobar los datos en el servidor. Los valores de los controles de texto llegan normalmente como cadenas, incluso cuando representan números.
<?php
$valor = '754';
var_dump(is_numeric($valor)); // true
var_dump(is_string($valor)); // true
var_dump(is_int($valor)); // falseis_numeric() comprueba si una cadena representa un número, pero no distingue por sí sola si es un entero o un decimal. Podemos utilizar filtros como FILTER_VALIDATE_INT, FILTER_VALIDATE_FLOAT y FILTER_VALIDATE_EMAIL.
Actividad
Identificar el valor recibido
Haz un formulario que reciba un valor y compruebe si representa un entero, un número decimal o una cadena no numérica.
Actividad
Tabla de multiplicar
Crea un formulario para introducir un número. Comprueba que se ha enviado y que contiene un valor numérico válido antes de mostrar su tabla de multiplicar.
Orientación para la solución
<?php
$numero = filter_input(INPUT_POST, 'numero', FILTER_VALIDATE_INT);
if ($numero === false || $numero === null) {
echo 'Introduce un número entero válido.';
} else {
for ($i = 1; $i <= 10; $i++) {
echo "$numero × $i = " . ($numero * $i) . '<br>';
}
}El formulario debe utilizar method="POST" y un campo name="numero".
Actividad: ficha de datos personales
Actividad
Crear y procesar un formulario
Realiza un formulario que solicite:
- Nombre, apellidos y dirección.
- Fecha de nacimiento y edad.
- Idiomas que habla, entre cuatro opciones (
checkbox). - Género, con la opción de no informar (
radio). - Correo electrónico (
email). - Estudios: ESO, Bachillerato, Ciclo Formativo o Grado Universitario (
select).
En una segunda página, muestra una ficha con los datos recibidos.
Posible solución: index.php
<!doctype html>
<html lang="es">
<head><meta charset="UTF-8"><title>Datos personales</title></head>
<body>
<form action="datos.php" method="POST">
<fieldset>
<legend>Datos personales</legend>
<label>Nombre <input type="text" name="nombre"></label>
<label>Apellidos <input type="text" name="apellido"></label>
<label>Dirección <input type="text" name="direccion"></label>
<label>Fecha de nacimiento <input type="date" name="fNac"></label>
<label>Edad <input type="number" name="edad" min="0"></label>
<p>Idiomas</p>
<label><input type="checkbox" name="idiomas[]" value="castellano"> Castellano</label>
<label><input type="checkbox" name="idiomas[]" value="rumano"> Rumano</label>
<label><input type="checkbox" name="idiomas[]" value="ingles"> Inglés</label>
<label><input type="checkbox" name="idiomas[]" value="frances"> Francés</label>
<p>Género</p>
<label><input type="radio" name="genero" value="masculino"> Masculino</label>
<label><input type="radio" name="genero" value="femenino"> Femenino</label>
<label><input type="radio" name="genero" value="no_aporta"> No quiero informar</label>
<label>Correo electrónico <input type="email" name="email"></label>
<label>Estudios
<select name="estudios">
<option value="eso">ESO</option>
<option value="bach">Bachillerato</option>
<option value="cicloFormativo">Ciclo Formativo</option>
<option value="gradoUniversitario">Grado Universitario</option>
</select>
</label>
<button type="submit">Enviar</button>
</fieldset>
</form>
</body>
</html>Posible solución: datos.php
<?php
function escapar(string $texto): string {
return htmlspecialchars($texto, ENT_QUOTES, 'UTF-8');
}
$nombre = trim((string) (filter_input(INPUT_POST, 'nombre') ?? ''));
$apellidos = trim((string) (filter_input(INPUT_POST, 'apellido') ?? ''));
$direccion = trim((string) (filter_input(INPUT_POST, 'direccion') ?? ''));
$fecha = (string) (filter_input(INPUT_POST, 'fNac') ?? '');
$edad = filter_input(INPUT_POST, 'edad', FILTER_VALIDATE_INT);
$idiomas = filter_input(INPUT_POST, 'idiomas', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
$idiomas = is_array($idiomas) ? $idiomas : [];
$genero = (string) (filter_input(INPUT_POST, 'genero') ?? '');
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
$estudios = (string) (filter_input(INPUT_POST, 'estudios') ?? '');
?>
<!doctype html>
<html lang="es">
<head><meta charset="UTF-8"><title>Ficha personal</title></head>
<body>
<h1>Datos personales</h1>
<p>Nombre: <?= escapar($nombre) ?></p>
<p>Apellidos: <?= escapar($apellidos) ?></p>
<p>Dirección: <?= escapar($direccion) ?></p>
<p>Fecha de nacimiento: <?= escapar($fecha) ?></p>
<p>Edad: <?= $edad === false || $edad === null ? 'No válida' : $edad ?></p>
<p>Idiomas: <?= escapar(implode(', ', array_map('strval', $idiomas))) ?></p>
<p>Género: <?= escapar($genero) ?></p>
<p>Correo: <?= $email === false || $email === null ? 'No válido' : escapar($email) ?></p>
<p>Estudios: <?= escapar($estudios) ?></p>
</body>
</html>Esta solución ilustra la lectura y el escape de salida; en una aplicación real también se deben validar los valores permitidos de cada campo y comprobar los tipos de los datos recibidos.
Filtrado y seguridad: XSS
Si mostramos directamente en una página HTML el contenido enviado por un usuario, podemos introducir una vulnerabilidad XSS (Cross-Site Scripting): el navegador podría interpretar contenido no confiable como código ejecutable.
Ejemplo de salida insegura<?php
$nombre = $_GET['nombre'] ?? '';
echo $nombre; // No usar con datos externos sin escapar<?php
$nombre = $_GET['nombre'] ?? '';
echo htmlspecialchars($nombre, ENT_QUOTES, 'UTF-8');- Validar: comprobar que el dato cumple las reglas esperadas (por ejemplo, que una edad sea un entero).
- Escapar la salida: convertir caracteres especiales para que el dato se muestre como texto en su contexto de destino. Para texto dentro de HTML, usamos
htmlspecialchars(). - Filtrar: aplicar una regla de validación o transformación con funciones como
filter_var()yfilter_input().
No debemos confiar en que un navegador bloquee los ataques. strip_tags() tampoco sustituye el escape correcto de salida.
filter_var() y filter_input()
<?php
$correo = 'alumno@example.com';
var_dump(filter_var($correo, FILTER_VALIDATE_EMAIL));
$edad = filter_input(INPUT_POST, 'edad', FILTER_VALIDATE_INT);filter_var($valor, $filtro)aplica un filtro a un valor que ya tenemos.filter_input(INPUT_POST, 'edad', $filtro)lee y filtra un dato de entrada.- Un filtro de validación suele devolver el valor validado,
falsesi falla onullsi falta el dato; conviene usar comparaciones estrictas.
Consulta la referencia de filtros de PHP.
Actividad
Filtrando valores
Crea un formulario que permita introducir diferentes valores. Comprueba cuáles son enteros válidos, números decimales o direcciones de correo electrónico. Muestra los valores recibidos de forma segura.
Referencias
Para ampliar
Ejercicios
Crea una página web que simule una tienda online.
- Muestra un producto con su imagen y precio.
- Incluye una caja de texto para escribir un comentario.
- Al pulsar el botón Publicar, el comentario debe aparecer debajo de la imagen del producto.
- Puedes usar la imagen:
