libro / blog / formularios-php

// capítulo 06 · formularios

Formularios en PHP: $_GET, $_POST, validación y superglobales

Gustavo Arias9 min de lectura

TL;DR Usa GET para leer (búsquedas, filtros) y POST para modificar (registros, compras). Nunca confíes en $_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:

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:

Validar y sanear: no son lo mismo

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:

  1. Post: el formulario envía los datos.
  2. Redirect: el servidor los procesa y responde con header('Location: ...').
  3. 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

Documentación oficial: superglobales y filtros de validación en php.net.

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