Search by

pollora / roles

pollora

WordPress roles and capabilities declared with attributes: a #[Role] class, #[ModifyRole] adjustments, post type and taxonomy capabilities, compiled into the roles WordPress stores. Runs with or without Laravel.

v0.1.0 2026-10-15 14:19 UTC

This package is auto-updated.

Last update: 2026-10-15 14:20:30 UTC


README

Pollora Roles: WordPress roles and capabilities declared with attributes

Latest version Total downloads Tests License

WordPress roles and capabilities declared in code: a class with #[Role] is a role, #[ModifyRole] adjusts a role WordPress already has, and the capabilities of your post types and taxonomies are granted by naming them. The package compiles the declarations into the roles WordPress stores and puts them in WP_Roles when it loads, so the code is the source of truth and the database follows.

Part of Pollora, the Laravel framework for WordPress, where it is installed and wired for you: see Roles & capabilities. It runs without Laravel too: in a plugin, a Bedrock site or any PHP project. The Laravel authorization bridge ($user->can(), @can, the role middleware and the @role directive) stays in the framework.

Installation

composer require pollora/roles

Requires PHP 8.3+ and WordPress. It builds on pollora/attributes (the #[Role] family) and pollora/discovery.

Declaring

use Pollora\Attributes\Role;
use Pollora\Attributes\Role\Grants;
use Pollora\Attributes\Role\GrantsPostType;
use Pollora\Attributes\Role\GrantsTaxonomy;
use Pollora\Attributes\Role\Without;
use Pollora\Role\Domain\Enums\Access;

#[Role('event_manager', label: 'Event manager', inherits: 'author')]
#[GrantsPostType(Event::class, Access::Editor)]
#[GrantsTaxonomy(Genre::class)]
#[Grants('export_attendees', 'upload_files')]
#[Without('publish_posts')]
final class EventManager {}

The role starts from the capabilities of the role it inherits, adds what it grants (a post type's or a taxonomy's own capabilities by class or slug, or any capability by name or from a backed enum marked #[CapabilitySet]), and drops what it is Without. Without a label, it is the class name as words (Event Manager). #[ModifyRole('editor')] takes the same attributes to adjust a role that is not yours. The super roles (['administrator'] above, roles.super_roles in a Pollora application) receive every capability the code declares.

Wiring it outside Pollora

Point the discovery engine at your code and add RoleDiscovery, then hand the roles to WordPress when it builds WP_Roles:

use Pollora\Discovery\Domain\Models\DiscoveryLocation;
use Pollora\Discovery\Infrastructure\Services\DiscoveryEngine;
use Pollora\Role\Application\Services\CapabilityOwnerReader;
use Pollora\Role\Application\Services\RoleDefinitionBuilder;
use Pollora\Role\Application\Services\RoleRegistry;
use Pollora\Role\Domain\Services\PostTypeCapabilityMap;
use Pollora\Role\Domain\Services\RoleCompiler;
use Pollora\Role\Infrastructure\Adapters\WordPressRoleInjector;
use Pollora\Role\Infrastructure\Services\RoleDiscovery;

$reader = new CapabilityOwnerReader;
$registry = new RoleRegistry($reader, new PostTypeCapabilityMap);
$injector = new WordPressRoleInjector($registry, new RoleCompiler, ['administrator']);

// Before WordPress builds WP_Roles: the first thing a plugin loads, or an mu-plugin
add_action('wp_roles_init', $injector->inject(...), 1);
add_action('init', $injector->reportWarnings(...), PHP_INT_MAX);

(new DiscoveryEngine)
    ->addLocation(new DiscoveryLocation('MyPlugin\\', __DIR__.'/src'))
    ->addDiscovery('roles', new RoleDiscovery(new RoleDefinitionBuilder($reader, ['administrator']), $registry, $injector))
    ->run();

Declarations are read before WordPress loads its roles, so run the engine early. The stored roles themselves are read and written through WordPressRoleStore (RoleStoreInterface) and the users who carry a role counted through WordPressRoleUsage (RoleUsageInterface).

Testing

composer test   # unit tests, PHPStan, Pint

License

MIT. See LICENSE.