pollora / wp-cli
WP-CLI commands declared with PHP attributes: a class with #[WpCli], subcommands with #[Command], synopsis, hooks and conditions as attributes. Runs with or without Laravel.
Requires
- php: ^8.3
- pollora/attributes: ^0.1 || ^1.0
- pollora/discovery: ^0.1 || ^1.0
- psr/log: ^3.0
Requires (Dev)
- antecedent/patchwork: ^2.2
- brain/monkey: ^2.7
- laravel/pint: ^1.22.1
- mockery/mockery: ^1.6.12
- pestphp/pest: ^3.8.2
- phpstan/phpstan: ^2.1.16
- szepeviktor/phpstan-wordpress: ^2.0
- wp-cli/wp-cli: ^2.12
Suggests
- wp-cli/wp-cli: Runs the commands; WP-CLI provides it when it loads WordPress
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-13 08:38:22 UTC
README
WP-CLI commands written as classes: #[WpCli] on a class registers it, its public methods become subcommands, and the command and subcommand names come from the class and method names unless you give them. Synopsis, when, before and after callbacks are attributes too; no WP_CLI::add_command() call to write.
Part of Pollora, the Laravel framework for WordPress, where it is installed and wired for you, with a
pollora:make:wp-cligenerator: see WP-CLI Commands. It runs without Laravel too, in a WordPress plugin, a Bedrock site or any PHP project.
Installation
composer require pollora/wp-cli
Requires PHP 8.3+. It builds on pollora/attributes and pollora/discovery. WP-CLI itself provides the WP_CLI classes when it runs.
Declaring commands
use Pollora\Attributes\WpCli; use Pollora\Attributes\WpCli\Command; use Pollora\Attributes\WpCli\Synopsis; /** * Manage the reports. */ #[WpCli] final class ReportsCommand { public function __construct(private readonly ReportBuilder $reports) {} /** * Build a report. */ #[Command('build')] #[Synopsis('<month> [--format=<format>]')] public function build(array $arguments, array $options): void { WP_CLI::success($this->reports->build($arguments[0], $options['format'] ?? 'csv')); } #[Command('rm')] public function deleteReport(array $arguments, array $options): void {} }
wp reports build 2026-10 --format=json wp reports rm 2026-09
- The command name is the class name in kebab case, without its
Commandsuffix:ReportsCommand→reports.#[WpCli('reports')]sets it. A class with__invoke()is a single command. - A method with
#[Command]is a subcommand registered by this package:#[Command('rm')]names it, and its#[Synopsis],#[When],#[BeforeInvoke],#[AfterInvoke]and#[IsDeferred]become the matchingWP_CLI::add_command()arguments. - WP-CLI also runs the class's other public methods as subcommands, under their method name, with their help read from the docblock.
- The class is built once, with its constructor dependencies, and that instance runs every command and subcommand.
Wiring it outside Pollora
Run the discovery engine with WpCliDiscovery when WP-CLI loads your plugin:
use Pollora\Discovery\Domain\Models\DiscoveryLocation; use Pollora\Discovery\Infrastructure\Services\DiscoveryEngine; use Pollora\WpCli\Application\Services\WpCliService; use Pollora\WpCli\Infrastructure\Adapters\WpCliAdapter; use Pollora\WpCli\Infrastructure\Services\WpCliDiscovery; if (defined('WP_CLI') && WP_CLI) { (new DiscoveryEngine) ->addLocation(new DiscoveryLocation('MyPlugin\\', __DIR__.'/src')) ->addDiscovery('wp_cli_commands', new WpCliDiscovery(new WpCliService(new WpCliAdapter))) ->run(); }
Commands are built by the engine's instance resolver: by reflection by default (constructor parameters typed with a class are built in turn), by your container if you give the engine a ContainerInstanceResolver (PSR-11). WpCliService and WpCliDiscovery take a PSR-3 logger.
Testing
composer test # unit tests, PHPStan, Pint
License
MIT. See LICENSE.