Formularios HTML con PHP: GET, POST, validación y procesamiento paso a paso


En las guías anteriores trabajamos con variables, condicionales, bucles, arrays y funciones. Ahora vamos a conectar PHP con algo que cualquier usuario puede utilizar directamente: un formulario HTML.

La idea de esta guía es entender la secuencia completa, no solamente copiar código. Vamos a construir un formulario real, enviarlo al servidor, recibir los datos con PHP, validarlos y volver a mostrar el formulario cuando exista un error.

La secuencia completa

Cuando una persona completa un formulario y presiona el botón Enviar, ocurre algo parecido a esto:

Formulario HTML
      ↓
El usuario completa los campos
      ↓
Presiona "Enviar"
      ↓
El navegador crea una petición HTTP
      ↓
Los datos llegan al servidor
      ↓
PHP recibe los datos
      ↓
PHP valida la información
      ↓
Se procesa la operación
      ↓
El servidor devuelve una respuesta HTML

Este flujo es fundamental para entender el desarrollo web con PHP.

Primer formulario HTML

Empecemos con un archivo muy simple llamado formulario.html:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Mi primer formulario</title>
</head>
<body>

    <h1>Registro de usuario</h1>

    <form action="procesar.php" method="post">
        <label for="nombre">Nombre:</label>
        <input type="text" id="nombre" name="nombre">

        <br><br>

        <label for="email">Email:</label>
        <input type="email" id="email" name="email">

        <br><br>

        <button type="submit">Enviar</button>
    </form>

</body>
</html>

¿Qué hace la etiqueta form?

La etiqueta <form> indica que ese bloque contiene datos que pueden enviarse al servidor.

<form action="procesar.php" method="post">

Hay dos atributos muy importantes:

  • action: indica a qué archivo se enviarán los datos;
  • method: indica cómo se enviarán.

El atributo name es clave

Dentro de un formulario, PHP recibe los campos utilizando el atributo name.

<input type="text" name="nombre">

El atributo id es útil para asociar etiquetas, JavaScript o CSS. Pero para enviar el dato al servidor, el atributo importante es name.

Qué recibe PHP

Si el usuario escribe:

Nombre: Ana
Email: ana@email.com

el navegador enviará aproximadamente estos pares de datos:

nombre = Ana
email = ana@email.com

Como usamos method="post", PHP los coloca dentro de la variable superglobal $_POST.

procesar.php

Creamos ahora un segundo archivo:

<?php

$nombre = $_POST['nombre'];
$email = $_POST['email'];

echo "Nombre: " . $nombre;
echo "<br>";
echo "Email: " . $email;

Si completamos el formulario y presionamos Enviar, el navegador solicitará procesar.php y PHP podrá leer los valores enviados.

¿Qué es $_POST?

$_POST es un array asociativo especial proporcionado por PHP.

Si el formulario contiene:

<input type="text" name="nombre">
<input type="email" name="email">

PHP puede recibir algo similar a:

$_POST = [
    'nombre' => 'Ana',
    'email' => 'ana@email.com'
];

Por eso lo que aprendimos sobre arrays asociativos empieza a tener una aplicación directa.

Variables superglobales

PHP incluye algunas variables especiales que están disponibles en cualquier ámbito del script. Se llaman superglobales.

Algunas de las más utilizadas son:

$_GET
$_POST
$_FILES
$_SERVER
$_SESSION
$_COOKIE

En esta guía nos concentraremos principalmente en $_GET, $_POST y $_SERVER.

GET y POST

Los dos métodos más comunes para enviar formularios son GET y POST.

Formulario usando GET

<form action="buscar.php" method="get">
    <input type="text" name="q">
    <button type="submit">Buscar</button>
</form>

Si escribimos:

php

el navegador podría abrir una URL similar a:

buscar.php?q=php

¿Qué significa la URL?

La parte que aparece después del signo ? se llama query string.

?q=php

Si hubiera más de un dato:

buscar.php?categoria=php&nivel=basico

Los valores se separan con &.

Leer datos GET

<?php

$busqueda = $_GET['q'];

echo "Buscaste: " . $busqueda;

¿Cuándo conviene GET?

GET es especialmente útil cuando la información representa una consulta y queremos que pueda formar parte de la URL.

Ejemplos:

  • buscadores;
  • filtros;
  • paginación;
  • ordenamientos;
  • parámetros que queremos poder compartir mediante un enlace.

¿Cuándo conviene POST?

POST suele utilizarse cuando estamos enviando información para realizar una operación.

Ejemplos:

  • registrar un usuario;
  • crear una publicación;
  • enviar un mensaje;
  • guardar cambios;
  • procesar un formulario de contacto.

Que POST no muestre los datos en la URL no significa que los datos estén cifrados. La protección durante el transporte depende de utilizar HTTPS.

Evitar errores cuando un campo no existe

Este código puede generar una advertencia si todavía no se envió el formulario:

$nombre = $_POST['nombre'];

Una alternativa muy utilizada es el operador ??:

$nombre = $_POST['nombre'] ?? '';

Si la clave nombre no existe, se utiliza un string vacío.

Detectar si el formulario fue enviado

Podemos consultar el método HTTP utilizando $_SERVER['REQUEST_METHOD'].

<?php

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    echo "El formulario fue enviado";
}

Esto permite tener el formulario y su procesamiento dentro del mismo archivo.

Formulario y PHP en el mismo archivo

Ahora vamos a crear registro.php.

<?php

$nombre = '';
$email = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $nombre = $_POST['nombre'] ?? '';
    $email = $_POST['email'] ?? '';
}

?>

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Registro</title>
</head>
<body>

    <h1>Registro</h1>

    <form method="post">
        <label for="nombre">Nombre:</label>
        <input
            type="text"
            id="nombre"
            name="nombre"
            value="<?php echo htmlspecialchars($nombre); ?>"
        >

        <br><br>

        <label for="email">Email:</label>
        <input
            type="email"
            id="email"
            name="email"
            value="<?php echo htmlspecialchars($email); ?>"
        >

        <br><br>

        <button type="submit">Enviar</button>
    </form>

</body>
</html>

Como no indicamos action, el formulario se envía al mismo archivo.

¿Por qué usamos htmlspecialchars()?

Supongamos que un usuario escribe caracteres especiales o incluso código HTML.

Si imprimimos ese contenido directamente dentro de la página, el navegador podría interpretarlo como HTML.

htmlspecialchars() convierte caracteres especiales en una representación segura para mostrarlos como texto.

echo htmlspecialchars($nombre);

Esto es muy importante al mostrar información que proviene del usuario.

Validar campos obligatorios

Ahora vamos a comprobar que nombre y email tengan contenido.

<?php

$nombre = $_POST['nombre'] ?? '';
$email = $_POST['email'] ?? '';

$errores = [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    if (trim($nombre) === '') {
        $errores[] = 'El nombre es obligatorio';
    }

    if (trim($email) === '') {
        $errores[] = 'El email es obligatorio';
    }
}

¿Qué hace trim()?

trim() elimina espacios al comienzo y al final de un string.

De esta manera, un usuario que escriba solamente espacios no supera la validación.

Validar un email

PHP incluye filter_var(), que permite aplicar distintos filtros.

if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $errores[] = 'El email no es válido';
}

No necesitamos construir manualmente una expresión regular para un caso común como este.

Validar edad

Agreguemos un campo numérico:

<label for="edad">Edad:</label>
<input
    type="number"
    id="edad"
    name="edad"
    min="0"
>

Y en PHP:

$edad = $_POST['edad'] ?? '';

if ($edad === '') {
    $errores[] = 'La edad es obligatoria';
} elseif (filter_var($edad, FILTER_VALIDATE_INT) === false) {
    $errores[] = 'La edad debe ser un número entero';
} elseif ((int) $edad < 18) {
    $errores[] = 'Debés ser mayor de edad';
}

Validación del navegador y validación del servidor

HTML también permite realizar algunas validaciones:

<input type="text" name="nombre" required>
<input type="email" name="email" required>
<input type="number" name="edad" min="18" required>

Estas validaciones mejoran la experiencia del usuario, pero no reemplazan la validación en PHP.

Un usuario o programa puede enviar una petición directamente al servidor sin utilizar nuestro formulario HTML.

Por eso, las reglas importantes siempre deben comprobarse nuevamente en el servidor.

Validar no es lo mismo que sanitizar

Validar significa comprobar si un dato cumple determinadas reglas.

Por ejemplo:

¿El email tiene un formato válido?
¿La edad es un entero?
¿El nombre está completo?

Sanitizar o escapar significa preparar los datos para un contexto determinado.

Por ejemplo, al mostrar texto dentro de HTML:

htmlspecialchars($nombre, ENT_QUOTES, 'UTF-8');

Una buena aplicación suele necesitar ambas cosas.

Mostrar errores en el formulario

<?php if (!empty($errores)): ?>

    <ul>
        <?php foreach ($errores as $error): ?>
            <li>
                <?php echo htmlspecialchars($error); ?>
            </li>
        <?php endforeach; ?>
    </ul>

<?php endif; ?>

¿Qué hace empty()?

empty() permite comprobar si una variable está vacía.

if (empty($errores)) {
    echo "No hay errores";
}

Como $errores es un array, un array sin elementos se considera vacío.

Mantener los valores cuando ocurre un error

Si el usuario completa un formulario y hay un error, no queremos obligarlo a escribir todo de nuevo.

Por eso usamos el valor recibido nuevamente en el atributo value:

<input
    type="text"
    name="nombre"
    value="<?php echo htmlspecialchars($nombre, ENT_QUOTES, 'UTF-8'); ?>"
>

De esta manera el campo conserva lo que había escrito.

Formulario completo: HTML + PHP + validación

Veamos ahora el ejemplo completo en un único archivo llamado registro.php.

<?php

$nombre = '';
$email = '';
$edad = '';
$mensaje = '';

$errores = [];
$enviado = false;

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    $nombre = trim($_POST['nombre'] ?? '');
    $email = trim($_POST['email'] ?? '');
    $edad = trim($_POST['edad'] ?? '');
    $mensaje = trim($_POST['mensaje'] ?? '');

    if ($nombre === '') {
        $errores[] = 'El nombre es obligatorio';
    }

    if ($email === '') {
        $errores[] = 'El email es obligatorio';
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errores[] = 'El email no es válido';
    }

    if ($edad === '') {
        $errores[] = 'La edad es obligatoria';
    } elseif (filter_var($edad, FILTER_VALIDATE_INT) === false) {
        $errores[] = 'La edad debe ser un número entero';
    } elseif ((int) $edad < 18) {
        $errores[] = 'Debés ser mayor de edad';
    }

    if ($mensaje === '') {
        $errores[] = 'El mensaje es obligatorio';
    }

    if (empty($errores)) {
        $enviado = true;
    }
}

?>

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Formulario de registro</title>
</head>
<body>

    <h1>Formulario de registro</h1>

    <?php if ($enviado): ?>

        <h2>Datos recibidos correctamente</h2>

        <p>
            Nombre:
            <?php echo htmlspecialchars($nombre, ENT_QUOTES, 'UTF-8'); ?>
        </p>

        <p>
            Email:
            <?php echo htmlspecialchars($email, ENT_QUOTES, 'UTF-8'); ?>
        </p>

        <p>
            Edad:
            <?php echo htmlspecialchars($edad, ENT_QUOTES, 'UTF-8'); ?>
        </p>

        <p>
            Mensaje:
            <?php echo nl2br(
                htmlspecialchars($mensaje, ENT_QUOTES, 'UTF-8')
            ); ?>
        </p>

    <?php else: ?>

        <?php if (!empty($errores)): ?>

            <ul>
                <?php foreach ($errores as $error): ?>
                    <li>
                        <?php echo htmlspecialchars(
                            $error,
                            ENT_QUOTES,
                            'UTF-8'
                        ); ?>
                    </li>
                <?php endforeach; ?>
            </ul>

        <?php endif; ?>

        <form method="post">

            <div>
                <label for="nombre">Nombre:</label>

                <input
                    type="text"
                    id="nombre"
                    name="nombre"
                    value="<?php echo htmlspecialchars(
                        $nombre,
                        ENT_QUOTES,
                        'UTF-8'
                    ); ?>"
                    required
                >
            </div>

            <br>

            <div>
                <label for="email">Email:</label>

                <input
                    type="email"
                    id="email"
                    name="email"
                    value="<?php echo htmlspecialchars(
                        $email,
                        ENT_QUOTES,
                        'UTF-8'
                    ); ?>"
                    required
                >
            </div>

            <br>

            <div>
                <label for="edad">Edad:</label>

                <input
                    type="number"
                    id="edad"
                    name="edad"
                    min="18"
                    value="<?php echo htmlspecialchars(
                        $edad,
                        ENT_QUOTES,
                        'UTF-8'
                    ); ?>"
                    required
                >
            </div>

            <br>

            <div>
                <label for="mensaje">Mensaje:</label>

                <textarea
                    id="mensaje"
                    name="mensaje"
                    rows="5"
                    required
                ><?php echo htmlspecialchars(
                    $mensaje,
                    ENT_QUOTES,
                    'UTF-8'
                ); ?></textarea>
            </div>

            <br>

            <button type="submit">
                Enviar
            </button>

        </form>

    <?php endif; ?>

</body>
</html>

Analicemos la secuencia de este ejemplo

1. El navegador solicita registro.php.

Como todavía no se envió ningún formulario, la petición normalmente es GET.

GET /registro.php

2. PHP ejecuta el archivo.

La condición:

$_SERVER['REQUEST_METHOD'] === 'POST'

es falsa, por lo que simplemente se muestra el formulario.

3. El usuario completa los campos.

Nombre: Ana
Email: ana@email.com
Edad: 25
Mensaje: Quiero registrarme

4. El usuario presiona Enviar.

El navegador genera una nueva petición, esta vez usando POST:

POST /registro.php

5. PHP recibe los datos.

$_POST['nombre']
$_POST['email']
$_POST['edad']
$_POST['mensaje']

6. PHP valida cada dato.

Si existe algún problema, se agrega un mensaje al array $errores.

7. Si hay errores, el formulario vuelve a mostrarse.

Los valores anteriores se conservan para que el usuario pueda corregir solamente lo necesario.

8. Si no hay errores, se procesa la información.

En nuestro ejemplo simplemente mostramos los datos. Más adelante podríamos:

  • guardarlos en MySQL;
  • enviar un correo;
  • registrar un usuario;
  • crear una reserva;
  • generar una factura;
  • ejecutar cualquier otra lógica de negocio.

¿Dónde termina HTML y dónde empieza PHP?

Una de las particularidades de PHP es que podemos alternar entre HTML y PHP dentro del mismo archivo.

<h1>Bienvenido</h1>

<?php

$nombre = 'Ana';

?>

<p>
    Hola <?php echo htmlspecialchars($nombre); ?>
</p>

El servidor procesa primero el PHP. El navegador recibe solamente el HTML resultante.

El navegador nunca recibe el código PHP

Esto es importante:

Servidor:
registro.php
    ↓
PHP ejecuta el código
    ↓
genera HTML
    ↓
Navegador recibe HTML

El usuario no descarga el código PHP original desde el navegador.

Ejemplo práctico con GET: buscador

<?php

$q = trim($_GET['q'] ?? '');

?>

<form method="get">

    <label for="q">Buscar:</label>

    <input
        type="text"
        id="q"
        name="q"
        value="<?php echo htmlspecialchars(
            $q,
            ENT_QUOTES,
            'UTF-8'
        ); ?>"
    >

    <button type="submit">
        Buscar
    </button>

</form>

<?php if ($q !== ''): ?>

    <p>
        Buscando resultados para:
        <strong>
            <?php echo htmlspecialchars(
                $q,
                ENT_QUOTES,
                'UTF-8'
            ); ?>
        </strong>
    </p>

<?php endif; ?>

Si buscamos php arrays, la URL podría ser:

buscar.php?q=php+arrays

Esto tiene sentido porque una búsqueda es algo que puede compartirse mediante una URL.

Un error común: confiar solo en required

Este formulario parece validar el nombre:

<input type="text" name="nombre" required>

Pero un cliente HTTP podría enviar directamente:

POST /registro.php

nombre=

sin utilizar nuestro HTML.

Por eso seguimos necesitando:

if (trim($_POST['nombre'] ?? '') === '') {
    $errores[] = 'El nombre es obligatorio';
}

Otro error común: imprimir datos directamente

No conviene hacer esto con información proporcionada por el usuario:

echo $_POST['nombre'];

Es preferible escapar la salida:

echo htmlspecialchars(
    $_POST['nombre'] ?? '',
    ENT_QUOTES,
    'UTF-8'
);

HTMLspecialchars no reemplaza la validación

htmlspecialchars() sirve para mostrar contenido dentro de HTML de manera segura.

No comprueba si:

  • un email es válido;
  • un número está dentro de un rango;
  • una fecha existe;
  • un usuario tiene permisos;
  • un dato cumple las reglas de negocio.

Cada problema necesita su propia validación.

Errores comunes

  • olvidar el atributo name;
  • confundir id con name;
  • usar $_GET cuando el formulario envía por POST;
  • acceder a una clave de $_POST antes de comprobar si existe;
  • confiar solamente en la validación HTML;
  • mostrar datos del usuario sin escaparlos;
  • confundir validación con sanitización;
  • no conservar los valores del formulario después de un error;
  • usar GET para operaciones que modifican información.

Buenas prácticas

  • usar GET para consultas y filtros;
  • usar POST para operaciones que envían o modifican información;
  • validar siempre en el servidor;
  • escapar la salida al imprimir datos dentro de HTML;
  • utilizar mensajes de error claros;
  • mantener los valores que el usuario ya escribió;
  • separar progresivamente la presentación de la lógica cuando el proyecto crece.

Ejercicios

  1. crear un formulario con nombre y apellido usando POST;
  2. mostrar los datos recibidos con PHP;
  3. crear un buscador sencillo usando GET;
  4. agregar un campo email y validarlo con filter_var();
  5. agregar edad y exigir un mínimo de 18 años;
  6. guardar los errores dentro de un array;
  7. mostrar todos los errores con foreach;
  8. mantener los valores escritos cuando hay un error;
  9. agregar un campo select para elegir una provincia;
  10. agregar varios checkbox y observar cómo llegan a PHP;
  11. crear un formulario de contacto completo;
  12. explicar con tus palabras la secuencia navegador → HTTP → PHP → respuesta.

Qué aprendimos

  • cómo construir un formulario HTML;
  • qué hacen action y method;
  • por qué name es fundamental;
  • diferencia entre GET y POST;
  • cómo funcionan $_GET y $_POST;
  • qué son las superglobales;
  • cómo detectar el método HTTP;
  • cómo validar campos;
  • cómo validar emails y números;
  • diferencia entre validación y sanitización;
  • cómo usar htmlspecialchars();
  • cómo conservar valores después de un error;
  • cómo combinar HTML y PHP en un mismo archivo;
  • cómo funciona la secuencia completa de envío de un formulario.

¿Qué sigue?

En la próxima guía podemos profundizar en formularios PHP más completos: select, checkbox, radio buttons, múltiples valores, archivos con $_FILES y validaciones más avanzadas.

¿Te sirvió esta guía? ☕

Si este contenido te ayudó y querés apoyar a Club Programador, podés colaborar mediante:

☕ Apoyar con Mercado Pago

🌎 Apoyar con PayPal


Descubre más desde Club Programador

Suscríbete y recibe las últimas entradas en tu correo electrónico.

Deja un comentario

Descubre más desde Club Programador

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo