Warning
This is a read-only repository! For pull requests or issues, see stellarwp/foundation.
Foundation helpers for building WP-CLI commands with the Foundation container.
composer require stellarwp/foundation-wpcliWP-CLI is expected to provide the WP_CLI and WP_CLI_Command runtime classes. This package includes wp-cli/wp-cli as a development dependency for tests and static analysis, but applications normally do not need to install it separately when running inside WP-CLI.
Install stellarwp/foundation-wpcli as a normal dependency when the plugin ships WP-CLI commands. Install stellarwp/foundation-cli separately with composer require --dev stellarwp/foundation-cli only when developers need generators such as make:wpcli-command.
Extend StellarWP\Foundation\WPCli\Command for commands that should receive the Foundation container.
If the project also installs stellarwp/foundation-cli as a development dependency, scaffold a WP-CLI command class in a consuming WordPress project:
vendor/bin/foundation make:wpcli-command Sync_Products_CommandIf the consuming project has a Composer script named foundation that points to the installed Foundation binary, it can also run composer run foundation -- make:wpcli-command Sync_Products_Command.
The generator reads the project's autoload.psr-4 namespaces from composer.json and writes a Snake_Case command class under Cli/Commands inside the default PSR-4 root. When --namespace is passed, the output path is resolved from the matching PSR-4 root unless --path is also passed.
For example, a project with this Composer autoload entry:
{
"autoload": {
"psr-4": {
"Acme\\Plugin\\": "src"
}
}
}will generate:
src/Cli/Commands/Sync_Products_Command.php
with namespace:
Acme\Plugin\Cli\CommandsThe generated class extends StellarWP\Foundation\WPCli\Command and includes example positional, associative, and flag arguments using constants.
When generated through foundation-cli, projects using Strauss with extra.strauss.namespace_prefix receive prefixed Foundation imports automatically.
Available options:
vendor/bin/foundation make:wpcli-command Sync_Products_Command --namespace="Acme\\Plugin\\Cli" --path=src/Cli --subcommand=sync-products --description="Sync products." --forceProject stub overrides live under:
foundation/stubs/wpcli/command.stub
When present, the override is used instead of the default stub from the foundation-wpcli package.
Override stubs should use the same context-aware placeholders as the default stub when writing PHP literals. For example, use {{ description_php }} and {{ subcommand_php }} for values returned from PHP methods, and {{ foundation_wpcli_command }} for the Foundation command import so Strauss-prefixed projects keep working.
<?php declare(strict_types=1);
namespace {{ namespace }};
use {{ foundation_wpcli_command }};
final class {{ class }} extends Command
{
public function runCommand( array $args = [], array $assocArgs = [] ): int {
// Run the command using services from $this->container.
return self::SUCCESS;
}
protected function subcommand(): string {
return {{ subcommand_php }};
}
protected function description(): string {
return {{ description_php }};
}
protected function arguments(): array {
return [
[
'type' => self::FLAG,
'name' => 'dry-run',
'description' => 'Preview the sync without writing changes.',
'optional' => true,
],
];
}
}Applications should register their own provider so they control the command namespace and command list.
Do not register StellarWP\Foundation\Cli\CliProvider in a WordPress plugin. That provider belongs to the developer-facing foundation console binary, not plugin runtime bootstrap.
Generated command classes use Strauss-prefixed Foundation imports automatically when extra.strauss.namespace_prefix is configured. Handwritten provider code is still application code, so projects using Strauss with update_call_sites=false may need to use their prefixed Foundation namespace in the imports below.
<?php declare(strict_types=1);
namespace Acme\App\Cli;
use StellarWP\Foundation\Container\Contracts\Provider;
use StellarWP\Foundation\WPCli\Command;
use StellarWP\Foundation\WPCli\TimestampedLogger;
use WP_CLI;
use WP_CLI\Loggers\Regular;
final class Wp_Cli_Provider extends Provider
{
private const string COMMAND_PREFIX = 'acme';
/**
* @var list<class-string<Command>>
*/
private const array COMMANDS = [
Sync_Command::class,
];
public function register(): void {
if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
return;
}
$this->configureCommands();
$this->registerTimestampedLogger();
add_action( 'cli_init', function (): void {
foreach ( self::COMMANDS as $commandClass ) {
$command = $this->container->get( $commandClass );
if ( $command instanceof Command ) {
$command->register();
}
}
}, 0, 0 );
}
private function configureCommands(): void {
foreach ( self::COMMANDS as $commandClass ) {
$this->container->when( $commandClass )
->needs( '$commandPrefix' )
->give( self::COMMAND_PREFIX );
}
}
private function registerTimestampedLogger(): void {
$wpCliLogger = WP_CLI::get_logger();
if ( $wpCliLogger instanceof Regular ) {
WP_CLI::set_logger( new TimestampedLogger( $wpCliLogger ) );
}
}
}Use cli_init so commands are registered only during WP-CLI command bootstrap, after WordPress has loaded enough for plugin providers and hooks to be available.
If your application does not use WordPress hooks during bootstrap, call the command registration loop at the point where WP-CLI is active and your container has been configured.