Como interceptar wp_mail para logs y debugging en PHP en WordPress

Interceptar las llamadas a wp_mail en WordPress es una técnica imprescindible para registrar correos, depurar el flujo de envío, evitar envíos en entornos de desarrollo y auditar contenido y cabeceras. A continuación tienes un tutorial detallado con ejemplos claros, buenas prácticas y advertencias sobre seguridad y rendimiento. Todos los fragmentos de código están listos para usarse en un plugin o en el archivo functions.php de un tema (preferible plugin para persistencia).

Contents

Por qué interceptar wp_mail

  • Depuración: Ver exactamente qué se está mandando (destinatarios, asunto, headers, cuerpo) y detectar problemas de codificación o contenido inesperado.
  • Registro/Auditoría: Mantener un historial de comunicaciones generadas por el sitio (útil en entornos multisitio o transaccionales).
  • Entornos de desarrollo: Evitar envíos reales y redirigir correos a buzones de pruebas.
  • Modificación avanzada: Manipular PHPMailer antes de que se envíe (por ejemplo, forzar SMTP, añadir cabeceras, cambiar Reply-To).

Resumen de hooks útiles

Hook Tipo Qué permite
wp_mail Filter Modificar/leer los argumentos (to, subject, message, headers, attachments) justo antes del envío. Devuelve el array de argumentos.
pre_wp_mail Filter (opcional) Permite anular el envío por completo y devolver una respuesta (ideal para entornos de testing). (Disponibilidad según versión en versiones modernas existe.)
phpmailer_init Action Acceder al objeto PHPMailer para inspeccionar/alterar transporte, cabeceras y contenido final.
wp_mail_failed Action Recibir errores cuando falla el envío (recibe un WP_Error con detalles).

Buenas prácticas antes de tocar el envío

  • Implementa el código como un plugin para que no se pierda al actualizar el tema.
  • Respeta la privacidad: no registres información sensible (contraseñas, tokens) y anonymiza direcciones si el log es público.
  • Usa logs con rotación y límites de tamaño para evitar llenar disco.
  • En entornos de producción, registra a un servicio seguro o tabla en la base de datos con acceso restringido.

Ejemplos prácticos

1) Registro simple con add_filter(wp_mail)

Este ejemplo registra todos los correos en el log de PHP (error_log). Útil para debugging rápido.

 stringarray,
    //   subject => string,
    //   message => string,
    //   headers => stringarray,
    //   attachments => array
    // )

    log = sprintf(
        [WP_MAIL] To: %s  Subject: %s  Headers: %s  Attachments: %s  Time: %snMessage:n%sn---n,
        is_array( args[to] ) ? implode( ,, args[to] ) : args[to],
        args[subject],
        is_array( args[headers] ) ? json_encode( args[headers] ) : args[headers],
        !empty( args[attachments] ) ? implode( ,, args[attachments] ) : none,
        date( Y-m-d H:i:s ),
        args[message]
    )

    error_log( log ) // envía a PHP error log (puedes cambiar por file_put_contents)
    return args // Muy importante devolver los argumentos para continuar el envío
}, 10 )
?>

2) Evitar envíos en entornos de desarrollo y guardar en archivo

En development suele ser necesario bloquear los envíos y volcarlos a un archivo local. Aquí mostramos cómo hacerlo con pre_wp_mail si está disponible si no, se puede usar wp_mail y devolver un array con modificaciones.


3) Inspeccionar el PHPMailer final con phpmailer_init

Si quieres ver la versión final que PHPMailer está a punto de enviar (cabeceras ya procesadas, content-type, encodings, etc.), usa phpmailer_init. Útil para debugging con SMTP y para verificar cabeceras multipart.

 phpmailer->From,
        FromName   => phpmailer->FromName,
        Subject    => phpmailer->Subject,
        Body       => phpmailer->Body,
        AltBody    => phpmailer->AltBody,
        To         => array_map( function(t){ return (string) t }, (array) phpmailer->getToAddresses() ),
        Cc         => array_map( function(t){ return (string) t }, (array) phpmailer->getCcAddresses() ),
        Bcc        => array_map( function(t){ return (string) t }, (array) phpmailer->getBccAddresses() ),
        Headers    => phpmailer->getCustomHeaders(),
        Mailer     => phpmailer->Mailer,
        SMTPDebug  => phpmailer->SMTPDebug,
    ]

    // Guardar en fichero de debug (no público)
    uploads = wp_upload_dir()
    file = trailingslashit( uploads[basedir] ) . phpmailer-debug.log
    file_put_contents( file, date( Y-m-d H:i:s ) .   . print_r( info, true ) . n----n, FILE_APPEND  LOCK_EX )
})
?>

4) Capturar errores con wp_mail_failed

Cuando el envío falla, WordPress dispara wp_mail_failed pasando un WP_Error. Aprovecha esto para registrar fallos y notificar al administrador.

get_error_code(),
        wp_error->get_error_message(),
        date( Y-m-d H:i:s )
    )

    // Guardar en log de errores
    error_log( message )

    // Opcional: enviar alerta administrador (cuidado con loops)
    // wp_mail( get_option( admin_email ), Alerta: fallo envío de correo, message )
})
?>

5) Enviar logs a un endpoint remoto (webhook) de forma segura

Enviar los logs a un sistema centralizado es preferible en infraestructuras distribuidas. Ejemplo simple con cURL y autenticación por token. Asegúrate de no incluir contenido sensible o de cifrarlo.

 args[to],
        subject   => args[subject],
        message   => args[message],
        headers   => args[headers],
        time      => date( c ),
        site      => get_bloginfo( url ),
    ]

    ch = curl_init( https://logs.example.com/receive )
    curl_setopt( ch, CURLOPT_RETURNTRANSFER, true )
    curl_setopt( ch, CURLOPT_POST, true )
    curl_setopt( ch, CURLOPT_HTTPHEADER, [
        Content-Type: application/json,
        Authorization: Bearer YOUR_SECRET_TOKEN
    ] )
    curl_setopt( ch, CURLOPT_POSTFIELDS, json_encode( payload ) )
    curl_setopt( ch, CURLOPT_TIMEOUT, 2 ) // no bloquear
    resp = curl_exec( ch )
    curl_close( ch )

    return args
}, 10 )
?>

Qué registrar (recomendado)

  • Destinatarios (To, Cc, Bcc) — enmascara si es necesario.
  • Asunto y cuerpo — si contienen datos sensibles, guarda solo un hash o un extracto.
  • Cabeceras relevantes (From, Reply-To, Content-Type).
  • Adjuntos — nombre y tamaño, no el contenido.
  • Resultado del envío (éxito/fallo y detalles del error).
  • Marca temporal y site/entorno.

Rendimiento y escalabilidad

  1. Evita operaciones de red síncronas en cada llamado a wp_mail usa colas o wp_cron para procesar logs de forma asíncrona.
  2. Si registras en base de datos, crea tabla específica con índices en fecha y destinatario para consultas rápidas.
  3. Archiva o rota logs periódicamente (logrotate o tareas WordPress).
  4. Limita la cantidad de datos guardados por correo (no guardar HTML enorme completo si no es necesario).

Consideraciones de seguridad y privacidad

  • No registres datos personales sin justificación. Cumple la legislación aplicable (ej. RGPD): mínimo necesario, retención limitada y acceso controlado.
  • Protege ficheros y endpoints (ACLs, autenticación, cifrado en tránsito y en reposo si procede).
  • Si los logs contienen emails de usuarios, protégelos y ofrécete a anonimizar cuando corresponda.

Diagnóstico avanzado

  • Activa SMTPDebug en PHPMailer para obtener trazas del diálogo SMTP (solo en entornos seguros).
  • Comprueba encodings (UTF-8) y longitudes de asunto que pueden truncarse.
  • Verifica cabeceras duplicadas (Content-Type vs. headers plugin) y conflictos de plugins que manipulan el correo.

Conclusión

Interceptar wp_mail te da control total para logging y debugging. Empieza con un registro simple (wp_mail o phpmailer_init) y evoluciona a soluciones más robustas (colado asíncrono, envío a un servicio centralizado) según lo necesite tu proyecto. Ten siempre en cuenta la privacidad y el rendimiento al diseñar qué y cómo registras.

Recursos útiles



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *