各位观众老爷,晚上好!我是今晚的讲解员,江湖人称“代码搬运工”,今天给大家伙儿唠唠 WordPress 权限系统里一个非常重要的函数——current_user_can()
。这玩意儿就像 WordPress 的安检门,检查你有没有资格进入某些区域,或者执行某些操作。
咱们今天要深入源码,扒开它的底裤,看看它到底是怎么工作的。重点是map_meta_cap
这个过滤器,它可是权限映射的幕后推手,能把一些抽象的“元能力”翻译成具体的、用户组拥有的能力。
一、current_user_can()
:你的权限够不够?
首先,咱们先来简单了解一下 current_user_can()
这个函数。它的作用很简单:判断当前用户是否拥有某个指定的权限。
<?php
/**
* Checks the current user's permissions to perform a specific action.
*
* @param string $capability The capability to check.
* @param mixed ...$args Optional. Additional arguments for the capability check.
* @return bool True if the current user has the capability, false otherwise.
*/
function current_user_can( string $capability, ...$args ): bool {
global $wp_current_filter;
$args = array_merge( array( $capability ), $args );
/**
* Filters the capability being checked against.
*
* @since 2.0.0
*
* @param string $capability Name of capability being checked.
* @param int|null $user_id ID of the user being checked.
* @param array $args Array of the parameters passed (to current_user_can()).
*/
$capability = apply_filters( 'user_has_cap', $capability, get_current_user_id(), $args );
if ( ! is_scalar( $capability ) || empty( $capability ) ) {
return false;
}
$wp_roles = wp_roles();
if ( empty( $wp_roles ) ) {
return false;
}
$roles = wp_get_current_user()->roles;
// If the user has no roles, return false.
if ( empty( $roles ) ) {
return false;
}
// Iterate through each role that the user has.
foreach ( $roles as $role ) {
if ( isset( $wp_roles->role_objects[ $role ] ) ) {
$role_obj = $wp_roles->role_objects[ $role ];
} else {
continue;
}
if ( isset( $role_obj->capabilities[ $capability ] ) && true === $role_obj->capabilities[ $capability ] ) {
return true;
}
}
// Second, site specific caps.
if ( is_multisite() ) {
foreach ( $roles as $role ) {
$ms_users = get_site_option( 'wp_user_roles' );
if ( isset( $ms_users[ $role ]['capabilities'][ $capability ] ) && true === $ms_users[ $role ]['capabilities'][ $capability ] ) {
return true;
}
}
}
// Finally, super admin.
if ( is_super_admin() ) {
return true;
}
return false;
}
简单来说,你调用 current_user_can('edit_posts')
,它就会检查当前用户是否拥有编辑文章的权限。 如果有,返回 true
,否则返回 false
。
参数解释:
$capability
: (string) 必须。要检查的权限(能力)名称。比如'edit_posts'
、'manage_options'
等等。...$args
: (mixed) 可选。传递给权限检查的其他参数。这些参数会被传递给map_meta_cap
过滤器,用于更细致的权限判断。 例如,current_user_can('edit_post', $post_id)
,这里的$post_id
就是额外的参数。
工作流程:
- 参数准备: 将
capability
和args
合并成一个数组。 user_has_cap
过滤器: 应用user_has_cap
过滤器,允许修改要检查的 capability。- 角色检查: 获取当前用户的角色,并遍历这些角色,检查这些角色是否直接拥有
$capability
权限。 - 多站点检查(如果是多站点): 检查用户是否在当前站点拥有
$capability
权限。 - 超级管理员检查: 如果用户是超级管理员,直接返回
true
。 - 返回结果: 如果以上都没有找到
$capability
权限,返回false
。
举个栗子:
<?php
// 检查当前用户是否可以编辑 ID 为 123 的文章
if ( current_user_can('edit_post', 123) ) {
echo '当前用户可以编辑该文章!';
} else {
echo '当前用户无权编辑该文章!';
}
?>
二、map_meta_cap
:权限映射的幕后推手
current_user_can()
内部依赖于 WordPress 的角色和权限系统。 但是,有些权限是不能直接通过角色来定义的,比如“编辑某个特定的文章”这种权限。 这时候,map_meta_cap
过滤器就派上用场了。
map_meta_cap
的作用是将一些“元能力”(meta capabilities)映射到实际的角色能力上。 所谓“元能力”,就是一些抽象的、需要根据具体情况才能判断的权限。 比如 'edit_post'
、'delete_post'
、'read_post'
等等。
map_meta_cap
过滤器的定义:
<?php
/**
* Filters the capabilities required for a specific action.
*
* @since 2.8.0
*
* @param string[] $caps Returns the user's actual capabilities.
* @param string $cap Capability name.
* @param int $user_id User ID.
* @param array $args Adds the context to the cap. Typically the object ID.
*/
function map_meta_cap( array $caps, string $cap, int $user_id, array $args ): array;
参数解释:
$caps
: (string[]) 数组。 初始值为一个包含$cap
的数组。 这个数组最终会被修改,返回用户实际需要的权限。$cap
: (string) 要映射的元能力名称。 比如'edit_post'
。$user_id
: (int) 用户 ID。$args
: (array) 传递给current_user_can()
的额外参数。 对于'edit_post'
来说,通常是文章 ID。
工作流程:
current_user_can()
调用apply_filters('map_meta_cap', $caps, $capability, $user_id, $args)
。- 注册到
map_meta_cap
过滤器的函数会被依次执行。 - 这些函数根据
$cap
和$args
的值,判断用户是否应该拥有该权限。 - 如果用户应该拥有该权限,则修改
$caps
数组,将其替换为实际的角色能力。 - 如果用户不应该拥有该权限,则
$caps
数组保持不变。 current_user_can()
最终根据$caps
数组中的权限来判断用户是否有权执行操作。
举个栗子:edit_post
权限的映射
WordPress 核心代码中,对 edit_post
权限的映射大致如下(简化版):
<?php
add_filter( 'map_meta_cap', 'my_map_meta_cap', 10, 4 );
function my_map_meta_cap( $caps, $cap, $user_id, $args ) {
// 如果不是 edit_post 权限,直接返回
if ( 'edit_post' !== $cap ) {
return $caps;
}
// 确保有文章 ID
if ( empty( $args[0] ) ) {
return array( 'do_not_allow' ); // 不允许
}
$post_id = (int) $args[0];
$post = get_post( $post_id );
if ( empty( $post ) ) {
return array( 'do_not_allow' ); // 文章不存在,不允许
}
// 获取文章作者 ID
$author_id = (int) $post->post_author;
// 获取当前用户的信息
$user = get_userdata( $user_id );
// 如果当前用户是作者,并且允许编辑自己的文章
if ( $user_id === $author_id ) {
if ( 'publish' === $post->post_status ) {
$caps = array( 'edit_published_posts' ); // 允许编辑已发布的文章
} else {
$caps = array( 'edit_posts' ); // 允许编辑未发布的文章
}
} else {
// 如果不是作者,则需要 edit_others_posts 权限
$caps = array( 'edit_others_posts' );
}
return $caps;
}
?>
代码解释:
- 注册过滤器: 使用
add_filter('map_meta_cap', 'my_map_meta_cap', 10, 4)
注册my_map_meta_cap
函数到map_meta_cap
过滤器。 - 判断
$cap
: 首先判断$cap
是否为'edit_post'
,如果不是,则直接返回$caps
,不做任何修改。 - 获取文章信息: 从
$args
中获取文章 ID,并使用get_post()
函数获取文章对象。如果文章不存在,则返回array('do_not_allow')
,表示不允许该用户编辑该文章。 - 判断用户是否为作者: 判断当前用户是否为文章的作者。
- 如果是作者,则根据文章的状态(已发布或未发布),返回
'edit_published_posts'
或'edit_posts'
权限。 - 如果不是作者,则返回
'edit_others_posts'
权限。
- 如果是作者,则根据文章的状态(已发布或未发布),返回
- 返回
$caps
: 返回修改后的$caps
数组。
总结一下,这个例子说明了 map_meta_cap
如何将抽象的 'edit_post'
权限映射到具体的角色权限上:
- 如果用户是文章作者, 则需要
'edit_posts'
或'edit_published_posts'
权限。 - 如果用户不是文章作者, 则需要
'edit_others_posts'
权限。
这意味着:
- 如果一个用户拥有
'edit_others_posts'
权限,那么他就可以编辑所有人的文章。 - 如果一个用户只拥有
'edit_posts'
权限,那么他只能编辑自己的文章。
三、map_meta_cap
的应用场景
map_meta_cap
过滤器在 WordPress 中被广泛使用,常见的应用场景包括:
- 自定义文章类型权限: 为自定义文章类型设置独立的编辑、删除、阅读权限。
- 用户角色权限细化: 根据用户的角色和具体情况,动态调整用户的权限。
- 插件权限控制: 插件可以使用
map_meta_cap
来控制用户对插件功能的访问权限。
举个栗子:自定义文章类型的权限控制
假设我们创建了一个名为 book
的自定义文章类型,我们希望只有具有 'manage_books'
权限的用户才能管理所有书籍,而作者只能编辑自己发布的书籍。
<?php
// 注册自定义文章类型
function my_register_post_type() {
$args = array(
'public' => true,
'label' => '书籍',
'supports' => array( 'title', 'editor', 'author' ),
'capability_type' => 'book', // 设置 capability_type
'map_meta_cap' => true, // 启用 map_meta_cap
);
register_post_type( 'book', $args );
}
add_action( 'init', 'my_register_post_type' );
// 添加权限
function my_add_role_caps() {
// 获取管理员角色
$role = get_role( 'administrator' );
// 为管理员添加 manage_books 权限
$role->add_cap( 'manage_books' );
}
add_action( 'admin_init', 'my_add_role_caps' );
// 权限映射
add_filter( 'map_meta_cap', 'my_map_book_caps', 10, 4 );
function my_map_book_caps( $caps, $cap, $user_id, $args ) {
if ( 'book' !== get_post_type( $args[0] ) ) {
return $caps;
}
switch ( $cap ) {
case 'edit_book':
$post = get_post( $args[0] );
if ( $user_id == $post->post_author ) {
$caps = array( 'edit_posts' );
} else {
$caps = array( 'manage_books' );
}
break;
case 'read_book':
$caps = array( 'read' );
break;
case 'delete_book':
$post = get_post( $args[0] );
if ( $user_id == $post->post_author ) {
$caps = array( 'delete_posts' );
} else {
$caps = array( 'manage_books' );
}
break;
case 'edit_books':
case 'edit_others_books':
case 'publish_books':
case 'read_private_books':
case 'delete_books':
case 'delete_private_books':
case 'delete_published_books':
case 'delete_others_books':
$caps = array( 'manage_books' );
break;
default:
$caps = array( 'manage_books' );
break;
}
return $caps;
}
?>
代码解释:
- 注册自定义文章类型: 使用
register_post_type()
函数注册book
自定义文章类型。 关键在于设置了'capability_type' => 'book'
和'map_meta_cap' => true
。capability_type
定义了权限的前缀,map_meta_cap
启用了权限映射。 - 添加权限: 为管理员角色添加
'manage_books'
权限。 - 权限映射: 使用
map_meta_cap
过滤器,将edit_book
、read_book
、delete_book
等权限映射到实际的角色权限上。edit_book
:如果是作者,则需要'edit_posts'
权限,否则需要'manage_books'
权限。read_book
:需要'read'
权限。delete_book
:如果是作者,则需要'delete_posts'
权限,否则需要'manage_books'
权限。- 其他的
books
相关权限,都需要'manage_books'
权限。
四、总结
current_user_can()
函数是 WordPress 权限系统的核心,而 map_meta_cap
过滤器则是实现灵活权限控制的关键。 通过 map_meta_cap
,我们可以将抽象的元能力映射到具体的角色权限上,从而实现更精细化的权限管理。
核心要点:
current_user_can()
:检查用户是否拥有指定权限。map_meta_cap
:将元能力映射到角色权限。$capability_type
:定义自定义文章类型的权限前缀。$args
:传递给map_meta_cap
的额外参数,通常是文章 ID。
掌握了 current_user_can()
和 map_meta_cap
,你就能更好地理解 WordPress 的权限系统,并能根据自己的需求,定制出更安全、更灵活的网站。
好了,今天的讲解就到这里,希望对大家有所帮助! 如果有什么疑问,欢迎提问。 拜拜!