深入理解 `WP_Roles` 类的源码,解释它如何存储和管理 WordPress 的所有角色和权限。

各位观众老爷,大家好!今天咱们来聊聊 WordPress 权限管理的核心——WP_Roles 类。这玩意儿就像 WordPress 王国里的“户籍管理处”,负责记录每个“公民”(用户角色)的身份信息(权限)。听起来枯燥?别怕,咱用代码和段子,保证让你听得津津有味。

一、 WP_Roles 类的基本结构

首先,让我们看看 WP_Roles 类的真面目(简化版):

<?php

class WP_Roles {

    /**
     * Array of role objects.
     *
     * @var array
     */
    public $roles = array();

    /**
     * Array of role names.
     *
     * @var array
     */
    public $role_names = array();

    /**
     * Option name for storing role list.
     *
     * @var string
     */
    public $role_key;

    /**
     * Database object.
     *
     * @var wpdb
     */
    public $db;

    /**
     * Constructor - Registers the role management filters.
     *
     * The roles option is also updated on each instantiation, in case another
     * process updated the role list.
     *
     * @global wpdb $wpdb WordPress database abstraction object.
     */
    public function __construct() {
        global $wpdb;

        $this->db = $wpdb;
        $this->role_key = $this->db->prefix . 'user_roles';

        $this->roles = get_option( $this->role_key );
        $this->role_names = array();

        if ( is_array( $this->roles ) ) {
            foreach ( $this->roles as $role => $data ) {
                $this->role_names[ $role ] = $data['name'];
            }
        }

        if ( empty( $this->roles ) ) {
            $this->roles = array();
            $this->role_names = array();
        }

        // add_filter( 'editable_roles', array( $this, 'editable_roles' ) ); // Removed for brevity
        // add_filter( 'map_meta_cap', array( $this, 'map_meta_cap' ), 10, 4 ); // Removed for brevity
    }

    /**
     * Add a new role.
     *
     * @param string $role User role.
     * @param string $display_name Display name of role.
     * @param array  $capabilities List of capabilities keyed by the capability name.
     */
    public function add_role( $role, $display_name, $capabilities = array() ) {
        if ( isset( $this->roles[ $role ] ) ) {
            return;
        }

        $this->roles[ $role ] = array(
            'name'         => $display_name,
            'capabilities' => $capabilities,
        );
        $this->role_names[ $role ] = $display_name;
        $this->update_roles();
        /**
         * Fires after a role is added.
         *
         * @since 2.0.0
         *
         * @param string $role User role.
         */
        do_action( 'add_role', $role );

        return $role;
    }

    /**
     * Remove an existing role.
     *
     * @param string $role Role name.
     */
    public function remove_role( $role ) {
        if ( ! isset( $this->roles[ $role ] ) ) {
            return;
        }

        unset( $this->roles[ $role ] );
        unset( $this->role_names[ $role ] );
        $this->update_roles();

        /**
         * Fires after a role is removed.
         *
         * @since 2.0.0
         *
         * @param string $role User role.
         */
        do_action( 'remove_role', $role );
    }

    /**
     * Retrieve a role object.
     *
     * @param string $role Role name.
     * @return WP_Role|null WP_Role object if found, null if not.
     */
    public function get_role( $role ) {
        if ( isset( $this->roles[ $role ] ) ) {
            return new WP_Role( $this->roles[ $role ]['capabilities'] );
        }
        return null;
    }

    /**
     * Retrieve list of role names with display names.
     *
     * @return array List of role names with display names.
     */
    public function get_names() {
        return $this->role_names;
    }

    /**
     * Whether the role exists.
     *
     * @param string $role Role name.
     * @return bool
     */
    public function is_role( $role ) {
        return isset( $this->roles[ $role ] );
    }

    /**
     * Add capability to role.
     *
     * @param string $role       Role name.
     * @param string $cap        Capability name.
     * @param bool   $grant      Optional, whether to grant or deny capability. Defaults to true.
     */
    public function add_cap( $role, $cap, $grant = true ) {
        if ( ! isset( $this->roles[ $role ] ) ) {
            return;
        }

        $this->roles[ $role ]['capabilities'][ $cap ] = $grant;
        $this->update_roles();
    }

    /**
     * Remove capability from role.
     *
     * @param string $role Capability name.
     * @param string $cap  Capability to remove.
     */
    public function remove_cap( $role, $cap ) {
        if ( ! isset( $this->roles[ $role ] ) ) {
            return;
        }

        unset( $this->roles[ $role ]['capabilities'][ $cap ] );
        $this->update_roles();
    }

    /**
     * Update the role list option.
     *
     * @since 2.0.0
     */
    public function update_roles() {
        update_option( $this->role_key, $this->roles );
    }
}

简单来说,WP_Roles 类主要有以下几个关键成员:

  • $roles: 一个数组,存储了所有角色的信息。每个角色都是这个数组的一个元素,以角色名(例如 ‘administrator’, ‘editor’)作为键。每个角色元素的值又是一个数组,包含角色的显示名称(name)和权限列表(capabilities)。
  • $role_names: 一个数组,以角色名作为键,角色显示名称作为值。相当于一个角色名称的“花名册”。
  • $role_key: 存储角色信息的 WordPress option 的名称。 默认是 {$wpdb->prefix}_user_roles (例如 wp_user_roles)。
  • $db: WordPress 数据库对象,用于访问数据库。

二、 角色信息的存储方式

WP_Roles 类从 WordPress 的 options 表中读取和存储角色信息。具体来说,它使用 get_option()update_option() 函数来操作名为 $role_key 的 option。 默认是 {$wpdb->prefix}_user_roles (例如 wp_user_roles)。

这个 option 的值是一个序列化的 PHP 数组,包含了所有角色的信息。 这种序列化的方式,可以方便地把复杂的数据结构存储到数据库中。

举个栗子:

假设我们有三个角色:’administrator’,’editor’,’subscriber’。存储在 wp_options 表中的 wp_user_roles option 的值可能如下(简化版):

array(
    'administrator' => array(
        'name' => 'Administrator',
        'capabilities' => array(
            'read' => true,
            'edit_posts' => true,
            'delete_posts' => true,
            'manage_options' => true,
        ),
    ),
    'editor' => array(
        'name' => 'Editor',
        'capabilities' => array(
            'read' => true,
            'edit_posts' => true,
            'delete_posts' => false,
        ),
    ),
    'subscriber' => array(
        'name' => 'Subscriber',
        'capabilities' => array(
            'read' => true,
        ),
    ),
)

用表格总结一下:

角色名称 (Role Name) 显示名称 (Display Name) 权限列表 (Capabilities)
administrator Administrator read, edit_posts, delete_posts, manage_options, …
editor Editor read, edit_posts, …
subscriber Subscriber read

三、 WP_Roles 类的主要方法

WP_Roles 类提供了一些关键的方法,用于管理角色和权限。

  1. __construct() (构造函数):

    • 这个函数在 WP_Roles 类被实例化时调用。
    • 它主要负责从数据库中加载角色信息,并将其存储到 $roles$role_names 数组中。
    • 如果数据库中没有角色信息,它会初始化这两个数组为空。
  2. add_role( $role, $display_name, $capabilities = array() ):

    • 用于添加一个新的角色。
    • 参数:
      • $role: 角色的名称(例如 ‘author’)。
      • $display_name: 角色的显示名称(例如 ‘Author’)。
      • $capabilities: 一个数组,包含了该角色拥有的权限。
    • 该方法会将新的角色信息添加到 $roles$role_names 数组中,并更新数据库中的 wp_user_roles option。
    • 会触发 add_role action hook。

    示例代码:

    global $wp_roles;
    if ( ! isset( $wp_roles ) ) {
        $wp_roles = new WP_Roles();
    }
    $wp_roles->add_role( 'contributor', 'Contributor', array( 'read' => true, 'edit_posts' => false ) );

    这段代码添加了一个名为 ‘contributor’ 的角色,显示名称为 ‘Contributor’,拥有 read 权限,但不能编辑文章。

  3. remove_role( $role ):

    • 用于删除一个已存在的角色。
    • 参数:
      • $role: 要删除的角色的名称。
    • 该方法会将角色信息从 $roles$role_names 数组中移除,并更新数据库中的 wp_user_roles option。
    • 会触发 remove_role action hook。

    示例代码:

    global $wp_roles;
    if ( ! isset( $wp_roles ) ) {
        $wp_roles = new WP_Roles();
    }
    $wp_roles->remove_role( 'contributor' );

    这段代码删除了名为 ‘contributor’ 的角色。

  4. get_role( $role ):

    • 用于获取一个角色的信息。
    • 参数:
      • $role: 要获取的角色的名称。
    • 该方法会返回一个 WP_Role 对象,包含了该角色的权限信息。如果角色不存在,则返回 null

    示例代码:

    global $wp_roles;
    if ( ! isset( $wp_roles ) ) {
        $wp_roles = new WP_Roles();
    }
    $role = $wp_roles->get_role( 'editor' );
    if ( $role ) {
        echo "Editor 的权限:n";
        print_r( $role->capabilities );
    } else {
        echo "角色不存在。n";
    }

    这段代码获取了 ‘editor’ 角色的信息,并打印了它的权限列表。 这里要注意,返回的是 WP_Role 对象,不是 $roles 数组里存的数组。

  5. add_cap( $role, $cap, $grant = true ):

    • 用于给一个角色添加权限。
    • 参数:
      • $role: 要添加权限的角色的名称。
      • $cap: 要添加的权限的名称(例如 ‘edit_others_posts’)。
      • $grant: 一个布尔值,表示是否授予该权限。默认为 true
    • 该方法会将新的权限添加到 $roles 数组中,并更新数据库中的 wp_user_roles option。

    示例代码:

    global $wp_roles;
    if ( ! isset( $wp_roles ) ) {
        $wp_roles = new WP_Roles();
    }
    $wp_roles->add_cap( 'editor', 'edit_others_posts' );

    这段代码给 ‘editor’ 角色添加了 ‘edit_others_posts’ 权限,使其能够编辑其他用户的文章。

  6. remove_cap( $role, $cap ):

    • 用于从一个角色中移除权限。
    • 参数:
      • $role: 要移除权限的角色的名称。
      • $cap: 要移除的权限的名称。
    • 该方法会将权限从 $roles 数组中移除,并更新数据库中的 wp_user_roles option。

    示例代码:

    global $wp_roles;
    if ( ! isset( $wp_roles ) ) {
        $wp_roles = new WP_Roles();
    }
    $wp_roles->remove_cap( 'editor', 'edit_others_posts' );

    这段代码从 ‘editor’ 角色中移除了 ‘edit_others_posts’ 权限。

  7. get_names():

    • 返回一个包含所有角色名称及其显示名称的数组。
  8. is_role( $role ):

    • 检查一个角色是否存在。
  9. update_roles():

    • $roles 数组的内容更新到数据库的 wp_user_roles option 中。这个方法通常在添加、删除角色或修改角色权限后调用。

四、 WP_Role 类(注意不是 WP_Roles!)

上面我们提到了 get_role() 方法返回的是一个 WP_Role 对象。 WP_Role 类是 WP_Roles 类的“助手”,它代表一个单独的角色,主要负责管理该角色的权限。

<?php

/**
 * Core class used to implement user roles.
 *
 * @since 2.0.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 */
class WP_Role {

    /**
     * List of capabilities the role has.
     *
     * @since 2.0.0
     * @var array
     */
    public $capabilities;

    /**
     * Constructor - Set up object properties.
     *
     * The list of capabilities, `$capabilities`, is passed to the object.
     *
     * @since 2.0.0
     *
     * @param array $capabilities List of capabilities keyed by the capability name.
     */
    public function __construct( $capabilities = array() ) {
        $this->capabilities = $capabilities;
    }

    /**
     * Assign role name.
     *
     * @since 2.0.0
     *
     * @param string $role
     */
    public function set_name( $role ) {
        $this->name = $role;
    }

    /**
     * Check if the role has the given capability.
     *
     * @since 2.0.0
     *
     * @param string $cap Capability name.
     * @return bool True if the role has the capability, false if not.
     */
    public function has_cap( $cap ) {
        if ( isset( $this->capabilities[ $cap ] ) ) {
            return $this->capabilities[ $cap ];
        } else {
            return false;
        }
    }
}

WP_Role 类的主要成员是 $capabilities 数组,它存储了该角色拥有的权限。 has_cap() 方法用于检查角色是否拥有某个权限。

示例代码:

global $wp_roles;
if ( ! isset( $wp_roles ) ) {
    $wp_roles = new WP_Roles();
}
$role = $wp_roles->get_role( 'editor' );
if ( $role && $role->has_cap( 'edit_posts' ) ) {
    echo "Editor 可以编辑文章。n";
} else {
    echo "Editor 不能编辑文章。n";
}

五、 权限映射 (Capability Mapping)

WordPress 的权限系统不仅仅是简单的角色和权限的对应关系。 它还涉及到权限映射,即将一些“虚拟”的权限映射到实际的权限上。

例如,edit_posts 权限可能被映射到 edit_published_postsedit_private_posts 等更细粒度的权限上。 这个映射过程是由 map_meta_cap filter hook 来实现的。

map_meta_cap filter hook 允许你根据不同的情况,动态地修改用户所需的权限。 这使得 WordPress 的权限系统更加灵活和强大。

举个例子:

假设你想让某个用户只能编辑自己创建的文章,而不能编辑其他用户的文章。 你可以使用 map_meta_cap filter hook 来实现这个功能:

function my_map_meta_cap( $caps, $cap, $user_id, $args ) {
    if ( 'edit_post' == $cap ) {
        $post = get_post( $args[0] );
        if ( $post && $user_id != $post->post_author ) {
            $caps[] = 'do_not_allow'; // 添加一个不存在的权限,阻止编辑
        }
    }
    return $caps;
}
add_filter( 'map_meta_cap', 'my_map_meta_cap', 10, 4 );

这段代码拦截了 edit_post 权限的检查,如果用户不是文章的作者,就添加一个名为 do_not_allow 的不存在的权限,从而阻止用户编辑该文章。

六、 WP_Roles 类的应用场景

WP_Roles 类在 WordPress 中被广泛使用,主要用于以下几个方面:

  • 用户权限管理: 控制用户可以访问哪些后台功能,以及可以执行哪些操作。
  • 主题和插件开发: 根据用户的角色,显示不同的内容或提供不同的功能。
  • 会员系统: 创建自定义的角色,并根据用户的会员等级,授予不同的权限。

七、 总结

WP_Roles 类是 WordPress 权限管理的核心,它负责存储和管理所有角色和权限的信息。 通过 WP_Roles 类,你可以方便地添加、删除、修改角色和权限,从而实现灵活的用户权限控制。 理解 WP_Roles 类的工作原理,对于开发 WordPress 主题和插件,以及构建复杂的会员系统至关重要。

希望今天的讲座对大家有所帮助。 记住,理解源码是成为 WordPress 大神的必经之路。 下次见!

发表回复

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