3

Using the wp_mail filter I add a "Reply-To" to the header with my email address, so clients can reply directly to the email to contact me if there's any issues. I only want this on WordPress "system" emails, like emails for new accounts, password resets, etc. If a plugin sends an email using wp_mail() I don't want to set the Reply To address. Is there some way to know when it's WordPress core sending an email rather than a plugin?

Here's what I'm using to set the Reply-To (from a custom class):

public function __construct() {
    add_filter( 'wp_mail', array( $this, 'setReplyTo' ), PHP_INT_MAX );
}

public function setReplyTo( array $args ) : array
{
    $reply_to = 'Reply-To: My Name <[email protected]>';
    
    if ( ! empty( $args['headers'] ) ) {
        if ( ! is_array( $args['headers'] ) ) {
            $args['headers'] = array_filter( explode( "\n", str_replace( "\r\n", "\n", $args['headers'] ) ) );
        }
        // Remove existing Reply-To headers
        $args['headers'] = array_filter( $args['headers'], function( $header ) {
            return strpos( strtolower( $header ), 'reply-to' ) !== 0;
        } );
    }
    else {
        $args['headers'] = [];
    }

    $args['headers'][] = $reply_to;

    return $args;
}

Nothing in the $args parameter appears to contains anything I can use to accomplish this. Hopefully there's some way to know what is initiating the emails. Thanks!

4
  • 1
    WP doesn't differentiate "system" emails, but, perhaps you can filter based on the recipient? E.g. system emails are going to be sent to very specific people by vanilla WP sites, mainly site administrators. Heuristics are the closest you'll get but wether that's feasible depends on the kinds of emails your plugins are sending out. Another avenue to explore is not to use this filter to do that check, but to perform the check somewhere else, e.g. in the lost password filter and set a flag which you then look for in this filter
    – Tom J Nowell
    Commented Aug 15, 2023 at 10:55
  • @TomJNowell Thanks! I was thinking of checking if the recipient email is the administrator or one of the WordPress users, which could work, but obviously if a WordPress user/admin is the recipient of a non-system email then it won't work. That's not a big deal though. I really like the idea about hooking into the various filters and setting a flag. I'm going to explore that some more. Thanks again!
    – Gavin
    Commented Aug 15, 2023 at 11:12
  • 2
    Few years ago I suggested using keywords/namespaces in wp_mail here core.trac.wordpress.org/ticket/53829 No news but welcome to lobby for it there and take the work further.
    – birgire
    Commented Aug 15, 2023 at 11:44
  • 1
    @birgire That would be an excellent solution. I added a comment on there too.
    – Gavin
    Commented Aug 15, 2023 at 13:59

1 Answer 1

2

I agree with @birgire that #53829-core would be the ideal way to do this. If you need a hack that works in the meantime, though, then you can examine the filenames in the call stack.

add_filter( 'wp_mail', 'modify_system_emails' );

function modify_system_emails( array $attributes ) : array {
    $wp_mail_caller = get_wp_mail_caller();

    if ( is_core_file( $wp_mail_caller['file'] ) ) {
        $attributes['subject'] .= ' -- (system)';
    }

    return $attributes;
}

function get_wp_mail_caller() : array {
    $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS );

    foreach ( $backtrace as $call ) {
        if ( 'wp_mail' === $call['function'] ) {
            return $call;
        }
    }

    throw new Exception( "`wp_mail()` wasn't found in the backtrace." );
}

function is_core_file( string $filename ) : bool {
    $relative_path = str_replace( ABSPATH, '', $filename );
    $parts         = explode( '/', $relative_path );

    if ( in_array( $parts[0], [ 'wp-admin', 'wp-includes' ], true ) ) {
        return true;
    } elseif ( preg_match( '#^wp-.*\.php$#', $parts[0] ) ) {
        return true;
    }

    return false;
}

You can test that with some code like:

add_action( 'init', function() {
    wp_mail( '[email protected]', 'not a system email', 'nosirree' ); // This won't modify the subject.
    wp_site_admin_email_change_notification( '[email protected]', '[email protected]', 'foo' ); // This will modify the subject.
} );
2
  • That's actually a really cool solution. It's hack-ish for sure but it's a solid approach. Thanks!
    – Gavin
    Commented Aug 16, 2023 at 9:29
  • You're welcome :)
    – Ian Dunn
    Commented Aug 16, 2023 at 15:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.