剖析 WordPress `wp_get_current_user()` 函数的源码:如何获取当前登录用户的 `WP_User` 对象。

WordPress 用户魔法:wp_get_current_user() 函数源码深度剖析

大家好,我是你们今天的WordPress用户魔法师!今天要给大家揭秘一个WordPress世界里最常用的“咒语”之一:wp_get_current_user()。 别看它短短几个字,蕴含的能量可不小,能帮你召唤出当前登录用户的全部信息。

废话不多说,我们直接进入正题,一起扒开它的源码,看看它到底施展了什么魔法,才能如此轻松地获取用户信息。

1. wp_get_current_user() 的基本用法与返回值

首先,我们来简单回顾一下wp_get_current_user() 的基本用法。在你的WordPress主题或插件中,只需要简单地调用这个函数:

<?php
$current_user = wp_get_current_user();

if ( 0 == $current_user->ID ) {
    echo '当前没有用户登录';
} else {
    echo '欢迎,' . $current_user->user_login . '!';
    echo '你的邮箱是:' . $current_user->user_email;
}
?>

这段代码会检查当前是否有用户登录,如果有,就显示用户的用户名和邮箱。

wp_get_current_user() 函数的返回值是一个 WP_User 对象。如果没有用户登录,它会返回一个 ID 为 0 的 WP_User 对象。 也就是说,即使没有登录用户,你仍然能得到一个对象,只不过这个对象代表的是一个“匿名用户”。

返回值类型 说明
WP_User 如果有用户登录,返回包含用户信息的 WP_User 对象。
WP_User 如果没有用户登录,返回 ID 为 0 的 WP_User 对象 (表示匿名用户)。

2. 源码追踪:从入口开始

现在,让我们深入到 WordPress 源码中,找到 wp-includes/pluggable.php 文件,这里是 wp_get_current_user() 函数的定义所在。

/**
 * Gets the current logged-in user object.
 *
 * If the user is not logged in, then the return value will be 0.
 *
 * @since 2.0.0
 * @since 3.5.0 Added `$deprecated` parameter.
 * @since 4.5.0 `$deprecated` parameter marked as deprecated.
 *
 * @global WP_User|null $current_user
 *
 * @param mixed $deprecated Deprecated.
 * @return WP_User Current user object, WP_User object on failure.
 */
function wp_get_current_user( $deprecated = '' ) {
    global $current_user;

    if ( ! empty( $deprecated ) ) {
        _deprecated_argument( __FUNCTION__, '3.5.0' );
    }

    if ( isset( $current_user ) && ( $current_user instanceof WP_User ) ) {
        /**
         * Fires after the current user is set.
         *
         * @since 2.1.0
         *
         * @param WP_User $current_user WP_User object for the current user.
         */
        do_action( 'set_current_user', $current_user );
        return $current_user;
    }

    if ( function_exists( 'wp_set_current_user' ) ) {
        wp_set_current_user();
    } else {
        $current_user = new WP_User( 0 );
    }

    /** This action is documented in wp-includes/pluggable.php */
    do_action( 'set_current_user', $current_user );

    return $current_user;
}

我们来逐行解读这段代码:

  1. global $current_user; 这行代码声明了一个全局变量 $current_user。这个变量是用来存储当前登录用户的 WP_User 对象的。重点来了,这是个全局变量,也就是说,在WordPress的很多地方都可以访问到它。

  2. if ( ! empty( $deprecated ) ) { ... } 这部分代码是处理一个废弃参数 $deprecated 的。从 WordPress 3.5.0 开始,这个参数就被标记为废弃,所以我们可以忽略它。

  3. if ( isset( $current_user ) && ( $current_user instanceof WP_User ) ) { ... } 这是个关键的判断语句。它首先检查 $current_user 变量是否已经被设置,并且是否是一个 WP_User 对象。如果条件成立,说明当前用户的信息已经被加载过,直接返回 $current_user 即可,避免重复加载。

  4. do_action( 'set_current_user', $current_user ); 如果用户已经存在,会触发 set_current_user 这个 action hook。 插件或主题可以通过这个 hook 来执行一些与当前用户相关的操作,比如记录用户活动,更新用户统计信息等等。

  5. if ( function_exists( 'wp_set_current_user' ) ) { ... } 如果 $current_user 还没有被设置,它会检查 wp_set_current_user() 函数是否存在。这个函数通常是在 wp-includes/pluggable.php 中定义的(但也有可能被插件替换),负责加载当前用户的信息。

  6. else { $current_user = new WP_User( 0 ); } 如果wp_set_current_user 函数不存在,说明WordPress可能还没有完全加载,或者出现了某些问题。在这种情况下,它会创建一个 ID 为 0 的 WP_User 对象,表示一个匿名用户。

  7. do_action( 'set_current_user', $current_user ); 无论用户是否已经存在,都会触发 set_current_user 这个 action hook。

  8. return $current_user; 最后,返回 $current_user 对象。

3. wp_set_current_user() 函数的奥秘

现在,我们来重点看看 wp_set_current_user() 函数。它才是真正加载用户信息的幕后英雄。同样在 wp-includes/pluggable.php 文件中:

/**
 * Sets the current user.
 *
 * @since 2.0.0
 *
 * @global WP_User|null $current_user
 *
 * @param int|string|WP_User $id User ID, User login, or WP_User object.
 */
function wp_set_current_user( $id = 0 ) {
    global $current_user, $pagenow;

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

    if ( ! empty( $id ) ) {
        $id = (int) $id;
    } elseif ( is_admin() ) {
        $id = wp_validate_auth_cookie();
    } else {
        $id = apply_filters( 'determine_current_user', false );
    }

    if ( ! $id ) {
        $current_user = new WP_User( 0 );
        return;
    }

    if ( $id != $current_user->ID ) {
        $current_user->init( $id );
    }

    wp_cache_set( $current_user->ID, $current_user, 'users' );
}

我们来一行一行分析:

  1. global $current_user, $pagenow; 声明全局变量 $current_user$pagenow$pagenow 变量表示当前页面的名称,例如 index.phpedit.php 等。

  2. if ( ! ( $current_user instanceof WP_User ) ) { ... } 如果 $current_user 不是一个 WP_User 对象,就创建一个新的 WP_User 对象。

  3. if ( ! empty( $id ) ) { ... } 如果传入了 $id 参数,就将其转换为整数类型。这个 $id 可以是用户 ID、用户名或 WP_User 对象。

  4. elseif ( is_admin() ) { ... } 如果在后台管理页面 (is_admin() 返回 true),就调用 wp_validate_auth_cookie() 函数来验证用户的身份。这个函数会检查用户的登录 cookie,如果 cookie 有效,就返回用户 ID。

  5. else { ... } 如果在前台页面,就使用 apply_filters( 'determine_current_user', false ) 来确定当前用户。这个 filter hook 允许插件或主题自定义用户身份验证逻辑。

  6. if ( ! $id ) { ... } 如果 $id 为空,说明无法确定当前用户,就创建一个 ID 为 0 的 WP_User 对象。

  7. if ( $id != $current_user->ID ) { ... } 如果 $id 与当前用户的 ID 不一致,就调用 $current_user->init( $id ) 方法来初始化用户信息。

  8. wp_cache_set( $current_user->ID, $current_user, 'users' ); 将当前用户的信息缓存到 WordPress 对象缓存中,方便下次快速获取。

4. wp_validate_auth_cookie():Cookie 验证的秘密

wp_validate_auth_cookie() 函数在后台用户身份验证中扮演着关键角色。它负责解析和验证用户的登录 cookie。 让我们来看看它的源码(位于 wp-includes/pluggable.php 中):

/**
 * Validates authentication cookie.
 *
 * The cookie contains concatenated information: username, expiration timestamp,
 * and hash. The username is plain text, the timestamp is base64 encoded,
 * and the hash is a hash of the username, timestamp, password, and secret key.
 * The secret key is dynamically generated when the user logs in, and is
 * stored in the database.
 *
 * @since 2.5.0
 *
 * @param string  $cookie  Authentication cookie.
 * @param string  $scheme  Authentication scheme. Default is 'auth'. Accepts
 *                        'auth', 'secure_auth', or 'logged_in'.
 * @return int|false User ID if the cookie is valid, false otherwise.
 */
function wp_validate_auth_cookie( $cookie = '', $scheme = '' ) {
    if ( ! $cookie ) {
        switch ( $scheme ) {
            case 'auth':
                $cookie_name = AUTH_COOKIE;
                break;
            case 'secure_auth':
                $cookie_name = SECURE_AUTH_COOKIE;
                break;
            case 'logged_in':
                $cookie_name = LOGGED_IN_COOKIE;
                break;
            default:
                $cookie_name = AUTH_COOKIE;
        }

        if ( empty( $_COOKIE[ $cookie_name ] ) ) {
            return false;
        }

        $cookie = $_COOKIE[ $cookie_name ];
    }

    $cookie_elements = explode( '|', $cookie );

    if ( count( $cookie_elements ) !== 4 ) {
        return false;
    }

    list( $username, $expiration, $token, $hmac ) = $cookie_elements;

    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
        /**
         * Fires after a cookie is validated.
         *
         * @since 5.1.0
         *
         * @param string $username Username from the cookie.
         * @param int    $expiration Expiration timestamp from the cookie.
         * @param string $token Token from the cookie.
         * @param string $hmac HMAC from the cookie.
         * @param string $scheme Authentication scheme.
         */
        do_action( 'auth_cookie_validation', $username, $expiration, $token, $hmac, $scheme );
    }

    $user = get_user_by( 'login', $username );
    if ( ! $user ) {
        return false;
    }

    $pass_frag = substr( $user->user_pass, 8, 4 );

    $key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
    $hash = hash_hmac( 'md5', $username . '|' . $expiration . '|' . $token, $key );

    if ( hash_equals( $hmac, $hash ) ) {
        if ( $expiration > time() ) {
            return $user->ID;
        } else {
            /**
             * Fires when a cookie is invalid because the expiration time has passed.
             *
             * @since 6.1.0
             *
             * @param WP_User $user WP_User object of the user corresponding to the cookie.
             */
            do_action( 'auth_cookie_expired', $user );
        }
    }

    return false;
}

这段代码的核心逻辑如下:

  1. 获取 Cookie: 首先,它会根据 $scheme 参数(authsecure_authlogged_in)获取对应的 cookie 名称,并从 $_COOKIE 数组中读取 cookie 的值。

  2. 解析 Cookie: Cookie 的值是一个字符串,由四个部分组成,用 | 分隔:用户名、过期时间戳、token 和 HMAC (Hash-based Message Authentication Code)。

  3. 验证用户: 根据用户名从数据库中获取用户信息。

  4. 生成 Hash: 使用用户名、密码片段、过期时间戳、token 和一个密钥(基于用户密码和 scheme 生成)来生成一个 HMAC。

  5. 比较 Hash: 将生成的 HMAC 与 cookie 中的 HMAC 进行比较。如果两者一致,说明 cookie 是有效的。

  6. 验证过期时间: 检查 cookie 的过期时间是否已过。如果过期时间未过,则返回用户 ID。

如果任何一个验证步骤失败,wp_validate_auth_cookie() 函数都会返回 false

5. WP_User 对象:用户信息的大本营

WP_User 类是 WordPress 中用于表示用户的核心类。它包含了用户的各种属性,例如 ID、用户名、邮箱、角色等等。 当 wp_get_current_user() 函数返回一个 WP_User 对象时,你就可以通过这个对象访问用户的各种信息。

WP_User 类的定义位于 wp-includes/class-wp-user.php 文件中。

class WP_User {

    /**
     * User data.
     *
     * @since 2.0.0
     * @var stdClass
     */
    public $data;

    /**
     * The user's ID.
     *
     * @since 2.1.0
     * @access public
     * @var int
     */
    public $ID = 0;

    /**
     * The user's capabilities.
     *
     * @since 2.0.0
     * @access public
     * @var WP_User_Capabilities
     */
    public $caps = array();

    /**
     * User metadata.
     *
     * @since 2.0.0
     * @access public
     * @var array
     */
    public $meta = array();

    /**
     * The roles the user is part of.
     *
     * @since 2.0.0
     * @access public
     * @var array
     */
    public $roles = array();

   //... (省略其他方法)
}

你可以通过 WP_User 对象的属性来访问用户的各种信息,例如:

  • $current_user->ID: 用户 ID
  • $current_user->user_login: 用户名
  • $current_user->user_email: 用户邮箱
  • $current_user->roles: 用户角色
  • $current_user->caps: 用户权限
  • $current_user->data: 包含了所有用户数据的 stdClass 对象

6. 总结:wp_get_current_user() 的工作流程

现在,我们来总结一下 wp_get_current_user() 函数的工作流程:

  1. 检查全局变量 $current_user 是否已经存在。如果存在,并且是一个 WP_User 对象,则直接返回 $current_user
  2. 如果 $current_user 不存在,则调用 wp_set_current_user() 函数来加载当前用户的信息。
  3. wp_set_current_user() 函数会根据当前环境(后台或前台)来确定用户 ID。
    • 在后台,它会使用 wp_validate_auth_cookie() 函数来验证用户的登录 cookie。
    • 在前台,它会使用 apply_filters( 'determine_current_user', false ) filter hook 来确定用户 ID。
  4. wp_set_current_user() 函数会创建一个 WP_User 对象,并将用户的信息加载到这个对象中。
  5. wp_set_current_user() 函数会将 WP_User 对象缓存到 WordPress 对象缓存中。
  6. wp_get_current_user() 函数返回 $current_user 对象。

7. 实际应用:一些有趣的例子

现在,让我们来看一些实际应用 wp_get_current_user() 函数的例子:

  • 根据用户角色显示不同的内容:

    <?php
    $current_user = wp_get_current_user();
    
    if ( in_array( 'administrator', $current_user->roles ) ) {
        echo '欢迎管理员!你可以访问所有功能。';
    } elseif ( in_array( 'editor', $current_user->roles ) ) {
        echo '欢迎编辑!你可以编辑和发布文章。';
    } else {
        echo '欢迎!你可以阅读文章。';
    }
    ?>
  • 只允许登录用户访问特定页面:

    <?php
    if ( ! is_user_logged_in() ) {
        wp_redirect( wp_login_url( get_permalink() ) );
        exit;
    }
    ?>
  • 在用户个人资料页面显示用户的自定义元数据:

    <?php
    $current_user = wp_get_current_user();
    $favorite_color = get_user_meta( $current_user->ID, 'favorite_color', true );
    
    if ( ! empty( $favorite_color ) ) {
        echo '你最喜欢的颜色是:' . $favorite_color;
    }
    ?>

8. 注意事项:一些小贴士

  • 不要滥用 wp_get_current_user() 虽然 wp_get_current_user() 函数非常方便,但不要在每个页面都调用它。频繁调用会增加数据库查询的次数,影响网站的性能。尽量将用户信息缓存起来,避免重复加载。
  • 注意用户权限: 在使用用户信息时,一定要注意用户的权限。不要让用户访问他们不应该访问的内容或功能。
  • 使用 is_user_logged_in() 函数: 如果你只需要检查用户是否登录,而不需要获取用户信息,可以使用 is_user_logged_in() 函数。这个函数比 wp_get_current_user() 函数更轻量级。
  • 利用 determine_current_user filter hook: 如果你需要自定义用户身份验证逻辑,可以使用 determine_current_user filter hook。

9. 总结

wp_get_current_user() 函数是 WordPress 中一个非常重要的函数,它可以帮助你轻松地获取当前登录用户的全部信息。 通过深入了解它的源码,你可以更好地理解 WordPress 的用户管理机制,并能够更灵活地使用用户信息。

希望今天的讲座能够帮助你掌握 wp_get_current_user() 函数的魔法,在你的 WordPress 项目中大放异彩! 记住,理解源码是成为 WordPress 大师的关键一步!

谢谢大家!

发表回复

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