各位观众老爷们,晚上好!我是你们的老朋友,代码界的段子手。今天咱们要聊聊WordPress里一个看似简单,实则内藏乾坤的函数——get_users()
。
get_users()
这玩意儿,相信大家或多或少都用过,它就是用来获取用户列表的。但你有没有想过,它背后到底是怎么运作的?别急,今天咱们就扒开它的外衣,看看它的真实面目!
第一幕:get_users()
闪亮登场
首先,咱们来看看 get_users()
这家伙的真身。以下是 WordPress 源码中 get_users()
函数的定义:
function get_users( $args = array() ) {
$query = new WP_User_Query( $args );
return $query->get_results();
}
简单粗暴有没有? 就两行代码! 核心就是实例化了 WP_User_Query
类,然后调用了 get_results()
方法。
看到这里,有没有觉得有点懵?别慌,这就是 WordPress 源码的风格,把复杂的逻辑都藏在类里面了。
第二幕:幕后英雄 WP_User_Query
WP_User_Query
类,才是真正的幕后英雄。 它的职责就是根据你给的参数,构建 SQL 查询语句,然后从数据库里捞出符合条件的用户数据。
我们深入WP_User_Query
类,看看它的构造函数:
public function __construct( $query = null ) {
$this->query_vars_defaults = array(
'blog_id' => get_current_blog_id(),
'role' => '',
'role__in' => array(),
'role__not_in' => array(),
'meta_key' => '',
'meta_value' => '',
'meta_compare' => '',
'meta_query' => array(),
'date_query' => null,
'include' => array(),
'exclude' => array(),
'search' => '',
'search_columns' => array(),
'orderby' => 'login',
'order' => 'ASC',
'offset' => '',
'number' => '',
'paged' => 1,
'count_total' => true,
'fields' => 'all',
'who' => '',
'has_published_posts' => null,
);
$this->query_vars = wp_parse_args( $query, $this->query_vars_defaults );
$this->prepare_query();
$this->query();
}
这个构造函数做了几件事:
- 定义默认参数:
$this->query_vars_defaults
定义了查询用户的各种默认参数,比如默认排序方式是login
,排序顺序是ASC
(升序) 等等。 - 解析参数:
wp_parse_args()
函数会将你传入的参数$query
和默认参数合并,最终赋值给$this->query_vars
。 如果你传入了参数,会覆盖默认参数。 - 准备查询:
$this->prepare_query()
这个函数负责根据$this->query_vars
里的参数,构建 SQL 查询语句。 - 执行查询:
$this->query()
函数执行 SQL 查询,从数据库里获取用户数据。
第三幕:prepare_query()
磨刀霍霍
prepare_query()
函数是构建 SQL 查询语句的关键。 让我们来看看它的核心逻辑。
protected function prepare_query() {
global $wpdb;
$qv = &$this->query_vars;
$this->prepare_user_search( $qv );
$this->sql_fields = 'SQL_CALC_FOUND_ROWS wp_users.*';
if ( 'count' === $qv['fields'] ) {
$this->sql_fields = 'COUNT(*)';
}
$this->sql_from = "FROM {$wpdb->users} AS wp_users";
$this->sql_where = 'WHERE 1=1';
$this->sql_orderby = '';
$this->sql_groupby = '';
$this->sql_limit = '';
if ( is_array( $qv['include'] ) && ! empty( $qv['include'] ) ) {
$ids = implode( ',', array_map( 'intval', $qv['include'] ) );
$this->sql_where .= " AND wp_users.ID IN ($ids)";
} elseif ( is_array( $qv['exclude'] ) && ! empty( $qv['exclude'] ) ) {
$ids = implode( ',', array_map( 'intval', $qv['exclude'] ) );
$this->sql_where .= " AND wp_users.ID NOT IN ($ids)";
}
if ( '' !== $qv['who'] ) {
if ( 'authors' === $qv['who'] ) {
$this->sql_from .= " INNER JOIN {$wpdb->posts} ON (wp_users.ID = {$wpdb->posts}.post_author)";
$this->sql_where .= " AND {$wpdb->posts}.post_status = 'publish'";
$this->sql_where .= " AND {$wpdb->posts}.post_type = 'post'";
}
}
if ( ! empty( $qv['blog_id'] ) ) {
$this->sql_from .= " INNER JOIN {$wpdb->usermeta} ON (wp_users.ID = {$wpdb->usermeta}.user_id)";
$this->sql_where .= $wpdb->prepare( " AND {$wpdb->usermeta}.meta_key = %s", $wpdb->get_blog_prefix( $qv['blog_id'] ) . 'capabilities' );
}
if ( ! empty( $qv['role'] ) ) {
$this->sql_from .= " INNER JOIN {$wpdb->usermeta} AS mt1 ON (wp_users.ID = mt1.user_id)";
$this->sql_where .= ' AND mt1.meta_key = '' . $wpdb->prefix . 'capabilities'';
$this->sql_where .= ' AND mt1.meta_value LIKE '%' . esc_sql( like_escape( $qv['role'] ) ) . '%'';
} elseif ( ! empty( $qv['role__in'] ) ) {
// ... 处理 role__in
} elseif ( ! empty( $qv['role__not_in'] ) ) {
// ... 处理 role__not_in
}
if ( ! empty( $qv['meta_key'] ) || ! empty( $qv['meta_value'] ) || ! empty( $qv['meta_query'] ) ) {
$this->get_meta_sql( $qv['meta_query'], $qv['meta_key'], $qv['meta_value'], $qv['meta_compare'] );
}
if ( ! empty( $qv['date_query'] ) && is_array( $qv['date_query'] ) ) {
$date_query = new WP_Date_Query( $qv['date_query'], 'user_registered' );
$this->sql_where .= $date_query->get_sql();
}
$orderby_array = $this->parse_orderby( $qv['orderby'] );
$orderby = '';
if ( $orderby_array ) {
$orderby = implode( ', ', $orderby_array );
}
if ( $orderby ) {
$this->sql_orderby = "ORDER BY $orderby";
$this->sql_orderby .= ' ' . $qv['order'];
}
if ( is_numeric( $qv['number'] ) ) {
if ( is_numeric( $qv['offset'] ) ) {
$this->sql_limit = $wpdb->prepare( 'LIMIT %d, %d', $qv['offset'], $qv['number'] );
} else {
$this->sql_limit = $wpdb->prepare( 'LIMIT %d, %d', ( $qv['paged'] - 1 ) * $qv['number'], $qv['number'] );
}
}
}
代码有点长,但别怕,咱们来慢慢分析:
- 初始化 SQL 语句片段:
$this->sql_fields
(要查询的字段),$this->sql_from
(FROM 子句),$this->sql_where
(WHERE 子句),$this->sql_orderby
(ORDER BY 子句),$this->sql_groupby
(GROUP BY 子句),$this->sql_limit
(LIMIT 子句) 都被初始化为空字符串。 - 处理
include
和exclude
参数: 如果你指定了include
(包含哪些用户 ID) 或exclude
(排除哪些用户 ID) 参数,这里会构建相应的WHERE
子句。 - 处理
who
参数:who
参数可以用来筛选 "authors" (作者)。 如果指定了who = 'authors'
, 就会 INNER JOINwp_posts
表,并添加额外的WHERE
子句,限制只查询发布过文章的作者。 - 处理
blog_id
参数: 如果你在使用多站点 WordPress,并且指定了blog_id
,这里会 INNER JOINwp_usermeta
表,并添加WHERE
子句,限制只查询特定站点的用户。 - 处理
role
、role__in
、role__not_in
参数: 这些参数用于根据用户角色筛选用户。同样会 INNER JOINwp_usermeta
表,并添加WHERE
子句,筛选出符合特定角色的用户。role__in
允许你指定多个角色,role__not_in
允许你排除多个角色。 - 处理
meta_key
、meta_value
、meta_query
参数: 这些参数用于根据用户元数据 (usermeta) 筛选用户。meta_key
和meta_value
可以让你根据单个元数据键值对进行筛选。meta_query
允许你构建更复杂的元数据查询条件。get_meta_sql()
方法会负责生成相应的 SQL 片段。 - 处理
date_query
参数:date_query
参数允许你根据用户的注册日期筛选用户。 它会使用WP_Date_Query
类来构建日期查询的 SQL 片段。 - 处理
orderby
和order
参数: 这些参数用于指定排序方式和排序顺序。parse_orderby()
函数会将orderby
参数解析成一个 SQL 字段列表。 - 处理
number
、offset
和paged
参数: 这些参数用于分页查询用户。number
指定每页显示的用户数量,offset
指定查询的起始位置,paged
指定当前页码。
第四幕:query()
执行 SQL,获取数据
prepare_query()
函数只是磨刀,真正干活的是 query()
函数。 它的职责就是执行 prepare_query()
构建好的 SQL 查询语句,然后将查询结果保存到 $this->results
属性中。
public function query() {
global $wpdb;
$this->prepare_query();
$this->request = "SELECT {$this->sql_fields} {$this->sql_from} {$this->sql_where} {$this->sql_orderby} {$this->sql_limit}";
if ( is_numeric( $this->query_vars['number'] ) && $this->query_vars['count_total'] ) {
$this->total_users = (int) $wpdb->get_var( "SELECT FOUND_ROWS()" );
}
if ( 'count' === $this->query_vars['fields'] ) {
$this->results = (int) $wpdb->get_var( $this->request );
return;
}
$this->results = $wpdb->get_results( $this->request );
if ( 'all' === $this->query_vars['fields'] ) {
foreach ( $this->results as $key => $user ) {
$this->results[ $key ] = new WP_User( $user );
}
}
}
这个函数主要做了以下几件事:
- 再次调用
prepare_query()
: 这是为了确保 SQL 查询语句是最新的。 虽然构造函数里已经调用过prepare_query()
了,但这里再调用一次可以防止在query()
函数调用之前,有其他代码修改了$this->query_vars
属性。 - 构建完整的 SQL 查询语句: 将
$this->sql_fields
、$this->sql_from
、$this->sql_where
、$this->sql_orderby
和$this->sql_limit
拼接成完整的 SQL 查询语句,并赋值给$this->request
属性。 - 获取总用户数量: 如果指定了
number
参数 (每页显示的用户数量) 并且count_total
参数为true
(需要计算总用户数量), 就会执行SELECT FOUND_ROWS()
查询,获取总用户数量,并保存到$this->total_users
属性中。SQL_CALC_FOUND_ROWS
在prepare_query()
中已经添加过了。 - 执行 SQL 查询: 使用
$wpdb->get_results()
函数执行 SQL 查询,并将查询结果保存到$this->results
属性中。 - 将查询结果转换为
WP_User
对象: 如果fields
参数为'all'
(需要获取所有用户数据), 就会遍历$this->results
数组,将每个用户数据转换为WP_User
对象。
第五幕:get_results()
返回结果
最后, get_users()
函数调用 WP_User_Query
类的 get_results()
方法,将查询结果返回。
public function get_results() {
return $this->results;
}
get_results()
就只是简单地返回 $this->results
属性的值, 也就是 query()
函数查询到的用户数据。
总结:get_users()
的工作流程
现在,让我们来总结一下 get_users()
函数的工作流程:
- 调用
get_users()
函数,传入查询参数。 get_users()
函数实例化WP_User_Query
类,并将查询参数传递给WP_User_Query
类的构造函数。WP_User_Query
类的构造函数会:- 定义默认查询参数。
- 将传入的查询参数和默认参数合并。
- 调用
prepare_query()
函数构建 SQL 查询语句。 - 调用
query()
函数执行 SQL 查询,并将查询结果保存到$this->results
属性中。
get_users()
函数调用WP_User_Query
类的get_results()
方法,获取查询结果。get_results()
方法返回查询结果。
参数详解:get_users()
的十八般武艺
get_users()
函数支持很多参数,可以让你灵活地查询用户。 下面是一些常用的参数:
参数名 | 类型 | 描述 |
---|---|---|
blog_id |
int | 指定要查询的站点 ID (多站点 WordPress)。 |
role |
string | 指定要查询的用户角色。 |
role__in |
array | 指定要查询的多个用户角色。 |
role__not_in |
array | 指定要排除的多个用户角色。 |
meta_key |
string | 指定要查询的用户元数据的键名。 |
meta_value |
string | 指定要查询的用户元数据的键值。 |
meta_compare |
string | 指定元数据键值对的比较方式 (例如 ‘=’, ‘!=’, ‘>’, ‘<‘, ‘LIKE’, ‘NOT LIKE’, ‘IN’, ‘NOT IN’, ‘BETWEEN’, ‘NOT BETWEEN’)。 |
meta_query |
array | 允许构建更复杂的元数据查询条件。 |
date_query |
array | 允许根据用户的注册日期筛选用户。 |
include |
array | 指定要包含的用户 ID 列表。 |
exclude |
array | 指定要排除的用户 ID 列表。 |
search |
string | 指定要搜索的关键词。 |
search_columns |
array | 指定要在哪些字段中搜索关键词 (例如 ‘ID’, ‘user_login’, ‘user_email’, ‘user_url’, ‘user_nicename’, ‘display_name’)。 |
orderby |
string | 指定排序方式 (例如 ‘ID’, ‘login’, ‘nicename’, ’email’, ‘url’, ‘registered’, ‘display_name’, ‘post_count’, ‘meta_value’, ‘meta_value_num’)。 |
order |
string | 指定排序顺序 (‘ASC’ 或 ‘DESC’)。 |
offset |
int | 指定查询的起始位置。 |
number |
int | 指定每页显示的用户数量。 |
paged |
int | 指定当前页码。 |
count_total |
bool | 是否计算总用户数量。 |
fields |
string | 指定要返回的字段 (‘all’ (默认), ‘ID’, ‘login’, ‘nicename’, ’email’, ‘url’, ‘registered’, ‘display_name’, ‘post_count’, ‘meta_value’, ‘meta_value_num’, ‘count’)。 如果是’count’,则返回用户总数。 |
who |
string | 如果设置为 'authors' ,则只返回作者。 |
has_published_posts |
boolean | 根据用户是否发表过文章来过滤用户。true 只返回发表过文章的用户,false 只返回没有发表过文章的用户。 null (默认) 则不进行过滤。 |
举个栗子:用 get_users()
查询用户
咱们来举几个例子,看看 get_users()
函数怎么用。
- 获取所有管理员:
$args = array(
'role' => 'administrator',
);
$administrators = get_users( $args );
foreach ( $administrators as $administrator ) {
echo $administrator->user_login . '<br>';
}
- 获取注册日期在 2023 年 1 月 1 日之后的用户:
$args = array(
'date_query' => array(
array(
'after' => '2023-01-01',
'column' => 'user_registered',
),
),
);
$new_users = get_users( $args );
foreach ( $new_users as $new_user ) {
echo $new_user->user_login . '<br>';
}
- 获取用户元数据
favorite_color
为'blue'
的用户:
$args = array(
'meta_key' => 'favorite_color',
'meta_value' => 'blue',
);
$blue_lovers = get_users( $args );
foreach ( $blue_lovers as $blue_lover ) {
echo $blue_lover->user_login . '<br>';
}
总结与思考
get_users()
函数的源码分析就到这里了。 通过这次剖析,我们可以看到:
get_users()
函数只是一个简单的入口, 真正的逻辑都藏在WP_User_Query
类里。WP_User_Query
类通过构建 SQL 查询语句,从数据库里获取用户数据。get_users()
函数支持很多参数,可以灵活地查询用户。
希望这次讲座能让你对 get_users()
函数有更深入的了解。 以后再用它的时候,就不会觉得它只是一个黑盒了。
最后,留个小作业: 尝试使用 meta_query
参数,构建一个更复杂的元数据查询条件,例如查询 favorite_color
为 'blue'
或者 favorite_food
为 'pizza'
的用户。 试试看,你会发现 get_users()
函数的强大之处!
各位,下课!