各位WordPress探险家们,欢迎来到今天的源码解剖课堂!今天我们要一起扒一扒WordPress里一个相当实用的函数,get_blogs_of_user()
,看看它是怎么把一个用户“扒”得精光,哦不,是找到这个用户所属的所有站点的。
开场白:用户与站点,一场不得不说的关系
在WordPress的世界里,用户和站点之间的关系,就像鱼和水,鸟和树林,程序员和Bug…总之,它们是紧密相连的。一个用户可以只属于一个站点,也可以是多个站点的成员,拥有不同的角色和权限。而get_blogs_of_user()
函数,就是用来查询指定用户,在哪些站点里有“户口”,也就是哪些站点里有它的身影。
正文:get_blogs_of_user()
的源码解剖
好了,废话不多说,咱们直接上源码,看看这个函数到底是怎么工作的。
/**
* Retrieve all blogs of a user.
*
* @since 3.0.0
*
* @param int $user_id User ID.
* @param bool $all Whether to retrieve all sites of the user. Default is false.
* @return array An array of site ID keys and site URL values.
*/
function get_blogs_of_user( $user_id, $all = false ) {
global $wpdb;
$user_id = (int) $user_id;
if ( empty( $user_id ) ) {
return array();
}
/**
* Filters the blogs of a user.
*
* @since 3.0.0
*
* @param array $blogs Array of site IDs and site URL values.
* @param int $user_id User ID.
* @param bool $all Whether to retrieve all sites of the user. Default is false.
*/
$blogs = apply_filters( 'pre_get_blogs_of_user', null, $user_id, $all );
if ( ! is_null( $blogs ) ) {
return $blogs;
}
$cache_key = "blogs_of_user_{$user_id}";
if ( $all ) {
$cache_key .= '_all';
}
$cache = wp_cache_get( $cache_key, 'site-transient' );
if ( false !== $cache ) {
/**
* Filters the blogs of a user from the cache.
*
* @since 3.0.0
*
* @param array $cache Array of site IDs and site URL values.
* @param int $user_id User ID.
* @param bool $all Whether to retrieve all sites of the user. Default is false.
*/
$cache = apply_filters( 'get_blogs_of_user_cache', $cache, $user_id, $all );
return $cache;
}
$blogs = array();
if ( is_multisite() ) {
if ( $all ) {
$query = $wpdb->prepare(
"SELECT blog_id FROM {$wpdb->prefix}users WHERE user_id = %d",
$user_id
);
$ids = $wpdb->get_col( $query );
} else {
$query = $wpdb->prepare(
"SELECT blog_id FROM {$wpdb->prefix}usermeta WHERE user_id = %d AND meta_key = 'wp_%d_capabilities' AND meta_value NOT LIKE '%%s:13:"administrator"%%'",
$user_id,
$user_id
);
$ids = $wpdb->get_col( $query );
}
if ( ! empty( $ids ) ) {
foreach ( $ids as $id ) {
$id = (int) $id;
$details = get_blog_details( $id );
if ( $details ) {
$blogs[ $id ] = $details->siteurl;
}
}
}
} else {
$blogs[ get_current_blog_id() ] = get_home_url();
}
wp_cache_set( $cache_key, $blogs, 'site-transient', DAY_IN_SECONDS );
/**
* Filters the blogs of a user.
*
* @since 3.0.0
*
* @param array $blogs Array of site IDs and site URL values.
* @param int $user_id User ID.
* @param bool $all Whether to retrieve all sites of the user. Default is false.
*/
return apply_filters( 'get_blogs_of_user', $blogs, $user_id, $all );
}
源码分解:步步为营,逐行剖析
咱们把这段代码拆开,像拆解一个精密的机械钟表一样,看看每个零件都干了啥。
-
函数定义和参数说明
function get_blogs_of_user( $user_id, $all = false ) {
$user_id
: 必须的参数,要查询的用户ID。$all
: 可选参数,一个布尔值。如果设置为true
,就查询用户所有的站点(包括管理员权限的站点)。如果设置为false
(默认值),则只查询用户非管理员权限的站点。
-
参数校验和默认值处理
global $wpdb; $user_id = (int) $user_id; if ( empty( $user_id ) ) { return array(); }
- 首先,声明全局变量
$wpdb
,这是WordPress用来操作数据库的核心对象。 - 然后,强制将
$user_id
转换为整数类型,防止SQL注入之类的安全问题。 - 接着,判断
$user_id
是否为空。如果为空,说明没有提供用户ID,直接返回一个空数组。
- 首先,声明全局变量
-
pre_get_blogs_of_user
过滤器$blogs = apply_filters( 'pre_get_blogs_of_user', null, $user_id, $all ); if ( ! is_null( $blogs ) ) { return $blogs; }
- 这是一个非常重要的环节,它允许开发者在函数执行的核心逻辑之前,通过过滤器来改变函数的行为或者直接返回结果。
apply_filters()
函数会调用所有挂载到pre_get_blogs_of_user
钩子上的函数。- 如果任何一个挂载的函数返回了非
null
的值,那么这个值就会被作为get_blogs_of_user()
的最终结果返回,后面的代码就不会执行了。这就像一个“短路”操作,可以用来实现自定义的缓存机制或者权限控制。
-
缓存机制
$cache_key = "blogs_of_user_{$user_id}"; if ( $all ) { $cache_key .= '_all'; } $cache = wp_cache_get( $cache_key, 'site-transient' ); if ( false !== $cache ) { $cache = apply_filters( 'get_blogs_of_user_cache', $cache, $user_id, $all ); return $cache; }
- WordPress使用了缓存来提高性能。这里先生成一个缓存键
$cache_key
,它基于$user_id
和$all
的值。 - 然后,使用
wp_cache_get()
函数尝试从缓存中获取数据。 - 如果缓存中存在数据(
$cache !== false
),则应用get_blogs_of_user_cache
过滤器,允许开发者修改缓存的数据,并直接返回缓存的数据。
- WordPress使用了缓存来提高性能。这里先生成一个缓存键
-
多站点判断和数据库查询
$blogs = array(); if ( is_multisite() ) { if ( $all ) { $query = $wpdb->prepare( "SELECT blog_id FROM {$wpdb->prefix}users WHERE user_id = %d", $user_id ); $ids = $wpdb->get_col( $query ); } else { $query = $wpdb->prepare( "SELECT blog_id FROM {$wpdb->prefix}usermeta WHERE user_id = %d AND meta_key = 'wp_%d_capabilities' AND meta_value NOT LIKE '%%s:13:"administrator"%%'", $user_id, $user_id ); $ids = $wpdb->get_col( $query ); } if ( ! empty( $ids ) ) { foreach ( $ids as $id ) { $id = (int) $id; $details = get_blog_details( $id ); if ( $details ) { $blogs[ $id ] = $details->siteurl; } } } } else { $blogs[ get_current_blog_id() ] = get_home_url(); }
is_multisite()
函数判断当前WordPress是否是多站点模式。- 如果是多站点模式:
- 如果
$all
为true
,则查询wp_users
表,找出$user_id
存在的所有blog_id
。注意,这个表在多站点中存在链接用户与站点的关系,但仅限于用户最初被创建时所在的站点。 - 如果
$all
为false
,则查询wp_usermeta
表,找出$user_id
的wp_{blog_id}_capabilities
元数据,并排除administrator
角色,从而找到用户不是管理员的站点。这里用到了LIKE '%%s:13:"administrator"%%'
来排除管理员角色,这是一种比较hacky的做法,因为角色信息是序列化存储的。 - 获取到
blog_id
列表后,遍历这些ID,使用get_blog_details()
函数获取站点的详细信息,并将站点的ID和URL存储到$blogs
数组中。
- 如果
- 如果不是多站点模式:
- 直接将当前站点的ID和URL存储到
$blogs
数组中。
- 直接将当前站点的ID和URL存储到
-
设置缓存
wp_cache_set( $cache_key, $blogs, 'site-transient', DAY_IN_SECONDS );
- 将查询到的站点信息存储到缓存中,以便下次更快地获取。缓存的有效期是
DAY_IN_SECONDS
(一天)。
- 将查询到的站点信息存储到缓存中,以便下次更快地获取。缓存的有效期是
-
get_blogs_of_user
过滤器return apply_filters( 'get_blogs_of_user', $blogs, $user_id, $all );
- 这是另一个过滤器,允许开发者在函数返回结果之前,对结果进行最后的修改。
代码示例:如何使用get_blogs_of_user()
<?php
// 获取用户ID为1的所有站点(包括管理员权限的站点)
$user_id = 1;
$all_sites = get_blogs_of_user( $user_id, true );
echo "用户ID为 {$user_id} 的所有站点:<br>";
echo "<ul>";
foreach ( $all_sites as $blog_id => $blog_url ) {
echo "<li>站点ID:{$blog_id},站点URL:{$blog_url}</li>";
}
echo "</ul>";
// 获取用户ID为2的非管理员站点
$user_id = 2;
$non_admin_sites = get_blogs_of_user( $user_id ); // $all 默认为 false
echo "用户ID为 {$user_id} 的非管理员站点:<br>";
echo "<ul>";
foreach ( $non_admin_sites as $blog_id => $blog_url ) {
echo "<li>站点ID:{$blog_id},站点URL:{$blog_url}</li>";
}
echo "</ul>";
?>
注意事项和坑点
-
多站点环境下的
wp_users
表: 在多站点环境中,wp_users
表存储的用户数据,其blog_id
列指示了用户最初被创建时所在的站点。这并不意味着用户仅属于该站点,用户可能被添加到其他站点。因此,如果你需要获取用户的所有站点,通常需要结合wp_usermeta
表的信息。 -
角色判断的hacky方式: 使用
LIKE '%%s:13:"administrator"%%'
来判断角色是否是管理员,这种方式依赖于角色信息序列化的格式,如果WordPress的角色管理机制发生变化,这段代码可能会失效。更健壮的做法是使用WordPress提供的角色管理API,例如get_user_meta( $user_id, 'wp_{blog_id}_capabilities', true )
获取用户的角色,然后判断是否包含administrator
角色。 -
缓存问题: 虽然WordPress使用了缓存来提高性能,但在某些情况下,缓存可能会导致数据不一致。例如,如果用户被添加或移除站点后,缓存没有及时更新,那么
get_blogs_of_user()
可能会返回过时的数据。可以使用wp_cache_delete()
函数来手动清除缓存。 -
性能问题: 在多站点环境下,如果站点数量非常多,
get_blogs_of_user()
函数可能会执行较慢,因为它需要查询数据库并遍历所有站点。可以考虑使用更高效的查询方式或者自定义的缓存机制来优化性能。
get_blogs_of_user
的优化方向
既然说到了性能,我们不妨再深入探讨一下get_blogs_of_user
的优化方向。毕竟,任何代码都有改进的空间,对吧?
-
更精确的角色判断:
正如前面提到的,使用
LIKE
语句判断角色并不靠谱。更稳妥的方式是直接获取用户的角色信息,然后进行判断。可以这样修改:if ( ! $all ) { $sites = get_sites(); $ids = array(); foreach ($sites as $site) { $blog_id = $site->blog_id; $capabilities = get_user_meta($user_id, 'wp_' . $blog_id . '_capabilities', true); if (isset($capabilities['administrator'])) { continue; // Skip administrator roles. } $ids[] = $blog_id; } }
这种方式虽然代码稍微多了一些,但是更准确,也更易于维护。
-
更高效的缓存策略:
当前的缓存策略是简单的将结果缓存一段时间。可以考虑使用更精细的缓存策略,例如:
- 基于事件的缓存失效: 当用户被添加到站点或从站点移除时,清除相关的缓存。可以使用
add_user_to_blog
和remove_user_from_blog
钩子来实现。 - 分层缓存: 将站点信息存储在不同的缓存层级,例如内存缓存(Memcached或Redis)和数据库缓存。
- 基于事件的缓存失效: 当用户被添加到站点或从站点移除时,清除相关的缓存。可以使用
-
数据库查询优化:
如果站点数量非常多,数据库查询可能会成为瓶颈。可以考虑使用更高效的查询方式,例如:
- 使用JOIN查询: 将
wp_usermeta
表和wp_blogs
表进行JOIN查询,一次性获取所有需要的信息。 - 使用索引: 确保
wp_usermeta
表的user_id
和meta_key
列上有索引。
- 使用JOIN查询: 将
总结:掌握利器,纵横WordPress世界
今天我们一起深入剖析了WordPress的get_blogs_of_user()
函数,从源码到示例,再到注意事项和优化方向,相信大家对这个函数有了更深入的理解。
记住,理解源码是成为WordPress高手的必经之路。掌握这些利器,你就可以在WordPress的世界里纵横驰骋,解决各种复杂的问题,成为真正的探险家!下次再见!