阐述 WordPress `current_user_can()` 函数的源码:它如何通过 `map_meta_cap` 过滤器将元权限映射到具体权限。

各位观众老爷,晚上好!我是你们的老朋友,今天咱们来聊聊WordPress权限管理中的一个核心函数:current_user_can()。 这玩意儿,看着简单,但背后可藏着不少门道。 咱们不仅要知其然,还要知其所以然,彻底搞清楚它内部的运作机制。

一、current_user_can():一个权限守门员

current_user_can(),顾名思义,就是用来判断当前用户是否有执行某个操作的权限。 比如,你想看看当前用户能不能编辑文章,就可以这么写:

if ( current_user_can( 'edit_posts' ) ) {
  echo '用户可以编辑文章!';
} else {
  echo '用户没有编辑文章的权限!';
}

这个函数接收一个参数,就是你要检查的权限名(capability)。 如果用户有这个权限,就返回 true,否则返回 false

但是!事情并没有这么简单。 WordPress的权限系统非常灵活,它引入了“元权限”(meta capability)的概念。 所谓元权限,就是一种抽象的权限,它并不直接对应到用户的具体角色或者权限上。 比如,edit_post 就是一个元权限,它表示“编辑某个文章”的权限。

问题来了: 谁有权编辑哪个文章呢? 这就需要 map_meta_cap 过滤器来帮忙了。

二、map_meta_cap:权限映射的魔法师

map_meta_cap 过滤器,顾名思义,就是用来映射元权限的。 它的作用是将一个元权限,根据上下文(比如用户ID、文章ID等),映射到一个或多个具体的权限上。

我们先来看看current_user_can()函数的简化版源码(为了方便理解,我去掉了一些不相关的代码):

function current_user_can( $capability ) {
  $args = array_slice( func_get_args(), 1 ); // 获取除了capability之外的所有参数
  return _wp_cap_granted_through_role( $capability, get_current_user_id(), $args );
}

function _wp_cap_granted_through_role( $cap, $user_id, $args ) {
  $user = new WP_User( $user_id );

  // 1. 获取用户的所有权限
  $caps = apply_filters( 'user_has_cap', $user->allcaps, $caps, $args, $user );

  // 2. 检查用户是否拥有该权限
  if ( ! empty( $caps[ $cap ] ) ) {
    return true;
  }

  // 3. 如果没有直接权限,则进行元权限映射
  $primitive_caps = map_meta_cap( $cap, $user_id, $args );

  // 4. 检查映射后的权限
  foreach ( (array) $primitive_caps as $primitive_cap ) {
    if ( ! empty( $caps[ $primitive_cap ] ) ) {
      return true;
    }
  }

  return false;
}

这段代码的流程是这样的:

  1. 获取用户的所有权限 (user_has_cap 过滤器): 首先,它会获取当前用户的所有权限。 这些权限通常是根据用户的角色来决定的。 这里使用了 user_has_cap 过滤器,允许开发者自定义用户的权限。
  2. 检查用户是否拥有该权限: 如果用户直接拥有要检查的权限,那么就直接返回 true
  3. 进行元权限映射 (map_meta_cap 过滤器): 如果用户没有直接拥有要检查的权限,那么就调用 map_meta_cap() 函数,将元权限映射到一个或多个具体的权限。
  4. 检查映射后的权限: 最后,它会检查用户是否拥有映射后的权限。 如果用户拥有其中任何一个映射后的权限,就返回 true

重点来了,我们看看map_meta_cap()函数的源码(同样是简化版):

function map_meta_cap( $cap, $user_id, $args ) {
  $caps = array();

  // 调用 'map_meta_cap' 过滤器
  $caps = apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args );

  return $caps;
}

看到没? map_meta_cap() 函数本身只是一个空壳子, 真正的权限映射逻辑,全部都在 map_meta_cap 过滤器里面。

三、map_meta_cap 过滤器:权限映射的幕后英雄

map_meta_cap 过滤器接收四个参数:

  • $caps: 一个数组,用于存储映射后的权限。 初始值是一个空数组。
  • $cap: 要映射的元权限。
  • $user_id: 用户的ID。
  • $args: 一个数组,包含额外的参数,比如文章ID、分类ID等。

WordPress核心代码中,已经预定义了很多 map_meta_cap 过滤器的回调函数,用于处理各种常见的元权限。 比如,处理 edit_post 元权限的函数是 _map_edit_cap()

function _map_edit_cap( $caps, $cap, $user_id, $args ) {
  if ( 'edit_post' == $cap ) {
    $post = get_post( $args[0] ); // 获取文章对象

    if ( empty( $post ) ) {
      $caps[] = 'do_not_allow'; // 文章不存在,禁止操作
    } else {
      if ( $user_id == $post->post_author ) {
        $caps[] = 'edit_posts'; // 如果是作者,拥有编辑文章的权限
      } else {
        $caps[] = 'edit_others_posts'; // 如果不是作者,需要拥有编辑他人文章的权限
      }
    }
  }
  return $caps;
}

这个函数做了什么?

  1. 获取文章对象: 首先,它会根据传入的文章ID ($args[0]),获取文章对象。
  2. 判断文章是否存在: 如果文章不存在,那么就将 $caps 数组设置为 array( 'do_not_allow' ),表示禁止操作。
  3. 判断用户是否是作者: 如果用户是文章的作者,那么就将 $caps 数组设置为 array( 'edit_posts' ),表示用户拥有编辑自己文章的权限。
  4. 如果不是作者: 如果用户不是文章的作者,那么就将 $caps 数组设置为 array( 'edit_others_posts' ),表示用户需要拥有编辑他人文章的权限。

也就是说,edit_post 这个元权限,被映射成了 edit_postsedit_others_posts 权限。

四、自定义 map_meta_cap 过滤器:打造个性化权限系统

WordPress的权限系统非常灵活,你可以通过自定义 map_meta_cap 过滤器,来打造个性化的权限系统。

比如,你想实现一个“编辑特定分类的文章”的权限,可以这么做:

add_filter( 'map_meta_cap', 'my_map_meta_cap', 10, 4 );

function my_map_meta_cap( $caps, $cap, $user_id, $args ) {
  if ( 'edit_category_post' == $cap ) {
    $post_id = $args[0];
    $category_id = $args[1];

    // 获取文章的所有分类
    $post_categories = wp_get_post_categories( $post_id );

    // 检查文章是否属于指定的分类
    if ( in_array( $category_id, $post_categories ) ) {
      $caps[] = 'edit_posts'; // 如果属于该分类,则拥有编辑文章的权限
    } else {
      $caps[] = 'do_not_allow'; // 否则,禁止操作
    }
  }

  return $caps;
}

这段代码做了什么?

  1. 注册 map_meta_cap 过滤器: 首先,它使用 add_filter() 函数,注册了一个名为 my_map_meta_cap 的回调函数,用于处理 map_meta_cap 过滤器。
  2. 判断是否是 edit_category_post 元权限: 在回调函数中,它首先判断要映射的元权限是否是 edit_category_post
  3. 获取文章ID和分类ID: 如果是 edit_category_post 元权限,那么它会从 $args 数组中获取文章ID和分类ID。
  4. 检查文章是否属于指定的分类: 然后,它会使用 wp_get_post_categories() 函数,获取文章的所有分类,并检查文章是否属于指定的分类。
  5. 设置 $caps 数组: 如果文章属于指定的分类,那么就将 $caps 数组设置为 array( 'edit_posts' ),表示用户拥有编辑文章的权限。 否则,就将 $caps 数组设置为 array( 'do_not_allow' ),表示禁止操作。

现在,你就可以使用 current_user_can() 函数来检查用户是否拥有编辑特定分类的文章的权限了:

if ( current_user_can( 'edit_category_post', $post_id, $category_id ) ) {
  echo '用户可以编辑该分类的文章!';
} else {
  echo '用户没有编辑该分类的文章的权限!';
}

五、map_meta_cap 过滤器的优先级

map_meta_cap 过滤器可以注册多个回调函数,它们的执行顺序由优先级决定。 优先级越小,执行顺序越早。 WordPress核心代码中的 map_meta_cap 过滤器的回调函数,优先级通常设置为 10。 因此,如果你想覆盖WordPress核心代码中的权限映射逻辑,可以将你的回调函数的优先级设置为小于 10 的值。

六、案例分析:编辑附件的权限

我们再来分析一个稍微复杂一点的案例:编辑附件的权限 (edit_post,针对 attachment post type)。

WordPress 核心代码中,_map_edit_cap() 函数也会处理 edit_post 元权限,但是它只考虑了文章的作者和 edit_others_posts 权限。 如果你想实现更细粒度的权限控制,比如允许某些用户编辑特定类型的附件,可以这么做:

add_filter( 'map_meta_cap', 'my_map_edit_attachment_cap', 10, 4 );

function my_map_edit_attachment_cap( $caps, $cap, $user_id, $args ) {
  if ( 'edit_post' == $cap ) {
    $post_id = $args[0];
    $post = get_post( $post_id );

    // 检查是否是附件
    if ( 'attachment' == $post->post_type ) {
      // 获取附件的MIME类型
      $mime_type = get_post_mime_type( $post_id );

      // 允许用户编辑图片类型的附件
      if ( strpos( $mime_type, 'image/' ) !== false ) {
        $caps[] = 'edit_others_posts'; // 需要拥有编辑他人文章的权限
      } else {
        $caps[] = 'do_not_allow'; // 禁止编辑其他类型的附件
      }
    }
  }

  return $caps;
}

这段代码做了什么?

  1. 检查是否是附件: 首先,它会检查文章的类型是否是 attachment
  2. 获取附件的MIME类型: 如果是附件,那么它会使用 get_post_mime_type() 函数,获取附件的MIME类型。
  3. 判断是否是图片类型的附件: 然后,它会判断附件的MIME类型是否包含 image/
  4. 设置 $caps 数组: 如果是图片类型的附件,那么就将 $caps 数组设置为 array( 'edit_others_posts' ),表示用户需要拥有编辑他人文章的权限。 否则,就将 $caps 数组设置为 array( 'do_not_allow' ),表示禁止编辑其他类型的附件。

这样,只有拥有 edit_others_posts 权限的用户,才能编辑图片类型的附件。 其他类型的附件,则不允许编辑。

七、总结:current_user_can()map_meta_cap 的完美配合

current_user_can() 函数是WordPress权限管理的核心入口,它通过 map_meta_cap 过滤器,将元权限映射到具体的权限,实现了灵活的权限控制。

函数/过滤器 作用
current_user_can() 检查当前用户是否拥有指定的权限。
map_meta_cap 将元权限映射到一个或多个具体的权限。
user_has_cap 允许开发者自定义用户的权限。

通过自定义 map_meta_cap 过滤器,你可以打造个性化的权限系统,满足各种复杂的业务需求。

八、一些建议

  • 仔细阅读WordPress官方文档: WordPress官方文档是学习WordPress权限系统的最佳资源。
  • 多看源码: 阅读WordPress核心代码和插件代码,可以帮助你更深入地理解权限系统的运作机制。
  • 善用调试工具: 使用调试工具,可以帮助你跟踪代码的执行流程,找到权限问题的根源。
  • 不要滥用权限: 权限控制应该遵循最小权限原则,只赋予用户必要的权限。

掌握了current_user_can()map_meta_cap 过滤器,你就掌握了WordPress权限系统的钥匙。 祝你在WordPress开发的道路上越走越远!

今天的讲座就到这里,谢谢大家! 如果有什么问题,欢迎在评论区留言。 咱们下期再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注