CLI Commands
Same name and namespace in other branches
- 11.x core/core.api.php \console_commands
Providing CLI commands for the Drupal CLI.
Drupal includes a CLI tool (invoked as `vendor/bin/dr`) built on the Symfony Console component. Modules can provide their own commands that are automatically discovered.
Creating a command
To provide a command, place a class in your module's `src/Command/` directory and apply the `#[AsCommand]` attribute to declare the command name and description. Command names follow the convention `module:action` (e.g. `my_module:hello`).
There are two types of commands:
Extending Symfony's Command class
One style extends `\Symfony\Component\Console\Command\Command` and implements `execute()`:
use Drupal\Component\Datetime\TimeInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class HelloCommand extends Command {
public function __construct(private readonly TimeInterface $time) {
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output) : int {
$io = new SymfonyStyle($input, $output);
$now = new \DateTimeImmutable('@' . $this->time
->getRequestTime());
$io->success('Hello! The time is ' . $now->format('r'));
return Command::SUCCESS;
}
}Invokable command classes
As an alternative, a plain class can define a single `__invoke()` method instead of extending `\Symfony\Component\Console\Command\Command`. Symfony Console attributes such as `#[Argument]`, `#[Option]`, and `#[Ask]` can be applied to individual parameters to declare arguments, options, and interactive prompts, removing the need for a `configure()` method. Drupal services can be injected via the constructor as normal.
use Drupal\Component\Datetime\TimeInterface;
use Symfony\Component\Console\Attribute\Argument;
use Symfony\Component\Console\Attribute\Ask;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Attribute\Option;
use Symfony\Component\Console\Output\OutputInterface;
class GreetCommand {
public function __construct(protected readonly TimeInterface $time) {
}
public function __invoke(OutputInterface $output, #[Argument('The name to greet.')] #[Ask('Who should be greeted?', 'World')] string $name, #[Option('Shout the greeting.')] bool $shout = FALSE) : int {
$now = new \DateTimeImmutable('@' . $this->time
->getRequestTime());
$greeting = "[{$now}] Hello, {$name}!";
$output->writeln($shout ? strtoupper($greeting) : $greeting);
return Command::SUCCESS;
}
}Auto-discovery and dependency injection
`\Drupal\Core\DependencyInjection\Compiler\ConsoleCompilerPass` scans each installed module's `src/Command/` directory at container compile time. Any class carrying the `#[AsCommand]` attribute is automatically registered as an autowired service tagged `console.command` — no service YAML entry is needed. Constructor dependencies are resolved from the service container automatically.
After Drupal bootstraps, `\Drupal\Core\Command\DrupalApplication` registers all tagged commands.
Registering via service tag
Commands that do not use `#[AsCommand]` — for example, those that declare their name only via `configure()` — are not auto-discovered and must be registered explicitly as a service tagged `console.command` in a module's `*.services.yml` file:
my_module.hello_command:
class: Drupal\my_module\Command\HelloCommand
tags:
- { name: console.command }
See also
\Drupal\Core\Command\DrupalApplication
\Drupal\Core\DependencyInjection\Compiler\ConsoleCompilerPass
Parent topics
File
-
core/
core.api.php, line 2869
Classes
| Title Sort descending | File name | Summary |
|---|---|---|
| DrupalApplication | core/ |
Customize the Symfony Application for Drupal. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.