各位观众老爷们,晚上好!我是你们的老朋友,代码界的段子手。今天咱们要聊聊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() 函数的强大之处!
各位,下课!