WordPress “phpmailer_init” not working for me

I add the following code to the functions.php file.

add_action( 'phpmailer_init', 'my_phpmailer_example' );
function my_phpmailer_example( $phpmailer ) {
    $phpmailer->isSMTP();     
    $phpmailer->Host = SMTP_HOST;
    $phpmailer->SMTPAuth = SMTP_AUTH;
    $phpmailer->Port = SMTP_PORT;
    $phpmailer->Username = SMTP_USER;
    $phpmailer->Password = SMTP_PASS;
    $phpmailer->SMTPSecure = SMTP_SECURE;
    $phpmailer->From = SMTP_FROM;
    $phpmailer->FromName = SMTP_NAME;
}

and wp-config.php

// SMTP email settings
define( 'SMTP_USER', '{email}' );
define( 'SMTP_PASS', '{password}' );
define( 'SMTP_HOST', '{server}' );
define( 'SMTP_FROM', '{from}' );
define( 'SMTP_NAME', '{name}' );
define( 'SMTP_PORT', '465' );
define( 'SMTP_SECURE', 'ssl' );
define( 'SMTP_AUTH', true );

wp-login.php?action=lostpassword” when I go here, I enter my e-mail address and click the send button,

enter image description here

“Failed to send e-mail. Your site may not be properly configured to send e-mail.” I am getting this error.


But when I paste the same code into another WordPress with WooCommerce plugin installed,
I forgot my WooCommerce password. Mail can be successfully sent from the front-end design.

Where is the problem? Am I doing something wrong?


3rd step

add_action( 'wp_mail_failed', function ( $error ) {
    error_log( $error->get_error_message() );
} );

Not working (IDK). But I searched and found the code below,

add_action('wp_mail_failed', 'log_mailer_errors', 10, 1);
function log_mailer_errors( $wp_error ){
  $fn = ABSPATH . '/mail.log'; // say you've got a mail.log file in your server root
  $fp = fopen($fn, 'a');
  fputs($fp, "Mailer Error: " . $wp_error->get_error_message() ."\n");
  fclose($fp);
}

This gave me the following error

Mailer Error: Invalid address:  (From): wordpress@localhost

I found the relevant location from pluggable.php.

Since I was working on localhost, it was emitting an invalid e-mail address because it did not create a valid TLD.

        if ( ! isset( $from_email ) ) {
            // Get the site domain and get rid of www.
            $sitename = wp_parse_url( network_home_url(), PHP_URL_HOST );
            if ( 'www.' === substr( $sitename, 0, 4 ) ) {
                $sitename = substr( $sitename, 4 );
            }

            $from_email="wordpress@" . $sitename;
        }

This means, “$phpmailer->From = SMTP_FROM;” instead of “add_filter ('wp_mail_from', 'set_from');” should use.

function set_from()
{
    return 'name@domain.tld';
}

add_filter('wp_mail_from', 'set_from');

Works now.

I don’t understand why phpmailer_init doesn’t overwrite from information.


By the way, the sender still appears to be what I defined in ($phpmailer-> From).

I guess this is some kind of bug and should be reported to WordPress?

1
1

Where is the problem? Am I doing something wrong?

Your code seems fine to me, so despite I can’t give you a definitive answer on what/where exactly is the problem, I thought these might help you troubleshoot the issue:

  1. First and foremost, check your configuration — e.g. did you use the correct (SMTP) username/email, password, host?

    And make sure the SMTP_FROM value is a valid email addresss — e.g. user@example.com and not user@example or User <user@example.com>; otherwise, the email will not be sent.

  2. If you’re sure the configuration is good, then because you’re using SMTP, try enabling debugging like so — add this to your my_phpmailer_example() function:

    // Note: I assumed you're using at least WordPress version 5.5.0
    
    $phpmailer->SMTPDebug = PHPMailer\PHPMailer\SMTP::DEBUG_SERVER;
    

    Then visit the WordPress’s password reset page and try to reset your password, and on the next page, if PHPMailer was unable to send the email, then you’d see the error message displayed at the top of the page.

  3. If you can’t do the SMTP debugging above (which is just a quick troubleshooting, actually), then enable debugging in WordPress and use the wp_mail_failed hook to try to catch any errors in sending the email — add this to the functions file:

    add_action( 'wp_mail_failed', function ( $error ) {
        error_log( $error->get_error_message() );
    } );
    

    Then check the wp-content/debug.log file and see if there’s any relevant errors/information.

Update

(sorry, I couldn’t resist from saying this) I liked this question and I’m glad that you managed to fix the issue. 🙂

I guess this is some kind of bug and should be reported to WordPress?

Maybe, but it’s definitely a good thing if they could do something to avoid the issue from happening, particularly because there are many folks who develop on localhost.

But just in case you’re not aware of it, you could actually use virtual hosts like localhost.dev and that way, the issue could be avoided.

Since I was working on localhost, it was emitting an invalid e-mail
address because it did not create a valid TLD.

Yes, and I guessed you’ve already seen this note in the wp_mail() code (in pluggable.php):

/*
 * If we don't have an email from the input headers, default to wordpress@$sitename
 * Some hosts will block outgoing mail from this address if it doesn't exist,
 * but there's no easy alternative. Defaulting to admin_email might appear to be
 * another option, but some hosts may refuse to relay mail from an unknown domain.
 * See https://core.trac.wordpress.org/ticket/5007.
 */

So yes, either use the wp_mail_from hook or as I said above, use a virtual host.

I don’t understand why phpmailer_init doesn’t overwrite from
information.

Because WordPress/wp_mail() calls $phpmailer->setFrom() to set the From property and if PHPMailer throws an exception/error (e.g. due to invalid email address), then wp_mail() returns false and never fires the phpmailer_init hook.

But the wp_mail_failed hook does get fired, therefore it’s a very good way to catch any errors which may occur when sending an email using wp_mail(). ( and sorry, I didn’t mention it earlier because I thought the SMTP debugging would work for you as well 🙂 )

By the way, the sender still appears to be what I defined in ($phpmailer->From).

Yes, that’s normal, because the setFrom() is called before the phpmailer_init hook is fired.

And actually, when I said “enable debugging in WordPress”, I meant set both the WP_DEBUG and WP_DEBUG_LOG constants to true; and if error_log() doesn’t write to wp-content/debug.log (or the file defined via the WP_DEBUG_LOG constant), then you could use the 2nd and 3rd parameters for error_log() like so:

add_action( 'wp_mail_failed', function ( $error ) {
    // the "3" means write the message to the file as defined in the third parameter
    error_log( $error->get_error_message(), 3, WP_CONTENT_DIR . '/debug.log' );
} );

But anyway, I hope this answer/update and the question also help someone else. =) Happy coding!

Leave a Comment