User management on WordPress is based on roles and capabilities.
A role is an entity made of a unique name and a set of capabilities. Each capability is used to define if the role has access to a particular feature of the platform.
Let’s take a closer look at how WordPress Roles and Capabilities work.
Key Takeaways
- User management in WordPress is based on roles and capabilities. A role is a unique name with a set of capabilities, which define the access level of the role to different features of the platform.
- WordPress provides a comprehensive API to customize roles and their capabilities. Functions such as add_role(), remove_role(), add_cap(), and remove_cap() can be used to manage roles and capabilities. However, care must be taken with database access and performance when using these functions.
- There are workarounds to avoid database issues when working with roles and capabilities. One option is to trigger code only when a plugin is enabled using the register_activation_hook() function. Another method is to bypass the WordPress database by setting the $wp_user_roles global variable.
Under the Hood
Storing the Roles
The list of default roles and capabilities is available on the WordPress Codex.
The database stores this list in the wp_options
table.
It uses the serialized wp_user_roles
key.
The unserialized data looks like this:
array(
'administrator' => array(
'name' => 'Administrator',
'capabilities' => array(
'switch_themes' => true,
'edit_themes' => true,
'activate_plugins' => true,
'edit_plugins' => true,
'edit_users' => true,
// [...]
)
),
'contributor' => array(
'name' => 'Contributor',
'capabilities' => array(
'delete_pages' => true,
'delete_others_pages' => true,
'delete_published_pages' => true,
'delete_posts' => true,
// [...]
)
),
// [...]
);
This meta is automatically set when installing a new WordPress site.
When WordPress starts, the WP_Roles
class loads the list from the database.
This occurs between the plugins_loaded
and the init
hooks.
Linking Users to Roles
WordPress uses a meta_key
, stored in the wp_usermeta
table, to link a user to his role.
Once unserialized, the meta looks like this:
array(
'administrator' => true
)
Note that WordPress uses an array, although a user can only have one role at a time, we will see later why.
Also, keep in mind that the wp_
part of the meta key is the prefix of the current blog.
(We can get it by using the $GLOBALS['wpdb']->get_blog_prefix()
function).
On a multisite installation, this would allow a user to use different roles on different instances:
wp_capabilities => a:1:{s:13:"administrator";b:1;}
wp_10_capabilities => a:1:{s:11:"contributor";b:1;}
wp_15_capabilities => a:1:{s:10:"subscriber";b:1;}
[...]
This rule also applies to the wp_user_roles
entry that we have seen before, in the wp_options
table.
Finally, we can see the wp_user_level
meta along with the role.
It was used to handle roles in old WordPress versions, and is now deprecated.
Working with Capabilities in the Core
We have seen how roles are loaded and linked to users; from there, WordPress is able to get the capabilities of a given user, when needed.
Several default capabilities are hard-coded in the WordPress core.
For instance, when loading the plugin screen, it will check if the current user can manage plugins, by running the following code:
if (!current_user_can('activate_plugins'))
{
wp_die(__('You do not have sufficient permissions to manage plugins for this site.'));
}
Roles are never hard-coded; a role is only a capabilities wrapper, it only exists in the database.
Working with Roles & Capabilities: the WordPress API
Access the API
WordPress provides the following global functions to help us work with roles.
current_user_can()
Checks if the current user owns the required capability.
add_action('init', function()
{
if (current_user_can('install_plugins'))
{
echo 'you can install plugins';
}
else
{
echo 'You cannot install plugins';
}
});
WP_User::has_cap
Checks if a specific user owns a capability.
add_action('init', function()
{
$user = get_user_by('slug', 'admin');
if ($user->has_cap('install_plugins'))
{
echo 'Admin can install plugins';
}
else
{
echo 'Admin cannot install plugins';
}
});
We can note that current_user_can
uses this function.
get_editable_roles()
Returns available roles.
add_action('admin_init', function()
{
$roles = get_editable_roles();
var_dump($roles);
});
The list may be overriden with the editable_roles
filter, so we should not rely on this function to get the complete roles list on a website.
Note the usage of the admin_init
hook, as the function is not loaded yet on the init
one.
get_role()
Gets a WP_Role
object from its slug.
add_action('init', function()
{
$role = get_role('administrator');
var_dump($role);
});
// This will print:
// WP_Role Object
// (
// [name] => administrator
// [capabilities] => Array
// (
// [switch_themes] => 1
// [edit_themes] => 1
// [activate_plugins] => 1
// [edit_plugins] => 1
// [...]
WP_Role::has_cap()
Checks if a role has the required capability.
add_action('init', function()
{
$role = get_role('administrator');
var_dump($role->has_cap('install_plugins')); // Prints TRUE
});
Customizing the API
WordPress also offers a complete API to customize the roles and their capabilities.
add_role()
Registers a new role in the database.
add_action('init', function()
{
add_role('plugins_manager', 'Plugins Manager', array(
'install_plugins',
'activate_plugins',
'edit_plugins'
));
});
remove_role()
Removes the required role from the database, if it exists.
add_action('init', function()
{
remove_role('plugins_manager');
});
WP_Role::add_cap()
Adds a capability to a role.
add_action('init', function()
{
$role = get_role('contributor');
$role->add_cap('install_plugins');
});
This may be a core capability (install_plugins
, edit_posts
, …) or any custom string (my_awesome_plugin_cap
).
It allows us to register as many custom capabilities as we need for our plugins.
WP_Role::remove_cap()
Removes a capability from a role, if it exists.
add_action('init', function()
{
$role = get_role('contributor');
$role->remove_cap('install_plugins');
});
WP_User::add_role()
Adds a role to the given user.
add_action('init', function()
{
$user = get_user_by('slug', 'admin');
$user->add_role('contributor');
});
This function allows you to theoretically set many roles on the same user.
As the WordPress backend only displays and manages one role per user, we should not add several roles for a user, and always use WP_User::remove_role()
before adding a new one.
WP_User::remove_role()
Removes a role from the given user.
add_action('init', function()
{
$user = get_user_by('slug', 'admin');
$user->remove_role('administrator');
});
WP_User::add_cap()
Adds a capability to the given user.
add_action('init', function()
{
$user = get_user_by('slug', 'admin');
$user->add_cap('my_custom_cap');
});
This can be useful if we want to add a single capability to a user, without having to create a complete role.
WP_User::remove_cap()
Removes a capability from the given user.
add_action('init', function()
{
$user = get_user_by('slug', 'admin');
$user->remove_cap('my_custom_cap');
});
A Few Issues with the WordPress API
Everything looks fine with the functions we have seen, except one thing: database access and performance.
The main concern we have when working with roles and capabilities is when should we trigger our code?
To explain this, let’s have a look at the code of the WordPress core.
First, we want to add a new, empty role:
add_action('init', function()
{
add_role('plugins_manager', 'Plugins Manager', array());
});
Here are the first lines of the add_role
function (which actually redirects to WP_Roles::add_role
):
public function add_role( $role, $display_name, $capabilities = array() ) {
if ( isset( $this->roles[$role] ) )
return;
If we add a new role, the add_role
function runs once, and then does nothing.
Next, let’s say we want to add a capability to our freshly created role:
add_action('init', function()
{
$role = get_role('plugins_manager');
$role->add_cap('install_plugins');
});
The WP_Role::add_cap()
function in WordPress 4.2.2 looks like this:
public function add_cap( $role, $cap, $grant = true ) {
if ( ! isset( $this->roles[$role] ) )
return;
$this->roles[$role]['capabilities'][$cap] = $grant;
if ( $this->use_db )
update_option( $this->role_key, $this->roles );
}
It updates the $this->roles
object, but we can also see that the database will be updated each time our code runs, even if our new capability has already been registered!
This means that if we care about performance, all the code we produce to customize roles and capabilities should not run on each page load.
Workarounds
There are several options to avoid these database problems.
Working with Plugin Activation
WordPress allows plugin authors to trigger code when a plugin is enabled from the backend, by using the register_activation_hook()
function.
Let’s create a sample plugin:
/*
Plugin Name: Our sample role plugin
*/
register_activation_hook(__FILE__, function()
{
$role = add_role('plugins_manager', 'Plugins Manager', array());
$role->add_cap('install_plugins');
});
This code would run only once, when enabling the plugin on the website.
Now, we have to keep in mind that this solution depends on plugin activation and deactivation.
What would occur if the plugin is already in production, or if its reactivation is omitted when pushing the update?
In fact, this solution depends on the database too, and requires an extra step when pushing code.
Bypassing the WordPress Database
There is a second, undocumented solution, that can work well in some cases.
Let’s have a last look at the WordPress core, when the WP_Roles
object loads the roles from the database on WordPress boot:
protected function _init() {
global $wpdb, $wp_user_roles;
$this->role_key = $wpdb->get_blog_prefix() . 'user_roles';
if ( ! empty( $wp_user_roles ) ) {
$this->roles = $wp_user_roles;
$this->use_db = false;
} else {
$this->roles = get_option( $this->role_key );
}
Before getting the data from the database, WordPress checks for the $wp_user_roles
global variable.
If set, WordPress will use its content, and block database usage by setting the $this->use_db
variable to false
.
Let’s try this, by keeping only a new, restricted administrator
role:
/*
Plugin Name: Our sample role plugin
*/
$GLOBALS['wp_user_roles'] = array(
'administrator' => array(
'name' => 'Administrator',
'capabilities' => array(
'activate_plugins' => true,
'read' => true,
)
)
);
When loading the backend, we can see it has kept the definition of our custom role:
This solution solves the database problem, but may introduce some others:
- Plugins which use the native API may not behave correctly.
- We have to manually set the definition of each role, even the ones we don’t want to change.
However, when building a custom WordPress application that needs a custom, static list of roles, this could be a possible solution:
- The roles definition can be versioned with the code.
- Pushing new code on an environment will automatically update the definition.
- No more questions about the database.
Conclusion
In this article, I’ve presented an overview of roles and capabilities usage in WordPress.
Although its complete API allows us to do almost whatever we want, the relation to the database remains the main concern.
We’ll have to keep this in mind when developing our plugins and themes.
What do you think about the way WordPress manages roles? I look forward to your feedback!
Frequently Asked Questions on Mastering WordPress Roles and Capabilities
What are the default user roles in WordPress and what are their capabilities?
WordPress comes with six default user roles: Super Admin, Administrator, Editor, Author, Contributor, and Subscriber. Each role has a specific set of capabilities. For instance, a Super Admin has access to all administrative features across multiple sites. An Administrator can perform all administrative tasks on a single site. Editors can publish and manage posts, including those of other users. Authors can publish and manage their own posts. Contributors can write and manage their own posts but cannot publish them. Subscribers can only manage their profile.
How can I add a new user role in WordPress?
To add a new user role in WordPress, you can use the add_role() function. This function accepts three parameters: the role, the display name, and an array of capabilities. For example, to add a new role called ‘custom_role’ with the capability to read and edit posts, you would use the following code:add_role(
'custom_role',
__( 'Custom Role' ),
array(
'read' => true, // Can read posts
'edit_posts' => true, // Can edit posts
)
);
How can I remove a user role in WordPress?
To remove a user role in WordPress, you can use the remove_role() function. This function accepts one parameter: the role. For example, to remove the ‘custom_role’ you added earlier, you would use the following code:remove_role( 'custom_role' );
How can I add capabilities to a user role in WordPress?
To add capabilities to a user role in WordPress, you can use the add_cap() function. This function accepts two parameters: the capability and a boolean indicating whether the role has the capability. For example, to add the ‘publish_posts’ capability to the ‘custom_role’, you would use the following code:$role = get_role( 'custom_role' );
$role->add_cap( 'publish_posts', true );
How can I remove capabilities from a user role in WordPress?
To remove capabilities from a user role in WordPress, you can use the remove_cap() function. This function accepts one parameter: the capability. For example, to remove the ‘publish_posts’ capability from the ‘custom_role’, you would use the following code:$role = get_role( 'custom_role' );
$role->remove_cap( 'publish_posts' );
How can I change the default user role in WordPress?
To change the default user role in WordPress, navigate to Settings > General in your WordPress dashboard. Under ‘New User Default Role’, select the role you want to set as the default from the drop-down menu.
How can I assign multiple roles to a user in WordPress?
WordPress does not support assigning multiple roles to a user by default. However, you can use a plugin like Multiple Roles to achieve this. Once the plugin is installed and activated, you can assign multiple roles to a user from the user’s profile page.
How can I restrict content access based on user roles in WordPress?
To restrict content access based on user roles in WordPress, you can use a plugin like Members. This plugin allows you to control which roles can access specific content on your site.
How can I create a custom capability in WordPress?
To create a custom capability in WordPress, you can use the add_cap() function. This function accepts two parameters: the capability and a boolean indicating whether the role has the capability. For example, to add a custom capability called ‘manage_custom’ to the ‘custom_role’, you would use the following code:$role = get_role( 'custom_role' );
$role->add_cap( 'manage_custom', true );
How can I check if a user has a specific capability in WordPress?
To check if a user has a specific capability in WordPress, you can use the current_user_can() function. This function accepts one parameter: the capability. For example, to check if the current user has the ‘manage_custom’ capability, you would use the following code:if ( current_user_can( 'manage_custom' ) ) {
// The current user has the 'manage_custom' capability
}
A web developer with a passion for WordPress, JavaScript, pragmatic coding and polished hand-crafted projects.