各位观众,晚上好!(敲黑板)
今天咱们来聊聊 WordPress 里的“权限管理”,更具体地说,深入 wpdb
类,扒一扒 has_cap()
这个方法,看看它是怎么在数据库层面判断用户有没有权限干点啥的。别担心,咱们尽量用大白话,配合代码,保证你听完之后也能对着源码指点江山。
一、权限是什么?为什么要管它?
想象一下,你开了一家网站,有人是管理员,有人是编辑,有人是读者。管理员可以删文章、改配置,编辑只能写文章,读者只能看看。这就是权限!
权限管理的目的很简单:
- 安全: 防止恶意用户搞破坏。
- 控制: 保证不同角色只能做他们该做的事。
- 组织: 让网站管理更有条理。
WordPress 的权限系统是基于 Capabilities(能力)的概念。一个 Capability 就像一张许可证,拥有这张许可证的用户就能执行特定的操作。 比如 edit_posts
表示编辑文章的权限,delete_posts
表示删除文章的权限。
二、wpdb
是什么鬼?跟权限有什么关系?
wpdb
类是 WordPress 里的数据库操作核心类。 所有的数据库查询、更新、删除操作,基本都得通过它。
你可能会问:权限判断不是应该在 PHP 代码里搞吗?怎么跟数据库扯上关系了?
别急,WordPress 的权限信息(哪些用户拥有哪些 Capability)实际上是存储在数据库里的。所以,wpdb
就成了判断权限的“中间人”。 当我们调用 has_cap()
判断用户是否有某个权限时,最终还是要通过 wpdb
去数据库里查一下才能知道答案。
三、has_cap()
的源码剖析:一步步追踪权限的足迹
接下来,咱们就来深入研究 has_cap()
方法的源码。 准备好,我们要开始“解剖”了!
-
current_user_can()
函数:权限判断的入口在 WordPress 里,我们通常使用
current_user_can()
函数来判断当前用户是否有某个权限。这个函数其实是对WP_User
类的has_cap()
方法的封装。// wp-includes/capabilities.php function current_user_can( $capability ) { $args = array_slice( func_get_args(), 1 ); return apply_filters( 'current_user_can', get_current_user_id() ? wp_get_current_user()->has_cap( $capability, $args ) : false, $capability, $args ); }
可以看到,它获取当前用户对象(
wp_get_current_user()
),然后调用这个对象的has_cap()
方法。 -
WP_User::has_cap()
方法:能力验证的核心WP_User
类的has_cap()
方法负责实际的能力验证。它会检查用户是否直接拥有该 Capability,或者是否拥有某个角色,而该角色又拥有该 Capability。// wp-includes/class-wp-user.php function has_cap( $capability ) { $args = array_slice( func_get_args(), 1 ); // 如果需要,获取用户元数据 if ( empty( $this->caps ) ) { $this->get_caps_from_db(); } // 如果用户是超级管理员,直接返回 true if ( is_multisite() && is_super_admin( $this->ID ) ) { return apply_filters( 'user_has_cap', true, $capability, $this->ID, $args ); } // 检查用户是否直接拥有该 Capability if ( isset( $this->caps[ $capability ] ) && $this->caps[ $capability ] ) { return apply_filters( 'user_has_cap', true, $capability, $this->ID, $args ); } // 检查用户是否拥有某个角色,而该角色又拥有该 Capability foreach ( (array) $this->roles as $role ) { if ( isset( $this->wp_roles->roles[ $role ]['capabilities'][ $capability ] ) && $this->wp_roles->roles[ $role ]['capabilities'][ $capability ] ) { return apply_filters( 'user_has_cap', true, $capability, $this->ID, $args ); } } // 如果以上条件都不满足,则返回 false return apply_filters( 'user_has_cap', false, $capability, $this->ID, $args ); }
这个方法做了这些事:
- 获取用户元数据: 如果用户的 Capability 信息(
$this->caps
)为空,就调用get_caps_from_db()
方法从数据库里加载。 - 超级管理员判断: 如果用户是超级管理员(在多站点环境中),直接返回
true
。 - 直接 Capability 检查: 检查用户的
$caps
数组里是否直接拥有该 Capability。 - 角色 Capability 检查: 遍历用户的角色,检查每个角色是否拥有该 Capability。
- 获取用户元数据: 如果用户的 Capability 信息(
-
WP_User::get_caps_from_db()
方法:从数据库加载 Capability这个方法是重点,它负责从数据库里加载用户的 Capability 信息。 这里就用到了
wpdb
类。// wp-includes/class-wp-user.php function get_caps_from_db() { global $wpdb, $wp_roles; $this->caps = apply_filters( 'user_capabilities', (array) get_user_meta( $this->ID, $wpdb->prefix . 'capabilities', true ) ); $this->roles = apply_filters( 'user_roles', (array) get_user_meta( $this->ID, $wpdb->prefix . 'user_roles', true ) ); $this->wp_roles = $wp_roles; return; }
看到了吗?这里调用了
get_user_meta()
函数两次:get_user_meta( $this->ID, $wpdb->prefix . 'capabilities', true )
: 获取用户的 Capability 信息。 这个信息以数组的形式存储在wp_usermeta
表里,键名为wp_capabilities
(wp_
是表前缀,可以自定义)。get_user_meta( $this->ID, $wpdb->prefix . 'user_roles', true )
: 获取用户的角色信息。 这个信息也以数组的形式存储在wp_usermeta
表里,键名为wp_user_roles
。
get_user_meta()
函数内部会使用wpdb
类来执行数据库查询。 -
get_user_meta()
函数:封装数据库查询get_user_meta()
函数负责从wp_usermeta
表里获取用户元数据。// wp-includes/user.php function get_user_meta( $user_id, $key = '', $single = false ) { return get_metadata( 'user', $user_id, $key, $single ); }
它实际上是对
get_metadata()
函数的封装,指定了元数据类型为user
。 -
get_metadata()
函数:通用的元数据获取函数get_metadata()
函数是一个通用的元数据获取函数,可以用于获取用户、文章、分类等各种类型的元数据。// wp-includes/meta.php function get_metadata( $meta_type, $object_id, $meta_key = '', $single = false ) { global $wpdb; if ( ! is_numeric( $object_id ) ) { return false; } $table = _get_meta_table( $meta_type ); if ( ! $table ) { return false; } $meta_key = sanitize_key( $meta_key ); $cache_key = sanitize_key( $meta_key ); $id_and_key = "$object_id:$cache_key"; $cache = wp_cache_get( $id_and_key, $meta_type . '_meta' ); if ( false !== $cache ) { if ( $meta_key && ! $single ) { $cache = array_map( 'maybe_unserialize', $cache ); } elseif ( $meta_key && $single ) { if ( isset( $cache[0] ) ) { $cache = maybe_unserialize( $cache[0] ); } else { $cache = ''; } } elseif ( ! $meta_key ) { $cache = array_map( 'maybe_unserialize', $cache ); } return $cache; } $meta_key_clause = $wpdb->prepare( "AND meta_key = %s", $meta_key ); $sql = "SELECT meta_value FROM $table WHERE meta_key != '_edit_lock' AND meta_key != '_edit_last' AND meta_id != 0 AND object_id = %d $meta_key_clause"; if ( $single ) { $sql .= " LIMIT 1"; } $cache = array(); $results = $wpdb->get_col( $wpdb->prepare( $sql, $object_id ) ); if ( ! empty( $results ) ) { if ( $single ) { $cache[0] = $results[0]; } else { $cache = $results; } } wp_cache_set( $id_and_key, $cache, $meta_type . '_meta' ); if ( $meta_key && ! $single ) { $cache = array_map( 'maybe_unserialize', $cache ); } elseif ( $meta_key && $single ) { if ( isset( $cache[0] ) ) { $cache = maybe_unserialize( $cache[0] ); } else { $cache = ''; } } elseif ( ! $meta_key ) { $cache = array_map( 'maybe_unserialize', $cache ); } return $cache; }
这里,我们终于看到了
wpdb
的身影!- 获取表名: 通过
_get_meta_table()
函数获取元数据表名(例如wp_usermeta
)。 - 构建 SQL 查询: 使用
$wpdb->prepare()
方法构建 SQL 查询语句,查询指定用户的指定元数据。 - 执行查询: 使用
$wpdb->get_col()
方法执行 SQL 查询,获取查询结果。 - 缓存结果: 将查询结果缓存起来,下次可以直接从缓存中获取,提高性能。
- 获取表名: 通过
四、总结:has_cap()
的数据库权限判断流程
现在,让我们把整个流程串起来:
- 调用
current_user_can( $capability )
函数。 current_user_can()
调用当前用户对象的has_cap( $capability )
方法。WP_User::has_cap()
方法:- 如果用户的 Capability 信息为空,则调用
get_caps_from_db()
方法从数据库加载。 - 检查用户是否是超级管理员。
- 检查用户是否直接拥有该 Capability。
- 检查用户是否拥有某个角色,而该角色又拥有该 Capability。
- 如果用户的 Capability 信息为空,则调用
WP_User::get_caps_from_db()
方法:- 调用
get_user_meta()
函数两次,分别获取用户的 Capability 和角色信息。
- 调用
get_user_meta()
函数:- 调用
get_metadata()
函数。
- 调用
get_metadata()
函数:- 使用
wpdb
类构建 SQL 查询语句,从元数据表(例如wp_usermeta
)中查询指定用户的指定元数据。 - 使用
wpdb
类执行 SQL 查询。 - 将查询结果缓存起来。
- 使用
用一张表格来概括一下:
步骤 | 涉及函数/方法 | 作用 | 是否涉及 wpdb |
---|---|---|---|
1. 入口 | current_user_can() |
权限判断的入口函数,封装了 WP_User::has_cap() 方法。 |
否 |
2. 能力验证核心 | WP_User::has_cap() |
实际的能力验证,检查用户是否直接拥有 Capability 或通过角色拥有 Capability。 | 否 |
3. 从数据库加载 Capability | WP_User::get_caps_from_db() |
从数据库加载用户的 Capability 和角色信息。 | 否 |
4. 获取用户元数据 | get_user_meta() |
获取指定用户的指定元数据。 | 否 |
5. 通用的元数据获取 | get_metadata() |
通用的元数据获取函数,负责构建 SQL 查询语句并执行,使用 wpdb 类操作数据库。 |
是 |
6. 数据库查询(在 get_metadata 内部) |
$wpdb->prepare() , $wpdb->get_col() |
构建 SQL 查询语句,并执行查询。 | 是 |
五、wpdb
在权限判断中的具体应用
咱们再具体看看 wpdb
在 get_metadata()
函数里是怎么用的。
// 假设我们要获取用户 ID 为 1 的 'wp_capabilities' 元数据
$user_id = 1;
$meta_key = 'wp_capabilities';
$single = true; // 获取单个值
global $wpdb;
$table = $wpdb->usermeta; // 获取 usermeta 表名
// 构建 SQL 查询语句
$sql = $wpdb->prepare(
"SELECT meta_value FROM $table WHERE user_id = %d AND meta_key = %s",
$user_id,
$meta_key
);
// 执行查询
$result = $wpdb->get_var( $sql );
// 解序列化结果(如果需要)
$value = maybe_unserialize( $result );
echo "用户 ID 为 1 的 wp_capabilities 元数据:";
print_r( $value );
这段代码演示了如何使用 wpdb
类直接从 wp_usermeta
表里获取用户的 Capability 信息。
$wpdb->usermeta
获取wp_usermeta
表的名称。$wpdb->prepare()
用于构建安全的 SQL 查询语句,防止 SQL 注入。$wpdb->get_var()
用于执行查询,并获取单个结果。maybe_unserialize()
用于解序列化存储在数据库里的数组。
六、总结与思考
通过对 has_cap()
方法的源码剖析,我们了解了 WordPress 是如何利用 wpdb
类在数据库层面进行权限判断的。
wpdb
类是权限判断的“桥梁”,它负责从数据库里获取用户的 Capability 和角色信息。get_metadata()
函数是关键,它使用wpdb
类构建 SQL 查询语句并执行,获取元数据。- WordPress 的权限系统是基于 Capability 的,通过检查用户是否直接拥有 Capability 或者通过角色拥有 Capability 来判断权限。
理解了这些,你就可以更深入地定制 WordPress 的权限系统,比如:
- 自定义 Capability:添加新的 Capability,用于控制特定的操作。
- 自定义角色:创建新的角色,并分配不同的 Capability。
- 修改数据库查询:如果你对性能有更高的要求,可以优化
get_metadata()
函数里的 SQL 查询语句。
当然,直接操作数据库有风险,一定要小心谨慎!
好了,今天的讲座就到这里。希望大家有所收获! 咱们下次再见!