深入理解 `wp_password_change_notification()` 函数的源码,它是如何发送密码修改通知邮件的?

各位听众,早上好!今天咱们来扒一扒 WordPress 里面的一个“神秘”函数:wp_password_change_notification()。 别看它名字长,其实干的事儿挺简单,就是当你改了 WordPress 密码,或者管理员帮你重置了密码后,它负责给你发个邮件,告诉你一声:“嘿,你的密码变啦!别忘了哈!”

但魔鬼藏在细节里嘛,今天咱们就来细细地拆解一下这个函数,看看它是怎么一步一步完成这个“通知”任务的。

一、函数概览:wp_password_change_notification() 的庐山真面目

首先,咱们来看一下 wp_password_change_notification() 函数的源码,它通常位于 wp-includes/pluggable.php 文件中。 (注: WordPress 版本不同,具体位置可能略有差异,但一般都在 wp-includes 目录下)

/**
 * Notifies the user that their password has changed.
 *
 * @since 4.3.0
 *
 * @param WP_User $user User object.
 */
function wp_password_change_notification( $user ) {
    /**
     * Fires after the user's password has been changed.
     *
     * @since 4.3.0
     *
     * @param WP_User $user User object.
     */
    do_action( 'password_changed', $user );

    $switched_locale = switch_to_locale( get_user_locale( $user ) );

    $message = __( 'Hi %s,' ) . "rnrn";
    /* translators: Site name. */
    $message .= sprintf( __( 'This notice confirms that your password was changed on %s.' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ) . "rnrn";
    $message .= __( 'If you did not change your password, please contact the Site Administrator.' ) . "rnrn";
    $message .= __( 'This email has been sent to %s' ) . "rn";

    wp_mail( $user->user_email, wp_specialchars_decode( sprintf( __( '[%s] Password Changed' ), get_option( 'blogname' ), ENT_QUOTES ) ), sprintf( $message, wp_specialchars_decode( $user->display_name, ENT_QUOTES ), $user->user_email ) );

    if ( $switched_locale ) {
        restore_previous_locale();
    }
}

看起来是不是很简单? 别着急,咱们一步一步来拆解。

二、源码逐行分析:庖丁解牛式讲解

  1. do_action( 'password_changed', $user );:

    • 作用: 这是一个 WordPress 的钩子 (Hook)。 简单来说,它就像一个预留的“插槽”,允许其他的插件或主题在这个函数执行的特定时刻插入自定义的代码。
    • 意义: 其他插件或主题可以通过监听 password_changed 这个 action,在密码修改后执行一些额外的操作,比如记录日志、发送短信通知等等。
    • 举例: 假设你安装了一个安全插件,它可以监听这个 action,并在每次密码修改后,检查密码的强度,或者强制用户进行二次验证。
    add_action( 'password_changed', 'my_custom_password_changed_function' );
    
    function my_custom_password_changed_function( $user ) {
        // 在这里执行你的自定义操作,比如记录日志
        error_log( 'User ' . $user->user_login . ' changed their password.' );
    }
  2. $switched_locale = switch_to_locale( get_user_locale( $user ) );:

    • 作用: 切换到用户的语言环境。 WordPress 支持多语言,这个函数会根据用户的设置,将当前网站的语言环境切换到用户的首选语言。
    • get_user_locale( $user ): 获取用户的语言环境代码(例如:zh_CNen_US)。
    • switch_to_locale(): 切换 WordPress 的语言环境。
    • 意义: 确保发送给用户的邮件是用用户首选的语言编写的。 想象一下,如果你的用户是日本人,但是收到的邮件却是英文的,那就太尴尬了。
  3. 邮件内容构建 ( $message ):

    • 作用: 构建邮件的正文内容。
    • __( 'Hi %s,' ): 使用 __() 函数进行国际化处理。 __() 函数会将字符串翻译成当前语言环境对应的翻译文本。 %s 是一个占位符,稍后会被用户的显示名称替换。
    • sprintf( __( 'This notice confirms that your password was changed on %s.' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ): 构建包含网站名称的句子。 get_option( 'blogname' ) 获取网站的名称,wp_specialchars_decode() 解码 HTML 实体,确保网站名称显示正确。
    • 其他内容: 包含提示用户如果不是自己修改的密码,应该联系管理员的说明。
    $message = __( 'Hi %s,' ) . "rnrn";
    /* translators: Site name. */
    $message .= sprintf( __( 'This notice confirms that your password was changed on %s.' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ) . "rnrn";
    $message .= __( 'If you did not change your password, please contact the Site Administrator.' ) . "rnrn";
    $message .= __( 'This email has been sent to %s' ) . "rn";

    表格:邮件内容构建关键函数说明

    函数 作用
    __() 国际化处理,将字符串翻译成当前语言环境对应的翻译文本。
    sprintf() 格式化字符串,将变量插入到字符串的占位符中。
    get_option( 'blogname' ) 获取网站的名称。
    wp_specialchars_decode() 解码 HTML 实体,确保字符串显示正确。
  4. wp_mail( $user->user_email, wp_specialchars_decode( sprintf( __( '[%s] Password Changed' ), get_option( 'blogname' ), ENT_QUOTES ) ), sprintf( $message, wp_specialchars_decode( $user->display_name, ENT_QUOTES ), $user->user_email ) );:

    • 作用: 发送邮件。 这是整个函数的核心部分。
    • wp_mail(): WordPress 自带的邮件发送函数。 它封装了 PHP 的 mail() 函数,并提供了一些额外的功能,比如可以设置邮件头、附件等等。
    • 参数:
      • $user->user_email: 用户的邮箱地址。
      • wp_specialchars_decode( sprintf( __( '[%s] Password Changed' ), get_option( 'blogname' ), ENT_QUOTES ) ): 邮件的主题。 同样使用了 sprintf()get_option() 函数来构建包含网站名称的主题。
      • sprintf( $message, wp_specialchars_decode( $user->display_name, ENT_QUOTES ), $user->user_email ): 邮件的正文内容。 将用户的显示名称和邮箱地址插入到之前构建的 $message 变量中。

    表格:wp_mail() 函数参数说明

    参数 说明
    $to 收件人的邮箱地址。
    $subject 邮件的主题。
    $message 邮件的正文内容。
    $headers 邮件头信息,可以设置发件人、抄送、密送等等。(可选)
    $attachments 邮件附件。(可选)
  5. if ( $switched_locale ) { restore_previous_locale(); }:

    • 作用: 如果之前切换了语言环境,则恢复到之前的语言环境。
    • 意义: 避免影响网站的其他部分。 因为切换语言环境可能会影响到网站的显示,所以需要在发送完邮件后恢复到之前的状态。

三、深入 wp_mail():邮件发送的幕后功臣

wp_mail() 函数是 WordPress 发送邮件的关键。 咱们再深入了解一下它。

wp_mail() 函数位于 wp-includes/pluggable.php 文件中。 它的基本结构如下:

function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) {
    // (大量代码...省略)

    /**
     * Filters the wp_mail() arguments.
     *
     * @since 2.2.0
     *
     * @param array $args An array of wp_mail() arguments.
     */
    $args = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) );

    if ( isset( $args['to'] ) ) {
        $to = $args['to'];
    }

    if ( ! is_array( $to ) ) {
        $to = explode( ',', $to );
    }

    if ( isset( $args['subject'] ) ) {
        $subject = $args['subject'];
    }

    if ( isset( $args['message'] ) ) {
        $message = $args['message'];
    }

    if ( isset( $args['headers'] ) ) {
        $headers = $args['headers'];
    }

    if ( isset( $args['attachments'] ) ) {
        $attachments = $args['attachments'];
    }

    if ( ! is_array( $attachments ) ) {
        $attachments = explode( "n", str_replace( "rn", "n", $attachments ) );
    }

    $phpmailer = new PHPMailer( true );

    try {
        // (更多代码...省略)

        // Set up the mailer.
        $phpmailer->IsMail(); // Use PHP's mail()

        // Set the From address.
        // If we're using WordPress to send the email, we'll set the From address.
        // Small handling of the name, which should be HTML-safe.
        if ( $from_email ) {
            $phpmailer->SetFrom( $from_email, $from_name );
        } else {
            // If we don't have a name and email from the tag, get the site info.
            // If the site name is HTML-safe, use it.
            if ( wp_startswith( $sitename, '<' ) ) {
                $sitename = wp_specialchars_decode( $sitename, ENT_QUOTES );
            }
            $phpmailer->SetFrom( $wp_email, $sitename );
        }

        // Set destination address(es).
        if ( !is_array( $to ) ) {
            $to = explode(',', $to);
        }

        foreach ( (array) $to as $recipient ) {
            try {
                $phpmailer->AddAddress( trim( $recipient ) );
            } catch ( phpmailerException $e ) {
                continue;
            }
        }

        // Set the subject.
        $phpmailer->Subject = $subject;

        // Set the message body.
        $phpmailer->Body = $message;
        $phpmailer->AltBody = strip_tags( $message );
        if ( ! empty( $headers ) ) {
            if ( is_array( $headers ) ) {
                foreach ( $headers as $header ) {
                    $phpmailer->AddCustomHeader( $header );
            }
            } else {
                // Explode the headers out, so that we can handle them as individual headers.
                $headers = explode( "n", str_replace( "rn", "n", $headers ) );

                foreach ( (array) $headers as $header ) {
                    if ( strpos( $header, ':' ) === false ) {
                        continue;
                    }

                    // Explode out the header/value pairs.
                    list( $name, $content ) = explode( ':', trim( $header ), 2 );

                    // Cleanup the name and value.
                    $name    = trim( $name );
                    $content = trim( $content );

                    $phpmailer->AddCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
                }
            }
        }

        // Add attachments.
        foreach ( $attachments as $attachment ) {
            try {
                $phpmailer->AddAttachment( $attachment );
            } catch ( phpmailerException $e ) {
                continue;
            }
        }

        // Send!
        $phpmailer->Send();
        $sent = true;
    } catch ( phpmailerException $e ) {
        $sent = false;
    }

    return $sent;
}
  • 使用了 PHPMailer: wp_mail() 函数实际上是基于 PHPMailer 这个强大的 PHP 邮件发送类库。 PHPMailer 提供了丰富的功能,比如支持 SMTP 认证、HTML 邮件、附件等等。
  • 过滤器 (Filter): apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) ); 这是一个过滤器,允许其他的插件或主题修改 wp_mail() 函数的参数。 这为我们提供了极大的灵活性,可以自定义邮件的内容、主题、发件人等等。
  • SMTP 设置: 默认情况下,wp_mail() 函数使用 PHP 的 mail() 函数发送邮件。 但是,mail() 函数的可靠性通常比较差,很容易被邮件服务器认为是垃圾邮件。 因此,建议配置 WordPress 使用 SMTP 发送邮件。 有很多插件可以帮助你配置 SMTP,比如 WP Mail SMTP、Easy WP SMTP 等等。
  • 发件人设置: WordPress 尝试确定合适的发件人地址。如果没有明确设置,它会使用 [email protected] 这样的地址,并用站点名称作为发件人名称。

四、常见问题与注意事项

  1. 邮件发送失败:

    • 原因: mail() 函数不可靠、SMTP 配置不正确、邮件服务器屏蔽、域名 DNS 设置不正确等等。
    • 解决方法: 配置 SMTP、检查域名 DNS 设置、联系邮件服务商。
  2. 邮件进入垃圾箱:

    • 原因: 邮件内容包含敏感词汇、发件人信誉低、域名没有配置 SPF、DKIM 记录等等。
    • 解决方法: 避免使用敏感词汇、配置 SPF、DKIM 记录、提高发件人信誉。
  3. 自定义邮件内容:

    • 方法: 使用 wp_mail 过滤器修改邮件参数。
    add_filter( 'wp_mail', 'my_custom_wp_mail_function' );
    
    function my_custom_wp_mail_function( $args ) {
        // 修改邮件主题
        $args['subject'] = '[My Site] ' . $args['subject'];
    
        // 修改邮件正文
        $args['message'] = 'This is a custom message: ' . $args['message'];
    
        return $args;
    }
  4. 使用 HTML 邮件:

    • 方法: 修改邮件头,设置 Content-Typetext/html
    add_filter( 'wp_mail', 'my_custom_wp_mail_html_function' );
    
    function my_custom_wp_mail_html_function( $args ) {
        $args['headers'] = 'Content-Type: text/html; charset=UTF-8';
        return $args;
    }

五、总结与展望

wp_password_change_notification() 函数虽然简单,但却体现了 WordPress 的设计哲学:简单、灵活、可扩展。 通过钩子和过滤器,我们可以轻松地自定义 WordPress 的行为,满足各种各样的需求。

掌握了 wp_password_change_notification() 函数的原理,你就可以更好地理解 WordPress 的邮件发送机制,并解决可能遇到的问题。 希望今天的讲座对你有所帮助!

六、代码示例:一个完整的自定义密码修改通知邮件的例子

// 1. 禁用默认的密码修改通知
remove_action( 'password_changed', 'wp_password_change_notification' );

// 2. 添加自定义的密码修改通知函数
add_action( 'password_changed', 'my_custom_password_change_notification' );

function my_custom_password_change_notification( $user ) {
    $user_locale = get_user_locale( $user );
    $switched_locale = switch_to_locale( $user_locale );

    $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
    $display_name = wp_specialchars_decode( $user->display_name, ENT_QUOTES );
    $user_email = $user->user_email;

    // 构建邮件主题
    $subject = sprintf( __( '[%s] 密码已更改' ), $site_name );

    // 构建邮件正文
    $message = sprintf( __( '尊敬的 %s:' ), $display_name ) . "rnrn";
    $message .= sprintf( __( '您好!您的密码已在 %s 上成功更改。' ), $site_name ) . "rnrn";
    $message .= __( '如果您并未执行此操作,请立即联系网站管理员。' ) . "rnrn";
    $message .= sprintf( __( '此邮件已发送至 %s。' ), $user_email ) . "rnrn";
    $message .= __( '感谢您的使用!' ) . "rn";

    // 构建邮件头,设置 HTML 格式
    $headers = array('Content-Type: text/html; charset=UTF-8');

    // 使用 wp_mail() 发送邮件
    wp_mail( $user_email, $subject, $message, $headers );

    if ( $switched_locale ) {
        restore_previous_locale();
    }
}

// 可选:自定义 wp_mail() 函数的参数,例如设置发件人
add_filter( 'wp_mail', 'my_custom_wp_mail_from' );

function my_custom_wp_mail_from( $args ) {
    $args['sender_email'] = '[email protected]'; // 替换为你的发件人邮箱
    $args['sender_name'] = 'Example Website'; // 替换为你的发件人名称
    $args['headers'][] = 'From: ' . $args['sender_name'] . ' <' . $args['sender_email'] . '>';
    return $args;
}

代码解释:

  1. 禁用默认通知: remove_action( 'password_changed', 'wp_password_change_notification' ); 这行代码先移除 WordPress 默认的密码修改通知函数,防止发送重复的邮件。
  2. 添加自定义函数: add_action( 'password_changed', 'my_custom_password_change_notification' ); 这行代码将我们自定义的 my_custom_password_change_notification() 函数添加到 password_changed action 中。
  3. 构建邮件内容: my_custom_password_change_notification() 函数负责构建邮件的主题、正文和头部信息。 我们使用了 sprintf()__() 函数来进行国际化和格式化。
  4. 发送邮件: wp_mail( $user_email, $subject, $message, $headers ); 这行代码使用 wp_mail() 函数发送邮件。
  5. 自定义发件人 (可选): my_custom_wp_mail_from() 函数使用 wp_mail 过滤器来修改邮件的发件人信息。 这可以帮助你设置一个更加专业的发件人邮箱和名称。

请注意,你需要将 [email protected]Example Website 替换为你自己的发件人邮箱和名称。 另外,确保你的 WordPress 站点已经配置了正确的 SMTP 设置,才能成功发送邮件。

希望这个例子能够帮助你更好地理解如何自定义 WordPress 的密码修改通知邮件!感谢大家的参与!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注