各位观众,晚上好!我是你们今晚的WordPress多站点探险向导,代号“Bug猎手”。今天咱们不聊风花雪月,就来扒一扒WordPress多站点模式下的一个核心函数:wp_get_sites()
。
这个函数,就像是多站点网络的“户籍管理员”,负责把所有“居民”(也就是子站点)的信息给你拎出来。你想知道你的网络里都有哪些站点?它们的ID、域名、路径都是啥?就得靠它。
那么,这个“户籍管理员”是怎么工作的呢?让我们深入源码,一探究竟。
一、wp_get_sites()
的身世背景
首先,我们要明确一点:wp_get_sites()
函数只在多站点模式下有效。如果你运行的是单站点WordPress,那它基本上就是个摆设,会直接返回 false
。
它的基本用法很简单:
<?php
$sites = wp_get_sites();
if ($sites) {
foreach ($sites as $site) {
echo "Site ID: " . $site['blog_id'] . "<br>";
echo "Domain: " . $site['domain'] . "<br>";
echo "Path: " . $site['path'] . "<br>";
echo "<hr>";
}
} else {
echo "No sites found or not in multisite mode.";
}
?>
这段代码会遍历所有子站点,并输出它们的ID、域名和路径。但是,wp_get_sites()
背后的运作机制远不止这么简单。
二、源码剖析:一步步追踪“户籍管理员”的足迹
让我们深入 wp-includes/ms-functions.php
文件,找到 wp_get_sites()
函数的真身。
function wp_get_sites( $args = array() ) {
global $wpdb;
if ( ! is_multisite() ) {
return false;
}
$defaults = array(
'limit' => '',
'offset' => '',
'network_id' => get_current_site()->id,
'public' => null,
'archived' => null,
'spam' => null,
'deleted' => null,
'mature' => null,
'fields' => 'all',
'search' => '',
'search_columns' => array( 'domain', 'path' ),
'orderby' => 'blog_id',
'order' => 'ASC',
'number' => null,
'update_site_cache' => true
);
$args = wp_parse_args( $args, $defaults );
/**
* Filters the arguments passed to wp_get_sites().
*
* @since 4.6.0
*
* @param array $args An array of wp_get_sites() arguments.
*/
$args = apply_filters( 'wp_get_sites_args', $args );
$site_cache = array();
$select = '';
switch ( $args['fields'] ) {
case 'ids':
$select = 'blog_id';
break;
case 'names':
$select = 'domain, path';
break;
case 'all_with_path':
$select = 'blog_id, domain, path';
break;
default:
$select = '*';
break;
}
$limit = '';
if ( ! empty( $args['number'] ) ) {
if ( $args['number'] < 0 ) {
$limit = '';
} else {
$number = absint( $args['number'] );
if ( ! empty( $args['offset'] ) ) {
$offset = absint( $args['offset'] );
$limit = "LIMIT {$offset}, {$number}";
} else {
$limit = "LIMIT {$number}";
}
}
} elseif ( ! empty( $args['limit'] ) ) {
if ( $args['limit'] < 0 ) {
$limit = '';
} else {
$limit = 'LIMIT ' . absint( $args['limit'] );
if ( ! empty( $args['offset'] ) ) {
$limit = 'LIMIT ' . absint( $args['offset'] ) . ', ' . absint( $args['limit'] );
}
}
}
$where = "WHERE 1=1";
if ( ! empty( $args['network_id'] ) ) {
$network_id = absint( $args['network_id'] );
$where .= " AND site_id = {$network_id}";
}
if ( null !== $args['public'] ) {
$public = absint( $args['public'] );
$where .= " AND public = {$public}";
}
if ( null !== $args['archived'] ) {
$archived = absint( $args['archived'] );
$where .= " AND archived = {$archived}";
}
if ( null !== $args['spam'] ) {
$spam = absint( $args['spam'] );
$where .= " AND spam = {$spam}";
}
if ( null !== $args['deleted'] ) {
$deleted = absint( $args['deleted'] );
$where .= " AND deleted = {$deleted}";
}
if ( null !== $args['mature'] ) {
$mature = absint( $args['mature'] );
$where .= " AND mature = {$mature}";
}
$search = '';
if ( ! empty( $args['search'] ) ) {
$search_term = esc_sql( $wpdb->esc_like( $args['search'] ) );
$search_columns = array_intersect( $args['search_columns'], array( 'domain', 'path' ) );
if ( ! empty( $search_columns ) ) {
$search = ' AND (';
$search_strings = array();
foreach ( $search_columns as $column ) {
$search_strings[] = "{$column} LIKE '%{$search_term}%'";
}
$search .= implode( ' OR ', $search_strings ) . ')';
}
}
$orderby = esc_sql( $args['orderby'] );
$order = esc_sql( $args['order'] );
$order_clause = "ORDER BY {$orderby} {$order}";
$table = $wpdb->blogs;
$sql = "SELECT {$select} FROM {$table} {$where} {$search} {$order_clause} {$limit}";
$cache_key = 'sites:' . md5( $sql );
$sites = wp_cache_get( $cache_key, 'site-transient' );
if ( false === $sites ) {
if ( 'ids' === $args['fields'] ) {
$sites = $wpdb->get_col( $sql );
if ( ! is_array( $sites ) ) {
return array();
}
$sites = array_map( 'intval', $sites );
} elseif ( 'names' === $args['fields'] ) {
$sites = $wpdb->get_results( $sql, ARRAY_A );
if ( ! is_array( $sites ) ) {
return array();
}
} elseif ( 'all_with_path' === $args['fields'] ) {
$sites = $wpdb->get_results( $sql, ARRAY_A );
if ( ! is_array( $sites ) ) {
return array();
}
} else {
$sites = $wpdb->get_results( $sql, ARRAY_A );
if ( ! is_array( $sites ) ) {
return array();
}
foreach ( $sites as $site ) {
$site_cache[ $site['blog_id'] ] = $site;
}
}
wp_cache_set( $cache_key, $sites, 'site-transient', DAY_IN_SECONDS );
}
if ( 'all' === $args['fields'] && $args['update_site_cache'] ) {
// Populate the cache.
if ( ! empty( $site_cache ) ) {
foreach ( $site_cache as $key => $value ) {
wp_cache_set( $key, $value, 'site' );
}
} else {
foreach ( $sites as $site ) {
wp_cache_set( $site['blog_id'], $site, 'site' );
}
}
}
/**
* Filters the array of sites returned by wp_get_sites().
*
* @since 3.7.0
*
* @param array $sites Array of WP_Site objects.
* @param array $args Array of wp_get_sites() arguments.
*/
return apply_filters( 'wp_get_sites', $sites, $args );
}
代码有点长,别怕,我们把它拆解成几个关键步骤:
-
检查是否为多站点模式:
if ( ! is_multisite() ) { return false; }
这是最基础的一步。如果不是多站点,直接返回
false
,没啥好说的。 -
设置默认参数:
$defaults = array( 'limit' => '', 'offset' => '', 'network_id' => get_current_site()->id, 'public' => null, 'archived' => null, 'spam' => null, 'deleted' => null, 'mature' => null, 'fields' => 'all', 'search' => '', 'search_columns' => array( 'domain', 'path' ), 'orderby' => 'blog_id', 'order' => 'ASC', 'number' => null, 'update_site_cache' => true ); $args = wp_parse_args( $args, $defaults );
这里定义了一堆默认参数,允许你自定义查询行为。比如,你可以限制返回站点的数量 (
limit
),设置偏移量 (offset
),指定网络ID (network_id
),过滤公开站点 (public
)、已存档站点 (archived
)、垃圾站点 (spam
)、已删除站点 (deleted
)、成人站点 (mature
),以及指定返回哪些字段 (fields
)等等。wp_parse_args()
函数会将你传入的参数与默认参数合并,确保所有需要的参数都有值。 -
构建 SQL 查询语句:
这是整个函数的灵魂所在。它根据你传入的参数,动态构建 SQL 查询语句,从数据库中检索站点信息。
-
选择字段 (
$select
):$select = ''; switch ( $args['fields'] ) { case 'ids': $select = 'blog_id'; break; case 'names': $select = 'domain, path'; break; case 'all_with_path': $select = 'blog_id, domain, path'; break; default: $select = '*'; break; }
根据
fields
参数的值,选择要查询的字段。你可以只获取站点ID (blog_id
),或者只获取域名和路径 (domain, path
),或者同时获取ID、域名和路径 (blog_id, domain, path
),或者获取所有字段 (*
)。 -
限制数量 (
$limit
):$limit = ''; if ( ! empty( $args['number'] ) ) { // ... } elseif ( ! empty( $args['limit'] ) ) { // ... }
根据
number
或limit
参数的值,设置LIMIT
子句,限制返回站点的数量。 -
构建 WHERE 子句 (
$where
):$where = "WHERE 1=1"; if ( ! empty( $args['network_id'] ) ) { $network_id = absint( $args['network_id'] ); $where .= " AND site_id = {$network_id}"; } if ( null !== $args['public'] ) { $public = absint( $args['public'] ); $where .= " AND public = {$public}"; } // ... 其他过滤条件
这是最复杂的部分。它根据
network_id
、public
、archived
、spam
、deleted
、mature
等参数的值,构建WHERE
子句,过滤出符合条件的站点。 -
构建搜索子句 (
$search
):$search = ''; if ( ! empty( $args['search'] ) ) { $search_term = esc_sql( $wpdb->esc_like( $args['search'] ) ); $search_columns = array_intersect( $args['search_columns'], array( 'domain', 'path' ) ); if ( ! empty( $search_columns ) ) { $search = ' AND ('; $search_strings = array(); foreach ( $search_columns as $column ) { $search_strings[] = "{$column} LIKE '%{$search_term}%'"; } $search .= implode( ' OR ', $search_strings ) . ')'; } }
根据
search
参数的值,构建AND
子句,在指定的列 (search_columns
) 中搜索包含指定关键词的站点。 注意这里使用了esc_sql()
和$wpdb->esc_like()
进行转义,防止 SQL 注入。 -
构建排序子句 (
$order_clause
):$orderby = esc_sql( $args['orderby'] ); $order = esc_sql( $args['order'] ); $order_clause = "ORDER BY {$orderby} {$order}";
根据
orderby
和order
参数的值,设置ORDER BY
子句,指定排序方式。 -
组装完整的 SQL 语句:
$table = $wpdb->blogs; $sql = "SELECT {$select} FROM {$table} {$where} {$search} {$order_clause} {$limit}";
将所有子句组合起来,形成完整的 SQL 查询语句。
$wpdb->blogs
通常对应的是wp_blogs
表,存储着站点的基本信息。
-
-
查询数据库并缓存结果:
$cache_key = 'sites:' . md5( $sql ); $sites = wp_cache_get( $cache_key, 'site-transient' ); if ( false === $sites ) { // ... 查询数据库 wp_cache_set( $cache_key, $sites, 'site-transient', DAY_IN_SECONDS ); }
首先,根据 SQL 语句生成一个缓存键 (
$cache_key
)。然后,尝试从缓存中获取结果。如果缓存中没有结果,就执行 SQL 查询,并将结果存入缓存,有效期为一天。这里使用了 WordPress 的对象缓存机制 (
wp_cache_get()
和wp_cache_set()
),可以有效提高查询效率。 -
处理查询结果:
if ( 'ids' === $args['fields'] ) { $sites = $wpdb->get_col( $sql ); if ( ! is_array( $sites ) ) { return array(); } $sites = array_map( 'intval', $sites ); } elseif ( 'names' === $args['fields'] ) { $sites = $wpdb->get_results( $sql, ARRAY_A ); if ( ! is_array( $sites ) ) { return array(); } } elseif ( 'all_with_path' === $args['fields'] ) { $sites = $wpdb->get_results( $sql, ARRAY_A ); if ( ! is_array( $sites ) ) { return array(); } } else { $sites = $wpdb->get_results( $sql, ARRAY_A ); if ( ! is_array( $sites ) ) { return array(); } foreach ( $sites as $site ) { $site_cache[ $site['blog_id'] ] = $site; } }
根据
fields
参数的值,对查询结果进行处理。如果只需要站点ID,就使用$wpdb->get_col()
获取ID列表;如果只需要域名和路径,或者需要ID、域名和路径,就使用$wpdb->get_results( $sql, ARRAY_A )
获取关联数组;如果需要所有字段,就使用$wpdb->get_results( $sql, ARRAY_A )
获取关联数组,并将结果存入$site_cache
数组,方便后续缓存。 -
更新站点缓存:
if ( 'all' === $args['fields'] && $args['update_site_cache'] ) { // Populate the cache. if ( ! empty( $site_cache ) ) { foreach ( $site_cache as $key => $value ) { wp_cache_set( $key, $value, 'site' ); } } else { foreach ( $sites as $site ) { wp_cache_set( $site['blog_id'], $site, 'site' ); } } }
如果
fields
参数的值为all
并且update_site_cache
参数的值为true
,就将查询结果存入站点缓存 (wp_cache_set( $key, $value, 'site' )
),方便其他函数使用。 -
应用过滤器并返回结果:
return apply_filters( 'wp_get_sites', $sites, $args );
最后,应用
wp_get_sites
过滤器,允许其他插件或主题修改查询结果,然后返回最终结果。
三、wp_get_sites()
的参数详解
为了更灵活地使用 wp_get_sites()
函数,我们需要了解它的所有参数。
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
limit |
int | '' |
限制返回站点的数量。如果为空,则返回所有站点。 |
offset |
int | '' |
设置偏移量,从第几个站点开始返回。 |
network_id |
int | get_current_site()->id |
指定网络ID。如果为空,则使用当前网络的ID。 |
public |
bool | null |
过滤公开站点。如果为 true ,则只返回公开站点;如果为 false ,则只返回非公开站点;如果为 null ,则不进行过滤。 |
archived |
bool | null |
过滤已存档站点。如果为 true ,则只返回已存档站点;如果为 false ,则只返回未存档站点;如果为 null ,则不进行过滤。 |
spam |
bool | null |
过滤垃圾站点。如果为 true ,则只返回垃圾站点;如果为 false ,则只返回非垃圾站点;如果为 null ,则不进行过滤。 |
deleted |
bool | null |
过滤已删除站点。如果为 true ,则只返回已删除站点;如果为 false ,则只返回未删除站点;如果为 null ,则不进行过滤。 |
mature |
bool | null |
过滤成人站点。如果为 true ,则只返回成人站点;如果为 false ,则只返回非成人站点;如果为 null ,则不进行过滤。 |
fields |
string | 'all' |
指定返回哪些字段。可选值包括:'ids' (只返回站点ID)、'names' (只返回域名和路径)、'all_with_path' (返回ID、域名和路径)、'all' (返回所有字段)。 |
search |
string | '' |
在指定的列中搜索包含指定关键词的站点。 |
search_columns |
array | array( 'domain', 'path' ) |
指定搜索的列。可选值包括:'domain' (域名)、'path' (路径)。 |
orderby |
string | 'blog_id' |
指定排序的字段。可选值包括:'blog_id' (站点ID)、'domain' (域名)、'path' (路径)、'registered' (注册时间)、'last_updated' (上次更新时间)。 |
order |
string | 'ASC' |
指定排序方式。可选值包括:'ASC' (升序)、'DESC' (降序)。 |
number |
int | null |
与 limit 相同,用于限制返回站点的数量。 |
update_site_cache |
bool | true |
是否更新站点缓存。如果为 true ,则将查询结果存入站点缓存,方便其他函数使用。 |
四、使用示例:各种姿势查询子站点
有了这些知识,我们就可以灵活地使用 wp_get_sites()
函数了。
-
获取所有站点的ID:
$site_ids = wp_get_sites( array( 'fields' => 'ids' ) ); if ($site_ids) { foreach ($site_ids as $site_id) { echo "Site ID: " . $site_id . "<br>"; } }
-
获取前5个公开站点:
$public_sites = wp_get_sites( array( 'public' => true, 'limit' => 5 ) ); if ($public_sites) { foreach ($public_sites as $site) { echo "Site ID: " . $site['blog_id'] . "<br>"; echo "Domain: " . $site['domain'] . "<br>"; echo "Path: " . $site['path'] . "<br>"; echo "<hr>"; } }
-
搜索域名或路径包含 "blog" 的站点:
$search_sites = wp_get_sites( array( 'search' => 'blog' ) ); if ($search_sites) { foreach ($search_sites as $site) { echo "Site ID: " . $site['blog_id'] . "<br>"; echo "Domain: " . $site['domain'] . "<br>"; echo "Path: " . $site['path'] . "<br>"; echo "<hr>"; } }
-
获取所有站点的域名和路径,并按域名升序排序:
$site_names = wp_get_sites( array( 'fields' => 'names', 'orderby' => 'domain', 'order' => 'ASC' ) ); if ($site_names) { foreach ($site_names as $site) { echo "Domain: " . $site['domain'] . "<br>"; echo "Path: " . $site['path'] . "<br>"; echo "<hr>"; } }
五、总结与建议
wp_get_sites()
函数是 WordPress 多站点模式下管理子站点的利器。通过理解它的源码和参数,你可以灵活地查询和过滤站点信息,满足各种需求。
在使用 wp_get_sites()
函数时,请注意以下几点:
- 性能优化: 尽量使用缓存,避免频繁查询数据库。合理设置
limit
和offset
参数,避免返回过多的数据。 - 安全性: 对用户输入进行转义,防止 SQL 注入。
- 代码可读性: 使用有意义的参数名,并添加适当的注释,方便他人理解你的代码。
- 善用过滤器:
wp_get_sites
过滤器允许你自定义查询结果,实现更高级的功能。
好了,今天的“WordPress多站点探险之旅”就到此结束。希望通过这次旅程,你对 wp_get_sites()
函数有了更深入的了解。下次再见,祝大家 Bug Free!