Manejo de Formularios en PHP

Objetivos de Aprendizaje

  • Comprender los métodos GET y POST para el envío de formularios
  • Aprender a procesar y validar datos de formularios
  • Implementar medidas de seguridad en el manejo de formularios
  • Trabajar con formularios de carga de archivos
  • Entender las mejores prácticas en el manejo de formularios

Métodos de Envío: GET vs POST

GET
  • Los datos se envían en la URL
  • Visible para el usuario
  • Limitado en cantidad de datos
  • Se puede bookmarkar
  • No seguro para datos sensibles
POST
  • Los datos se envían en el cuerpo de la petición
  • No visible en la URL
  • Sin límite práctico de datos
  • No se puede bookmarkar
  • Más seguro para datos sensibles

Formulario Básico

Veamos un ejemplo básico de cómo manejar un formulario en PHP:

formulario_basico.php
PHP Básico

<?php
// Verificar si el formulario fue enviado
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $nombre = $_POST['nombre'] ?? '';
    $email = $_POST['email'] ?? '';
    
    echo "Datos recibidos:<br>";
    echo "Nombre: " . htmlspecialchars($nombre) . "<br>";
    echo "Email: " . htmlspecialchars($email);
}
?>

<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>">
    <div class="mb-3">
        <label for="nombre">Nombre:</label>
        <input type="text" name="nombre" id="nombre" class="form-control" required>
    </div>
    <div class="mb-3">
        <label for="email">Email:</label>
        <input type="email" name="email" id="email" class="form-control" required>
    </div>
    <button type="submit" class="btn btn-primary">Enviar</button>
</form>
                            
Nota

Este es un ejemplo básico que muestra cómo procesar datos de un formulario usando PHP. Observa el uso de htmlspecialchars() para prevenir ataques XSS y el operador de fusión null (??) para manejar valores no definidos.

Validación de Formularios

La validación de datos es crucial para mantener la seguridad y la integridad de tu aplicación. Aquí hay un ejemplo más completo con validación:

validacion_formulario.php
PHP Intermedio

<?php
// Inicialización de variables
$errores = [];
$nombre = $email = $telefono = '';

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    // Validación del nombre
    if (empty($_POST['nombre'])) {
        $errores[] = "El nombre es requerido";
    } else {
        $nombre = limpiarDato($_POST['nombre']);
        if (!preg_match("/^[a-zA-Z-' ]*$/", $nombre)) {
            $errores[] = "El nombre solo puede contener letras y espacios";
        }
    }

    // Validación del email
    if (empty($_POST['email'])) {
        $errores[] = "El email es requerido";
    } else {
        $email = limpiarDato($_POST['email']);
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $errores[] = "Formato de email inválido";
        }
    }

    // Validación del teléfono (opcional)
    if (!empty($_POST['telefono'])) {
        $telefono = limpiarDato($_POST['telefono']);
        if (!preg_match("/^[0-9]{9}$/", $telefono)) {
            $errores[] = "El teléfono debe tener 9 dígitos";
        }
    }
}

/**
 * Limpia y sanitiza datos de entrada
 * @param string $dato El dato a limpiar
 * @return string El dato limpio
 */
function limpiarDato($dato) {
    $dato = trim($dato);
    $dato = stripslashes($dato);
    $dato = htmlspecialchars($dato);
    return $dato;
}
?>

<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>">
    <!-- Mostrar errores si existen -->
    <?php if (!empty($errores)): ?>
        <div class="alert alert-danger">
            <ul class="mb-0">
                <?php foreach ($errores as $error): ?>
                    <li><?php echo $error; ?></li>
                <?php endforeach; ?>
            </ul>
        </div>
    <?php endif; ?>

    <div class="mb-3">
        <label for="nombre" class="form-label">Nombre</label>
        <input type="text" name="nombre" id="nombre" class="form-control" 
               value="<?php echo $nombre; ?>" required>
    </div>
    <div class="mb-3">
        <label for="email" class="form-label">Email</label>
        <input type="email" name="email" id="email" class="form-control" 
               value="<?php echo $email; ?>" required>
    </div>
    <div class="mb-3">
        <label for="telefono" class="form-label">Teléfono</label>
        <input type="tel" name="telefono" id="telefono" class="form-control" 
               value="<?php echo $telefono; ?>">
        <small class="text-muted">Opcional, debe tener 9 dígitos</small>
    </div>
    <button type="submit" class="btn btn-primary">Enviar</button>
</form>
                            

Manejo de Archivos

PHP también permite manejar la carga de archivos a través de formularios. Aquí hay un ejemplo:

upload_archivo.php
PHP Intermedio

<?php
// Verificar si se subió un archivo
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    if (isset($_FILES['archivo'])) {
        $archivo = $_FILES['archivo'];
        $nombre = $archivo['name'];
        $tipo = $archivo['type'];
        $ruta_temporal = $archivo['tmp_name'];
        $error = $archivo['error'];
        $tamano = $archivo['size'];

        // Definir restricciones
        $permitidos = ['image/jpeg', 'image/png', 'image/gif'];
        $max_tamano = 5 * 1024 * 1024; // 5MB

        if ($error === 0) {
            if (in_array($tipo, $permitidos)) {
                if ($tamano <= $max_tamano) {
                    // Crear directorio de uploads si no existe
                    $directorio_destino = 'uploads/';
                    if (!is_dir($directorio_destino)) {
                        mkdir($directorio_destino, 0777, true);
                    }

                    // Generar nombre único para evitar sobrescrituras
                    $nombre_archivo = uniqid() . '_' . $nombre;
                    $ruta_destino = $directorio_destino . $nombre_archivo;

                    if (move_uploaded_file($ruta_temporal, $ruta_destino)) {
                        echo "<div class='alert alert-success'>Archivo subido correctamente</div>";
                    } else {
                        echo "<div class='alert alert-danger'>Error al subir el archivo</div>";
                    }
                } else {
                    echo "<div class='alert alert-danger'>El archivo es demasiado grande</div>";
                }
            } else {
                echo "<div class='alert alert-danger'>Tipo de archivo no permitido</div>";
            }
        } else {
            echo "<div class='alert alert-danger'>Error al subir el archivo</div>";
        }
    }
}
?>

<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" 
      enctype="multipart/form-data">
    <div class="mb-3">
        <label for="archivo" class="form-label">Selecciona un archivo:</label>
        <input type="file" name="archivo" id="archivo" class="form-control" required>
        <small class="text-muted">
            Formatos permitidos: JPG, PNG, GIF. Tamaño máximo: 5MB
        </small>
    </div>
    <button type="submit" class="btn btn-primary">Subir Archivo</button>
</form>
                        
Consideraciones de Seguridad
  • Siempre valida el tipo de archivo antes de procesarlo
  • Establece un límite de tamaño máximo para los archivos
  • Genera nombres únicos para evitar sobrescrituras
  • Almacena los archivos fuera del directorio web público si es posible
  • Verifica los permisos de escritura del directorio de destino

Mejores Prácticas de Seguridad

Recomendaciones de Seguridad
  • Siempre valida y sanitiza los datos de entrada
  • Usa htmlspecialchars() para prevenir XSS
  • Implementa CSRF tokens para formularios importantes
  • Limita el tamaño y tipo de archivos que se pueden subir
  • Usa prepared statements para consultas SQL
  • Implementa rate limiting para prevenir spam
  • Valida los datos tanto en el cliente como en el servidor

Ejercicios Prácticos

Ejercicio 1: Formulario de Registro

Crea un formulario de registro que incluya:

  • Nombre y apellidos
  • Email
  • Contraseña con confirmación
  • Fecha de nacimiento
  • Avatar (subida de imagen)

Implementa validación completa y muestra mensajes de error apropiados.

Ejercicio 2: Formulario de Contacto

Desarrolla un formulario de contacto que:

  • Valide el formato del email
  • Requiera un asunto
  • Permita adjuntar un archivo
  • Incluya un campo de captcha simple
  • Envíe los datos por email usando mail()