阐述 `get_users()` 函数的源码,它是如何通过 `WP_User_Query` 类查询用户列表的?

大家好,我是你们今天的码农老司机,准备好了吗?今天要带大家深入挖掘WordPress世界里一个看似简单,实则暗藏玄机的函数:get_users()。别看它只有短短几个字母,背后可是连接着一个强大的查询引擎——WP_User_Query 类。

咱们今天就来一场源码解剖,看看get_users()是如何驾驭WP_User_Query,从数据库里捞出一堆用户信息的。

第一幕:初识get_users()——简单易用,却不简单

首先,我们来看看get_users()的庐山真面目,它位于 WordPress 的 wp-includes/user.php 文件中。

/**
 * Retrieves a list of users.
 *
 * @since 2.1.0
 *
 * @param string|array $args Optional. Array or string of arguments to pass to WP_User_Query.
 *                              See WP_User_Query::prepare_query() for information on accepted arguments.
 * @return array Array of WP_User objects.
 */
function get_users( $args = array() ) {
    $wp_user_query = new WP_User_Query( $args );

    return $wp_user_query->get_results();
}

看到了吗?是不是出乎意料的简洁? 它主要做了两件事:

  1. 实例化WP_User_Query: $wp_user_query = new WP_User_Query( $args ); 这行代码是关键。它把传入的 $args 参数(如果没有传,就用默认的空数组)传递给 WP_User_Query 类的构造函数。

  2. 获取结果: $wp_user_query->get_results(); 这行代码调用了 WP_User_Query 对象的 get_results() 方法,获取查询结果,并返回一个包含 WP_User 对象的数组。

到这里,你可能会想:“就这?就这?这也太简单了吧!” 别急,好戏还在后头。真正的魔法,都藏在 WP_User_Query 类的内部。

第二幕:走进WP_User_Query——查询引擎的核心

WP_User_Query 类的定义位于 wp-includes/class-wp-user-query.php 文件中。它是一个专门用于查询用户信息的类,提供了丰富的查询参数和方法,可以让你灵活地检索 WordPress 数据库中的用户数据。

我们先来看看WP_User_Query构造函数做了什么。

    /**
     * Constructor.
     *
     * @since 3.1.0
     *
     * @param string|array $query Optional. Array or string of Query parameters.
     */
    public function __construct( $query = null ) {
        $this->prepare_query( $query );
        $this->query();
    }

构造函数做了两件事,prepare_queryquery

prepare_query():参数解析与准备

prepare_query() 方法负责解析传入的查询参数,并根据这些参数构建 SQL 查询语句。 它是整个查询过程的基石。

    /**
     * Prepares the query variables.
     *
     * @since 3.1.0
     *
     * @param string|array $query String or array of Query parameters.
     */
    public function prepare_query( $query = '' ) {
        global $wpdb;

        if ( empty( $query ) ) {
            $query = $this->query_vars;
        }

        $this->query_vars = wp_parse_args( $query, $this->query_vars );
        $this->query_vars = sanitize_user_query( $this->query_vars );
        /**
         * Fires before the WP_User_Query->prepare_query() method runs.
         *
         * @since 3.7.0
         *
         * @param WP_User_Query $this The current WP_User_Query instance (passed by reference).
         */
        do_action_ref_array( 'pre_user_query', array( &$this ) );

        if ( isset( $this->query_vars['number'] ) && ( $this->query_vars['number'] < 1 || ! is_numeric( $this->query_vars['number'] ) ) ) {
            $this->query_vars['number'] = '';
            unset( $this->query_vars['number'] );
        }

        $this->process_id_query();
        $this->process_login_query();
        $this->process_name_query();
        $this->process_email_query();
        $this->process_url_query();
        $this->process_nicename_query();
        $this->process_include_exclude_query();
        $this->process_meta_query();
        $this->process_date_query();

        // Combine querystring vars asking for IDs with an 'include' query.
        if ( ! empty( $this->query_vars['include'] ) ) {
            $ids = wp_parse_id_list( $this->query_vars['include'] );
            $ids = array_map( 'intval', $ids );

            $this->query_vars['include'] = implode( ',', $ids );
        }
        if ( isset( $this->query_vars['fields'] ) ) {
            if ( 'all' === $this->query_vars['fields'] ) {
                $this->query_fields = "$wpdb->users.*";
            } elseif ( 'all_with_meta' === $this->query_vars['fields'] ) {
                $this->query_fields = " DISTINCT $wpdb->users.*, umeta.*";
                $this->get_meta = true;
            } elseif ( 'ID' === $this->query_vars['fields'] ) {
                $this->query_fields = "$wpdb->users.ID";
            } elseif ( 'ids' === $this->query_vars['fields'] ) {
                $this->query_fields = "$wpdb->users.ID";
            } elseif ( 'count' === $this->query_vars['fields'] ) {
                $this->query_fields = 'COUNT(*)';
            } else {
                $this->query_fields = "$wpdb->users.ID";
            }
        }

        $this->parse_search( $this->query_vars );

        /**
         * Fires after the WP_User_Query->prepare_query() method runs.
         *
         * @since 3.1.0
         *
         * @param WP_User_Query $this The current WP_User_Query instance (passed by reference).
         */
        do_action_ref_array( 'user_query_pre_get_users', array( &$this ) );

        $this->where = apply_filters_ref_array( 'user_search_where', array( $this->where, &$this ) );
        $this->orderby = apply_filters_ref_array( 'user_search_orderby', array( $this->orderby, &$this ) );
    }

这段代码看起来很长,但其实主要做了以下几件事:

  1. 参数合并与清洗: 使用 wp_parse_args() 将传入的参数与默认参数合并,并使用 sanitize_user_query() 对参数进行安全过滤,防止 SQL 注入。

  2. 处理各种查询条件: 接下来,prepare_query() 会根据传入的参数,调用一系列 process_*_query() 方法来构建 SQL 查询的 WHERE 子句。 例如:

    • process_id_query(): 处理 includeexclude 参数,用于根据用户 ID 筛选用户。
    • process_login_query(): 处理 login 参数,用于根据用户名筛选用户。
    • process_email_query(): 处理 email 参数,用于根据邮箱筛选用户。
    • process_meta_query(): 处理 meta_query 参数,用于根据用户元数据筛选用户。这个非常重要,允许你根据自定义字段进行查询。
    • process_date_query(): 处理 date_query 参数,用于根据用户注册日期筛选用户。
  3. 处理查询字段: 根据 fields 参数,确定要查询哪些字段。 常见的选项有:

    • all: 查询所有字段 ( $wpdb->users.* )
    • ids: 只查询用户 ID ( $wpdb->users.ID )
    • count: 只查询用户数量 ( COUNT(*) )
  4. 处理搜索关键词: 如果传入了 search 参数,parse_search() 方法会构建 SQL 查询的 WHERE 子句,用于根据关键词搜索用户名、邮箱等字段。

  5. 应用过滤器: prepare_query() 方法还使用了 user_search_whereuser_search_orderby 过滤器,允许开发者自定义 SQL 查询的 WHEREORDER BY 子句。 这提供了极大的灵活性,可以满足各种复杂的查询需求。

query():执行查询,获取结果

query() 方法负责执行构建好的 SQL 查询语句,并从数据库中获取用户数据。

    /**
     * Executes the query, with the current variables.
     *
     * @since 3.1.0
     *
     * @global wpdb $wpdb WordPress database abstraction object.
     *
     * @return array|int List of found user IDs or array of WP_User objects.
     */
    public function query() {
        global $wpdb;

        $this->prepare_sql();

        /**
         * Fires before the WP_User_Query executes the query.
         *
         * @since 3.5.0
         *
         * @param WP_User_Query $this The current WP_User_Query instance (passed by reference).
         */
        do_action_ref_array( 'pre_get_users', array( &$this ) );

        if ( 'count' === $this->query_vars['fields'] ) {
            $this->total_users = (int) $wpdb->get_var( $this->request );

            return;
        }

        $this->results = $wpdb->get_results( $this->request );

        if ( $this->get_meta && ! empty( $this->results ) ) {
            update_meta_cache( 'user', wp_list_pluck( $this->results, 'ID' ) );
        }

        $this->total_users = $wpdb->get_var( $this->get_total );

        if ( $this->results ) {
            if ( 'ids' === $this->query_vars['fields'] || 'ID' === $this->query_vars['fields'] ) {
                $this->results = array_map( 'intval', $this->results );
            } else {
                $this->results = array_map( 'get_userdata', $this->results );
            }
        }

        return;
    }

让我们来拆解一下:

  1. 准备SQL语句 调用prepare_sql()方法,将之前的各种参数组合成最终可以执行的SQL查询语句。

  2. 执行查询: 使用 $wpdb->get_results() 方法执行 SQL 查询,并将结果存储在 $this->results 属性中。

  3. 处理元数据: 如果设置了 fieldsall_with_meta,则调用 update_meta_cache() 函数,预加载用户元数据,提高性能。

  4. 处理结果: 根据 fields 参数,将查询结果转换为相应的格式。 如果 fieldsids,则将结果转换为整数数组。 否则,将结果转换为 WP_User 对象数组。

  5. 获取总数: 执行 $wpdb->get_var( $this->get_total ) 获取符合条件的总用户数,并存储在 $this->total_users 属性中。

get_results():返回最终结果

    /**
     * Retrieve query results.
     *
     * @since 3.1.0
     *
     * @return array|int An array of WP_User objects, or array of user IDs.
     *                   Returns the user count if querying for a count.
     */
    public function get_results() {
        if ( isset( $this->query_vars['fields'] ) && 'count' === $this->query_vars['fields'] ) {
            return intval( $this->total_users );
        }

        return $this->results;
    }

这个方法非常简单,它仅仅根据查询字段(fields)的不同返回查询结果。 如果查询字段是count,那么它返回总用户数,否则返回包含用户对象的数组。

第三幕:get_users()的参数详解——灵活定制你的查询

get_users() 函数接收一个 $args 参数,它是一个数组,用于指定查询条件。 WP_User_Query 类支持的参数非常丰富,可以满足各种复杂的查询需求。 下面是一些常用的参数:

参数名 类型 描述
blog_id int 只在多站点环境下有效。指定要查询用户的站点 ID。
include array/string 指定要包含的用户 ID。
exclude array/string 指定要排除的用户 ID。
search string 搜索关键词。用于搜索用户名、邮箱等字段。
search_columns array 指定要搜索的字段。默认为 array( 'user_login', 'user_email', 'display_name' )
orderby string 排序字段。默认为 login。 常用的选项有 ID, login, email, nicename, registered, display_name
order string 排序方式。默认为 ASC。 常用的选项有 ASC (升序) 和 DESC (降序)。
number int 每页显示的用户数量。默认为空,表示显示所有用户。
offset int 偏移量。用于分页显示用户。
paged int 当前页码。用于分页显示用户。
role string/array 指定要查询的用户角色。
meta_key string 用户元数据键名。用于根据用户元数据筛选用户。 必须与 meta_valuemeta_query 配合使用。
meta_value string/array 用户元数据键值。用于根据用户元数据筛选用户。 必须与 meta_key 配合使用。
meta_compare string 用户元数据比较运算符。默认为 =。 常用的选项有 =, !=, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN, BETWEEN, NOT BETWEEN, REGEXP, NOT REGEXP, RLIKE
meta_query array 用于构建复杂的元数据查询。 数组中的每个元素都是一个子查询,包含 key, value, compare 等参数。
date_query array 用于根据用户注册日期筛选用户。 数组中的每个元素都是一个日期查询条件,包含 year, month, day, after, before, inclusive 等参数。
fields string 指定要查询的字段。默认为空,表示查询所有字段。 常用的选项有 all, ids, count

第四幕:实战演练——代码示例

光说不练假把式,我们来几个实际的例子,看看如何使用 get_users() 函数和 WP_User_Query 类。

例1: 获取所有用户

$users = get_users();

foreach ( $users as $user ) {
    echo '用户ID: ' . $user->ID . ', 用户名: ' . $user->user_login . '<br>';
}

例2: 获取 ID 为 1, 3, 5 的用户

$args = array(
    'include' => array( 1, 3, 5 )
);

$users = get_users( $args );

foreach ( $users as $user ) {
    echo '用户ID: ' . $user->ID . ', 用户名: ' . $user->user_login . '<br>';
}

例3: 获取注册时间在 2023 年 1 月 1 日之后的用户

$args = array(
    'date_query' => array(
        array(
            'after'     => '2023-01-01',
            'inclusive' => true, // 包含 2023-01-01
        ),
    ),
);

$users = get_users( $args );

foreach ( $users as $user ) {
    echo '用户ID: ' . $user->ID . ', 用户名: ' . $user->user_login . ', 注册时间: ' . $user->user_registered . '<br>';
}

例4: 获取用户角色为 editor 的用户

$args = array(
    'role' => 'editor'
);

$users = get_users( $args );

foreach ( $users as $user ) {
    echo '用户ID: ' . $user->ID . ', 用户名: ' . $user->user_login . ', 角色: ' . implode( ', ', $user->roles ) . '<br>';
}

例5: 使用 meta_query 获取自定义字段 favorite_color 为 ‘blue’ 的用户

$args = array(
    'meta_query' => array(
        array(
            'key'     => 'favorite_color',
            'value'   => 'blue',
            'compare' => '='
        )
    )
);

$users = get_users( $args );

foreach ( $users as $user ) {
    echo '用户ID: ' . $user->ID . ', 用户名: ' . $user->user_login . ', 喜欢的颜色: ' . get_user_meta( $user->ID, 'favorite_color', true ) . '<br>';
}

例6: 分页显示用户

$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
$per_page = 10;

$args = array(
    'number' => $per_page,
    'offset' => ( $paged - 1 ) * $per_page,
    'paged'  => $paged,
);

$users = get_users( $args );

echo '<p>当前页: ' . $paged . '</p>';

echo '<ul>';
foreach ( $users as $user ) {
    echo '<li>用户ID: ' . $user->ID . ', 用户名: ' . $user->user_login . '</li>';
}
echo '</ul>';

// 显示分页链接
$total_users = get_users( array( 'fields' => 'count' ) );
$total_pages = ceil( $total_users / $per_page );

echo paginate_links( array(
    'base'    => get_pagenum_link(1) . '%_%',
    'format'  => '/page/%#%',
    'current' => $paged,
    'total'   => $total_pages,
) );

第五幕:总结与展望

通过今天的源码剖析,我们了解了 get_users() 函数是如何利用 WP_User_Query 类,从数据库中灵活地查询用户信息的。 WP_User_Query 类提供了丰富的查询参数和方法,可以满足各种复杂的查询需求。 同时,WordPress 还提供了 user_search_whereuser_search_orderby 过滤器,允许开发者自定义 SQL 查询的 WHEREORDER BY 子句,进一步扩展了查询的灵活性。

掌握了 get_users()WP_User_Query,你就掌握了在 WordPress 中查询用户的核心技能。 以后再遇到用户查询相关的需求,就可以游刃有余地解决问题了。

当然,WP_User_Query 类还有很多高级用法,例如使用 meta_query 构建更复杂的元数据查询,使用 date_query 构建更灵活的日期查询等。 这些就留给你们自己去探索和学习了。

希望今天的讲座对大家有所帮助。 祝大家编码愉快!

发表回复

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