Answering my own question, if your system is not performance-critical, you can use a plugin for it, such as User Role Editor:
It supports multi-site, multiple roles per user and a lot more.
Optionally, If I were to write it:
If I would like to have total control over it and write my custom code, I would handle roles management in a plugin, to keep it organized and logically separated from the rest of the code.
The main advantage of this structure right now is to give a dedicated folder to Roles, keeping it organized, and providing a basic OOP structure to work with. This could be extended with methods to help assigning custom post types capabilities for instance, etc.
plugins/custom-roles-manager/custom-roles-manager.php
<?php
/*
* Plugin Name: Custom Roles Manager
* Description: This plugin handles roles capabilities.
* Requires PHP: 5.3
*/
defined('ABSPATH') OR exit;
/** Runs only when the plugin is activated */
register_activation_hook(__FILE__, 'custom_roles_manager_activate');
function custom_roles_manager_activate()
{
require_once(__DIR__ . '/RoleInterface.php');
// This is the folder we add roles on
$roles_folder = __DIR__ . '/roles';
// If it throws anything, WordPress will automatically disable the plugin and show the notice
$roles = crm_get_roles_in_folder($roles_folder);
// Give developer a chance to filter the roles for any reason
$roles = apply_filters('crm_get_roles', $roles);
foreach ($roles as $role) {
// Filter might add $role that does not implement RoleInterface
if ($role instanceof RoleInterface) {
$role->add();
} else {
throw new Exception('All roles handled by Custom Roles Manager must implement RoleInterface interface.');
}
}
}
/**
* Returns an array of concrete classes that implements RoleInterface
* in a given directory
*
* @param $dir
*
* @return array
* @throws ReflectionException
*/
function crm_get_roles_in_folder($dir)
{
$roles = array();
$roles_files = new RegexIterator(
new DirectoryIterator($dir),
'/\.php$/',
RegexIterator::MATCH
);
foreach ($roles_files as $role_file) {
include_once($role_file->getPathname());
$r = new ReflectionClass($role_file->getBasename('.php'));
if ($r->implementsInterface(RoleInterface::class)) {
$roles[] = $r->newInstance();
}
}
return $roles;
}
plugins/custom-roles-manager/RoleInterface.php
<?php
interface RoleInterface
{
/**
* Function that has to be implemented for
* each concrete class to actually add the role.
*/
public function add();
}
plugins/custom-roles-manager/roles/PublicRelationsRole.php
<?php
class PublicRelationsRole implements RoleInterface
{
public function add()
{
add_role(
'public_relations',
__('Public Relations'),
[
'read',
'edit_post',
'publish_post',
'delete_post',
]
);
}
}
Example scenario: Creating "Some Other Role"
- Create file wp-content/plugins/custom-roles-manager/Roles/SomeOtherRole.php
- Make it implement
RoleInterface
and create add
method in it, which calls add_role
function from WordPress.
- Deactivate and activate plugin.
With time, your complex role system will have a dedicated folder for them, helping keep it organized, using OOP, which gives a few options on code re-use over time.