剖析 `wp_set_current_user()` 函数的源码,它如何设置全局变量 `$current_user`?

大家好!欢迎来到今天的“WordPress源码扒皮”讲座!

今天我们要聊聊WordPress里一个非常核心的函数:wp_set_current_user()。这个函数的重要性,就好比一个国家的总统,负责确定当前是谁在操作WordPress,权限是什么,等等。 咱们要把它扒个底朝天,看看它是怎么把全局变量 $current_user 给安排上的。

一、wp_set_current_user() 是何方神圣?

简单来说,wp_set_current_user() 的作用就是设置当前用户。它接受一个用户ID作为参数,然后根据这个ID去数据库里捞取用户数据,并把这些数据填充到一个叫做 $current_user 的全局变量里。有了 $current_user,WordPress 才能知道当前用户是谁,才能进行权限判断、个性化显示等等操作。

二、源码剖析:一步一步解开它的面纱

咱们直接上代码,边看边解释:

/**
 * Sets the current user.
 *
 * @since 2.0.0
 *
 * @global WP_User $current_user
 *
 * @param int|WP_User $id User ID.
 */
function wp_set_current_user( $id ) {
    global $current_user;

    if ( ! ( $id instanceof WP_User ) ) {
        $id = (int) $id; // 确保是整数
    }

    if ( empty( $id ) ) {
        $current_user = new WP_User( 0 ); // 设置为匿名用户
        return;
    }

    if ( is_object( $id ) && isset( $id->ID ) ) {
        $id = $id->ID; // 如果传入的是 WP_User 对象,提取 ID
    }

    if ( ! ( $current_user instanceof WP_User ) ) {
        $current_user = new WP_User();
    }

    if ( get_current_user_id() == $id && isset( $current_user->ID ) && ( (int) $current_user->ID === (int) $id ) ) {
        return; // 如果已经是当前用户,直接返回
    }

    $current_user = get_userdata( $id ); // 从数据库获取用户数据

    if ( ! $current_user ) {
        $current_user = new WP_User( 0 ); // 如果用户不存在,设置为匿名用户
        return;
    }

    wp_cache_set( 'current_user', $current_user, 'users' ); // 缓存当前用户

    do_action( 'set_current_user', $current_user ); // 触发一个钩子,方便扩展
}

咱们来一行一行地解读:

  1. function wp_set_current_user( $id ) {: 定义函数,接受一个参数 $id,这就是我们要设置的用户的ID。

  2. global $current_user;: 声明 $current_user 为全局变量。 这意味着我们在函数内部可以访问和修改全局作用域中的 $current_user 变量。 如果没有这句,函数内部的 $current_user 只是一个局部变量,修改它不会影响全局变量。

  3. if ( ! ( $id instanceof WP_User ) ) { $id = (int) $id; }: 检查 $id 是否是 WP_User 对象。如果不是,就强制转换为整数。 这样做是为了确保 $id 是一个有效的用户ID,方便后续操作。

  4. if ( empty( $id ) ) { $current_user = new WP_User( 0 ); return; }: 如果 $id 为空,则创建一个匿名用户。 new WP_User(0) 会创建一个 WP_User 对象,其ID为0,表示匿名用户或未登录用户。 然后函数直接返回,不再执行后续操作。

  5. if ( is_object( $id ) && isset( $id->ID ) ) { $id = $id->ID; }: 如果 $id 是一个 WP_User 对象,并且包含 ID 属性,则提取出 ID 属性的值。 这样做的目的是为了兼容传入 WP_User 对象的情况,直接使用对象的 ID。

  6. if ( ! ( $current_user instanceof WP_User ) ) { $current_user = new WP_User(); }: 检查 $current_user 是否是 WP_User 的实例。如果不是,就创建一个新的 WP_User 对象。 这通常发生在第一次调用 wp_set_current_user() 的时候,$current_user 还没有被初始化。

  7. if ( get_current_user_id() == $id && isset( $current_user->ID ) && ( (int) $current_user->ID === (int) $id ) ) { return; }: 这是一个优化措施。如果当前用户已经是我们要设置的用户,就直接返回,不再重复设置。 get_current_user_id() 函数会获取当前用户的ID。 isset( $current_user->ID ) 确保 $current_user 对象已经被正确初始化,并且包含 ID 属性。

  8. $current_user = get_userdata( $id );: 这是最核心的一步。使用 get_userdata() 函数根据 $id 从数据库中获取用户数据,并赋值给 $current_userget_userdata() 函数会返回一个 WP_User 对象,包含了用户的各种信息,如用户名、邮箱、角色等等。

  9. if ( ! $current_user ) { $current_user = new WP_User( 0 ); return; }: 如果 get_userdata() 没有找到对应的用户(例如,ID不存在),则创建一个匿名用户。 这是一种安全措施,防止出现未知的用户状态。

  10. wp_cache_set( 'current_user', $current_user, 'users' );: 将当前用户对象缓存起来,方便下次使用。 wp_cache_set() 函数是WordPress的缓存机制,可以提高性能。

  11. do_action( 'set_current_user', $current_user );: 触发一个名为 set_current_user 的动作钩子。 这允许其他插件或主题在设置当前用户后执行一些自定义操作。 比如,可以记录用户登录日志,或者更新用户的某些状态。

三、$current_user 到底存了些啥?

$current_user 是一个 WP_User 对象,它包含了关于当前用户的所有信息。 我们可以通过访问这个对象的属性来获取用户的各种信息。

属性名 数据类型 描述
ID int 用户ID
user_login string 用户名
user_pass string 用户密码(通常是哈希过的)
user_nicename string 用户昵称,用于友好的URL
user_email string 用户邮箱
user_url string 用户个人网站URL
user_registered string 用户注册时间
user_activation_key string 用户激活密钥(通常用于注册验证)
user_status string 用户状态(例如,0表示正常,1表示被禁止)
display_name string 显示名称(可以是昵称或其他设置)
roles array 用户角色(例如,administrator、editor、author)
caps array 用户权限(由角色决定,也可以单独赋予)
allcaps array 用户所有权限(包括角色权限和单独赋予的权限)
filter string 用于过滤数据的上下文

例子:

global $current_user;

if ( is_user_logged_in() ) {
    echo '你好,' . $current_user->display_name . '!';
    echo '<br>你的邮箱是:' . $current_user->user_email;

    if ( in_array( 'administrator', $current_user->roles ) ) {
        echo '<br>你是管理员!';
    }
} else {
    echo '您尚未登录。';
}

四、wp_set_current_user() 在哪里被调用?

wp_set_current_user() 函数在WordPress的很多地方都会被调用,主要是在以下场景:

  • 用户登录: 当用户成功登录后,wp_set_current_user() 会被调用,将当前用户设置为已登录的用户。
  • 后台管理页面: 在后台管理页面,wp_set_current_user() 会被调用,确保当前用户是经过身份验证的用户。
  • REST API: 在使用REST API时,wp_set_current_user() 会被调用,根据认证信息设置当前用户。
  • 插件和主题: 插件和主题也可以调用 wp_set_current_user(),例如,在处理自定义登录逻辑时。

五、注意事项和最佳实践

  • 安全性: wp_set_current_user() 是一个非常敏感的函数,因为它直接影响到用户的身份和权限。 因此,必须谨慎使用,确保只有在经过充分验证的情况下才能调用它。 否则,可能会导致安全漏洞。
  • 不要滥用: 不要随意调用 wp_set_current_user(),特别是不要在不必要的时候修改当前用户。 频繁修改当前用户可能会导致性能问题和不可预测的行为。
  • 使用 is_user_logged_in() 判断用户是否登录: 在访问 $current_user 之前,务必使用 is_user_logged_in() 函数判断用户是否已经登录。 否则,如果用户未登录,访问 $current_user 可能会导致错误。
  • 使用钩子: 如果需要在设置当前用户后执行一些自定义操作,可以使用 set_current_user 动作钩子,而不要直接修改 wp_set_current_user() 函数的代码。

六、get_userdata():幕后英雄

既然提到了 wp_set_current_user(),就不得不提一下它背后默默奉献的 get_userdata() 函数。 get_userdata() 负责从数据库中获取用户数据,并返回一个 WP_User 对象。

/**
 * Retrieve user info by user ID.
 *
 * @since 2.0.0
 *
 * @param int|object $user User ID or WP_User object
 * @return WP_User|false WP_User object on success, false on failure.
 */
function get_userdata( $user ) {
    if ( ! is_numeric( $user ) && ! ( $user instanceof WP_User ) ) {
        return false;
    }

    $user_id = ( $user instanceof WP_User ) ? $user->ID : (int) $user;

    $user = get_user( $user_id ); // 使用 get_user() 函数获取用户对象

    if ( ! $user ) {
        return false;
    }

    return $user;
}

实际上,get_userdata() 内部调用了 get_user() 函数。 get_user() 会首先检查缓存,如果缓存中存在用户信息,则直接返回缓存的数据。 否则,它会查询数据库,创建一个 WP_User 对象,并将其添加到缓存中。

七、总结

wp_set_current_user() 函数是WordPress中一个至关重要的函数,它负责设置当前用户,并将其信息存储在全局变量 $current_user 中。 理解这个函数的工作原理对于开发WordPress插件和主题至关重要。 通过深入剖析源码,我们了解了 wp_set_current_user() 是如何设置 $current_user 的,以及它在WordPress生态系统中的作用。 希望今天的讲座能帮助大家更好地理解WordPress的底层机制。

八、思考题

  1. 如果我直接修改 $current_user 全局变量,而不是使用 wp_set_current_user() 函数,会有什么潜在问题?
  2. set_current_user 动作钩子有哪些常见的应用场景?
  3. 如果我想在用户登录后,立即更新用户的一些自定义信息(例如,上次登录时间),应该如何实现?

今天的讲座就到这里,谢谢大家! 希望大家继续深入研究WordPress源码,成为真正的WordPress专家!

发表回复

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