nimbly_mail_active_mailer — Filter the Active Mailer

Type: Filter
Arguments: 3
Fired in: Processor::get_active_mailer()
Since: 1.0.0


Overview

Fires every time NimblyMail selects which AbstractMailer will handle a pending wp_mail() call. Attach a handler here to implement rule-based routing or backup-connection retry without touching the persisted mail.mailer option.

Important

This filter fires once per send, after the configured mailer is resolved from options but before AbstractMailer::configure() runs and before PHPMailer is invoked. Returning a different mailer instance causes that mailer to handle the current send only — the stored setting is unchanged.


Signature

/**
 * @param AbstractMailer  $mailer    The mailer selected from the persisted mail.mailer option.
 * @param PHPMailer|null  $phpmailer The PHPMailer instance if available at call time, else null.
 * @param array           $context   Best-effort context array. Keys present depend on call site:
 *                                   - to      string  Comma-separated recipients (from phpmailer_init).
 *                                   - subject string  Subject line (from phpmailer_init).
 *                                   - headers array   Custom headers (from phpmailer_init).
 *                                   - source  string  Calling context (test-email, resend, etc.).
 *                                   Missing keys are absent, not null.
 */
$mailer = apply_filters( 'nimbly_mail_active_mailer', $mailer, $phpmailer, $context );

Behaviour

Scenario Result
Handler returns an AbstractMailer instance That mailer is used for the current send
Handler returns anything else error_log() warning emitted; original mailer used
Re-entry depth ≥ 2 Filter skipped; original mailer used; error_log() warning emitted

Re-entry Guard

A static Processor::$active_mailer_filter_depth counter prevents infinite loops. The maximum depth is 2 — one primary call, one retry. If your handler calls wp_mail() recursively from inside the filter, the guard halts the loop on the second pass.

Warning

Do not call wp_mail() from inside an nimbly_mail_active_mailer handler without first using Processor::mark_bypass(). See pre-send.md for the bypass pattern.


Example — Route by Recipient Domain

<?php
/**
 * Plugin Name: My Smart Router (mu-plugin example)
 */

use NimblyMailMailersAbstractMailer;
use NimblyMailProcessor;

add_filter(
    'nimbly_mail_active_mailer',
    function ( AbstractMailer $mailer, $phpmailer, array $context ): AbstractMailer {
        $to = $context['to'] ?? '';

        // Route @example.com addresses through the SES mailer.
        if ( str_contains( $to, '@example.com' ) ) {
            $mailers = ( new Processor() )->get_mailers();
            if ( isset( $mailers['ses'] ) ) {
                return $mailers['ses'];
            }
        }

        // Return the original for everyone else.
        return $mailer;
    },
    10,
    3
);

Example — Backup Connection Retry

The backup-connection pattern attaches to nimbly_mail_mail_failed and marks a retry flag, then lets nimbly_mail_active_mailer return the backup mailer on the second pass:

<?php
add_action(
    'nimbly_mail_mail_failed',
    function ( WP_Error $error ): void {
        // Store a retry flag so the next nimbly_mail_active_mailer call
        // can return the backup mailer.
        set_transient( 'my_plugin_use_backup_mailer', true, 60 );
    }
);

add_filter(
    'nimbly_mail_active_mailer',
    function ( $mailer, $phpmailer, $context ) {
        if ( get_transient( 'my_plugin_use_backup_mailer' ) ) {
            delete_transient( 'my_plugin_use_backup_mailer' );
            $mailers = ( new NimblyMailProcessor() )->get_mailers();
            return $mailers['smtp'] ?? $mailer; // fall back to SMTP backup.
        }
        return $mailer;
    },
    10,
    3
);

Context Keys Reference

Key Type Populated from
to string phpmailer_init — PHPMailer::getAllAddresses()
subject string phpmailer_init — PHPMailer::Subject
headers array phpmailer_init — PHPMailer::getCustomHeaders()
source string Explicit calls (test email, resend)

Note

If you need the full wp_mail() $atts array (including message and attachments), attach to the WordPress core wp_mail filter and stash the data in a request-local property that your nimbly_mail_active_mailer handler reads. This filter does not expose $atts directly.


Common Pitfalls

  • Returning a non-AbstractMailer value (e.g. a slug string) is silently caught; the fallback triggers and a warning is logged. Always return an AbstractMailer instance.
  • Accessing $phpmailer — it is null when get_active_mailer() is called outside phpmailer_init (e.g. from test-email or resend code paths). Guard with if ( $phpmailer instanceof PHPMailer ).
  • Context is best-effort — always use $context['to'] ?? '' with a fallback.

Security Notes

  • A misbehaving handler that returns a misconfigured mailer could silently fail to deliver. Ensure your handler only returns mailers retrieved via Processor::get_mailers().
  • This hook fires inside a request context already gated by WordPress core — no additional capability checks are required, but Pro handlers should not trust $context data for access control.

See Also

Was this article helpful?