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

咳咳,大家好!今天老夫就来给大家扒一扒 WordPress 密码修改通知邮件的内裤…不是,是源码! 咱们细细地解剖一下 wp_password_change_notification() 函数,看看它到底是怎么把密码修改的“喜讯”传递给用户的。

开场白:为啥要知道这个?

你可能会问:“密码修改通知邮件而已,知道它干嘛?WordPress 自己搞定不就好了?” 嗯,没错,默认情况下是这样。但是,如果你想:

  • 自定义邮件内容: 想让邮件更个性化,更符合你的品牌调性。
  • 添加额外信息: 比如,显示修改密码的 IP 地址,提醒用户注意安全。
  • 更换邮件发送方式: 不用 WordPress 自带的 wp_mail() 函数,而是用更专业的 SMTP 服务。
  • Debug 邮件问题: 邮件发送失败了,你想知道哪里出了问题。

那么,了解 wp_password_change_notification() 的源码就非常有必要了。

正餐:wp_password_change_notification() 函数解剖

这个函数定义在 wp-includes/pluggable.php 文件中。我们来一步步分析它的源码:

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

    $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );

    $message = sprintf(
        /* translators: 1: Site name, 2: Username */
        __( 'Hi %1$s,' ) . "rnrn" .
        __( 'This notice confirms that the password for your account on %2$s has been changed.' ) . "rnrn" .
        __( 'If you did not change your password, please contact the Site Administrator immediately.' ) . "rnrn" .
        __( 'Regards,' ) . "rn" .
        __( 'All at %2$s' ) . "rn" .
        __( 'https://wordpress.org/' ),
        wp_specialchars_decode( $user->display_name ),
        $blogname
    );

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

让我们拆解一下:

  1. 函数声明和文档注释:

    /**
     * Notifies the user of a password change.
     *
     * @since 3.6.0
     *
     * @param WP_User $user         User object.
     */
    function wp_password_change_notification( $user ) {
    • @since 3.6.0:说明这个函数是从 WordPress 3.6 版本开始引入的。
    • @param WP_User $user:说明这个函数接收一个 WP_User 类型的参数,也就是用户对象。
  2. do_action( 'password_changed', $user );

    do_action( 'password_changed', $user );
    • 这是一个非常重要的 Hook! 它允许开发者在密码修改后执行自定义操作。 例如,你可以在这里记录密码修改日志,或者发送短信通知。
    • do_action() 函数是 WordPress 的核心 Hook 机制的一部分。 它会触发所有绑定到 password_changed 这个 action 上的函数。
  3. $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );

    $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
    • 获取网站名称 (blogname),并进行解码。 get_option( 'blogname' ) 从 WordPress 的选项表中获取网站名称。 wp_specialchars_decode() 用于解码 HTML 实体,确保网站名称显示正确。 ENT_QUOTES 参数表示同时解码单引号和双引号。
  4. 邮件内容构建 (重点!):

    $message = sprintf(
        /* translators: 1: Site name, 2: Username */
        __( 'Hi %1$s,' ) . "rnrn" .
        __( 'This notice confirms that the password for your account on %2$s has been changed.' ) . "rnrn" .
        __( 'If you did not change your password, please contact the Site Administrator immediately.' ) . "rnrn" .
        __( 'Regards,' ) . "rn" .
        __( 'All at %2$s' ) . "rn" .
        __( 'https://wordpress.org/' ),
        wp_specialchars_decode( $user->display_name ),
        $blogname
    );
    • 这里使用 sprintf() 函数格式化邮件内容。
    • __( '...' ) 是 WordPress 的国际化函数,用于翻译字符串。
    • %1$s%2$s 是占位符,分别会被 wp_specialchars_decode( $user->display_name ) (用户显示名称) 和 $blogname (网站名称) 替换。
    • rn 是换行符。
    • 注意: 邮件内容是纯文本的。
  5. 发送邮件:

    wp_mail( $user->user_email, sprintf( __( '[%s] Password Changed' ), $blogname ), $message );
    • wp_mail() 函数是 WordPress 中用于发送邮件的核心函数。
    • $user->user_email:收件人邮箱地址。
    • sprintf( __( '[%s] Password Changed' ), $blogname ):邮件主题,同样包含了网站名称。
    • $message:邮件内容。

wp_mail() 函数的幕后英雄

wp_mail() 函数本身也值得我们深入了解。 它位于 wp-includes/pluggable.php 文件中。 简单来说,wp_mail() 会:

  1. 构建邮件头部: 包括发件人地址、回复地址、Content-Type 等。
  2. 应用过滤器: 允许开发者修改邮件的各个方面,例如 wp_mail_from (发件人地址), wp_mail_from_name (发件人名称), wp_mail_content_type (内容类型), wp_mail_charset (字符集), wp_mail_headers (邮件头部), wp_mail (整个邮件数组)。
  3. 调用 wp_mail() 实现发送: 最终依赖于 PHP 的 mail() 函数,或者通过 SMTP 等方式发送邮件(如果配置了相应的插件)。

表格总结:核心步骤

步骤 描述 涉及的函数/变量
1. 触发 Action 允许开发者在密码修改后执行自定义操作。 do_action( 'password_changed', $user )
2. 获取网站名称 获取网站名称并解码。 get_option( 'blogname' ), wp_specialchars_decode()
3. 构建邮件内容 使用 sprintf() 和国际化函数 __() 构建邮件内容。 sprintf(), __(), $user->display_name
4. 发送邮件 使用 wp_mail() 函数发送邮件。 wp_mail(), $user->user_email

如何定制密码修改通知邮件?

了解了 wp_password_change_notification() 的源码,我们就可以开始定制邮件了。 主要有两种方法:

  1. 使用 password_changed Action:

    这是最推荐的方法,因为它不会直接修改 WordPress 的核心代码。

    add_action( 'password_changed', 'my_custom_password_notification', 10, 1 );
    
    function my_custom_password_notification( $user ) {
        // 构建自定义邮件内容
        $subject = sprintf( '[%s] 密码已修改', get_option( 'blogname' ) );
        $message = sprintf(
            '尊敬的 %s:' . "rnrn" .
            '您的密码已成功修改。' . "rnrn" .
            '如果您没有修改密码,请立即联系我们!' . "rnrn" .
            '祝您愉快!',
            $user->display_name
        );
    
        // 发送邮件
        wp_mail( $user->user_email, $subject, $message );
    }
    • add_action( 'password_changed', 'my_custom_password_notification', 10, 1 ):将 my_custom_password_notification 函数绑定到 password_changed action 上。 10 是优先级,1 是接收的参数数量(这里是 $user 对象)。
    • my_custom_password_notification 函数中,你可以构建自己的邮件内容,并使用 wp_mail() 函数发送。
    • 注意: 为了避免发送两封邮件,你需要在 my_custom_password_notification 函数中移除 WordPress 默认的 wp_password_change_notification 函数。 可以使用 remove_action() 函数:

      remove_action( 'password_changed', 'wp_password_change_notification' );

      完整的代码如下:

      add_action( 'password_changed', 'my_custom_password_notification', 10, 1 );
      
      function my_custom_password_notification( $user ) {
          // 移除默认的密码修改通知邮件
          remove_action( 'password_changed', 'wp_password_change_notification' );
      
          // 构建自定义邮件内容
          $subject = sprintf( '[%s] 密码已修改', get_option( 'blogname' ) );
          $message = sprintf(
              '尊敬的 %s:' . "rnrn" .
              '您的密码已成功修改。' . "rnrn" .
              '如果您没有修改密码,请立即联系我们!' . "rnrn" .
              '祝您愉快!',
              $user->display_name
          );
      
          // 发送邮件
          wp_mail( $user->user_email, $subject, $message );
      }
  2. 使用 wp_mail 过滤器:

    这种方法可以让你修改 wp_mail() 函数的参数,例如邮件头部、内容类型等。

    add_filter( 'wp_mail', 'my_custom_wp_mail' );
    
    function my_custom_wp_mail( $args ) {
        // 只修改密码修改通知邮件
        if ( strpos( $args['subject'], 'Password Changed' ) !== false ) {
            // 修改邮件主题
            $args['subject'] = sprintf( '[%s] 您的密码已修改', get_option( 'blogname' ) );
    
            // 修改邮件内容
            $args['message'] = sprintf(
                '尊敬的 %s:' . "rnrn" .
                '我们检测到您的密码已成功修改。' . "rnrn" .
                '如果您没有进行此操作,请立即联系我们!' . "rnrn" .
                '祝您愉快!',
                wp_get_current_user()->display_name
            );
    
            // 修改邮件头部,例如设置 Content-Type 为 HTML
            $args['headers'] = array( 'Content-Type: text/html; charset=UTF-8' );
        }
    
        return $args;
    }
    • add_filter( 'wp_mail', 'my_custom_wp_mail' ):将 my_custom_wp_mail 函数绑定到 wp_mail filter 上。
    • $args 是一个数组,包含了 wp_mail() 函数的所有参数,例如 to (收件人), subject (主题), message (内容), headers (头部), attachments (附件)。
    • my_custom_wp_mail 函数中,你可以修改这些参数,然后返回修改后的 $args 数组。
    • 注意: 你需要判断当前邮件是否是密码修改通知邮件,可以使用 strpos( $args['subject'], 'Password Changed' ) !== false 来判断。 否则,你的修改会影响到所有使用 wp_mail() 函数发送的邮件。

更高级的玩法:使用 SMTP

WordPress 默认使用 PHP 的 mail() 函数发送邮件,但这种方式不太可靠,容易被标记为垃圾邮件。 更推荐的做法是使用 SMTP (Simple Mail Transfer Protocol) 服务器。

有很多 WordPress 插件可以让你配置 SMTP,例如:

  • WP Mail SMTP by WPForms: 非常流行且易于使用的 SMTP 插件。
  • Easy WP SMTP: 另一个简单易用的 SMTP 插件。
  • Post SMTP Mailer/Email Log: 功能更强大的 SMTP 插件,支持邮件日志。

安装并配置好 SMTP 插件后,WordPress 就会通过 SMTP 服务器发送邮件,而不是使用 PHP 的 mail() 函数,从而提高邮件的送达率。

代码示例:添加修改密码的 IP 地址

这是一个更高级的定制示例,我们将在密码修改通知邮件中添加修改密码的 IP 地址。

add_action( 'password_changed', 'my_custom_password_notification_with_ip', 10, 1 );

function my_custom_password_notification_with_ip( $user ) {
    // 移除默认的密码修改通知邮件
    remove_action( 'password_changed', 'wp_password_change_notification' );

    // 获取用户 IP 地址
    $ip_address = $_SERVER['REMOTE_ADDR'];

    // 构建自定义邮件内容
    $subject = sprintf( '[%s] 密码已修改', get_option( 'blogname' ) );
    $message = sprintf(
        '尊敬的 %s:' . "rnrn" .
        '您的密码已于 %s 修改。' . "rnrn" .
        '修改密码的 IP 地址为:%s' . "rnrn" .
        '如果您没有修改密码,请立即联系我们!' . "rnrn" .
        '祝您愉快!',
        $user->display_name,
        date( 'Y-m-d H:i:s' ),
        $ip_address
    );

    // 发送邮件
    wp_mail( $user->user_email, $subject, $message );
}
  • $_SERVER['REMOTE_ADDR']:获取客户端 IP 地址。 注意: 这种方式获取的 IP 地址可能不准确,特别是当用户使用了代理服务器时。 更可靠的方法是使用一些 IP 地址检测服务。
  • date( 'Y-m-d H:i:s' ):获取当前时间,并格式化为 YYYY-MM-DD HH:MM:SS 格式。

总结:定制邮件,掌握主动权

wp_password_change_notification() 函数虽然简单,但它却是密码安全的重要一环。 通过理解它的源码,我们可以定制邮件内容、添加额外信息、更换邮件发送方式,从而更好地保障用户的账户安全,提升用户体验。

记住,定制邮件的关键在于使用 WordPress 的 Hook 机制,避免直接修改核心代码。 同时,选择合适的 SMTP 服务,可以提高邮件的送达率。

好了,今天的讲座就到这里。 希望大家以后也能像解剖 wp_password_change_notification() 一样,勇敢地探索 WordPress 的源码,掌握更多的主动权!

发表回复

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