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 annimbly_mail_active_mailerhandler without first usingProcessor::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()$attsarray (includingmessageandattachments), attach to the WordPress corewp_mailfilter and stash the data in a request-local property that yournimbly_mail_active_mailerhandler reads. This filter does not expose$attsdirectly.
Common Pitfalls
- Returning a non-
AbstractMailervalue (e.g. a slug string) is silently caught; the fallback triggers and a warning is logged. Always return anAbstractMailerinstance. - Accessing
$phpmailer— it isnullwhenget_active_mailer()is called outsidephpmailer_init(e.g. from test-email or resend code paths). Guard withif ( $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
$contextdata for access control.
See Also
nimbly_mail_pre_send— short-circuit delivery before it startsnimbly_mail_mail_sent— react after successful deliverynimbly_mail_mailers— register a custom mailer
