详解 WordPress `current_user_can()` 函数源码：如何通过 `map_meta_cap` 过滤器实现权限映射。

admin
Fri, 08 Aug 2025 09:52:23 +0000
PHP, current, wordpress, 源码

各位观众老爷，晚上好！我是今晚的讲解员，江湖人称“代码搬运工”，今天给大家伙儿唠唠 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. Additio …

继续阅读“详解 WordPress `current_user_can()` 函数源码：如何通过 `map_meta_cap` 过滤器实现权限映射。”

The post 详解 WordPress `current_user_can()` 函数源码：如何通过 `map_meta_cap` 过滤器实现权限映射。 first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

各位观众老爷，晚上好！我是今晚的讲解员，江湖人称“代码搬运工”，今天给大家伙儿唠唠 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 就是额外的参数。

工作流程：

1. 参数准备： 将 capability 和 args 合并成一个数组。

2. user_has_cap 过滤器： 应用 user_has_cap 过滤器，允许修改要检查的 capability。

3. 角色检查： 获取当前用户的角色，并遍历这些角色，检查这些角色是否直接拥有 $capability 权限。

4. 多站点检查（如果是多站点）： 检查用户是否在当前站点拥有 $capability 权限。

5. 超级管理员检查： 如果用户是超级管理员，直接返回 true。

6. 返回结果： 如果以上都没有找到 $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。

工作流程：

1. current_user_can() 调用 apply_filters('map_meta_cap', $caps, $capability, $user_id, $args)。

2. 注册到 map_meta_cap 过滤器的函数会被依次执行。

3. 这些函数根据 $cap 和 $args 的值，判断用户是否应该拥有该权限。

4. 如果用户应该拥有该权限，则修改 $caps 数组，将其替换为实际的角色能力。

5. 如果用户不应该拥有该权限，则 $caps 数组保持不变。

6. 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;
}
?>

代码解释：

1. 注册过滤器： 使用 add_filter('map_meta_cap', 'my_map_meta_cap', 10, 4) 注册 my_map_meta_cap 函数到 map_meta_cap 过滤器。

2. 判断 $cap： 首先判断 $cap 是否为 'edit_post'，如果不是，则直接返回 $caps，不做任何修改。

3. 获取文章信息： 从 $args 中获取文章 ID，并使用 get_post() 函数获取文章对象。如果文章不存在，则返回 array('do_not_allow')，表示不允许该用户编辑该文章。

4. 判断用户是否为作者： 判断当前用户是否为文章的作者。

如果是作者，则根据文章的状态（已发布或未发布），返回 'edit_published_posts' 或 'edit_posts' 权限。

如果不是作者，则返回 'edit_others_posts' 权限。

5. 返回 $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;
}
?>

代码解释：

1. 注册自定义文章类型： 使用 register_post_type() 函数注册 book 自定义文章类型。 关键在于设置了 'capability_type' => 'book' 和 'map_meta_cap' => true。capability_type 定义了权限的前缀，map_meta_cap 启用了权限映射。

2. 添加权限： 为管理员角色添加 'manage_books' 权限。

3. 权限映射： 使用 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 的权限系统，并能根据自己的需求，定制出更安全、更灵活的网站。

好了，今天的讲解就到这里，希望对大家有所帮助！ 如果有什么疑问，欢迎提问。 拜拜！