Console commands
A console command is application code run from the terminal rather than the browser: purge old records, send mail, show statistics. Such commands are usually run by the server's task scheduler. Laminas has the laminas-cli package for them, built on the Symfony Console component.
Installation
composer require laminas/laminas-cliThe project gets the program vendor/bin/laminas. The list of available commands is shown by:
vendor/bin/laminas listThe command class
A module's commands go into module/<Module>/src/Command. Create the folder and the file by hand:
// module/Blog/src/Command/HelloCommand.php
namespace Blog\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class HelloCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('The blog is running.');
return Command::SUCCESS;
}
}The execute() method is the body of the command. $output->writeln() prints a line to the terminal, and the return value tells whether the command finished successfully.
Registration
A command is described in two sections of module/Blog/config/module.config.php: the name in laminas-cli, the way to create it in service_manager:
// module/Blog/config/module.config.php
'laminas-cli' => [
'commands' => [
'blog:hello' => Command\HelloCommand::class,
],
],
'service_manager' => [
'factories' => [
Command\HelloCommand::class => InvokableFactory::class,
],
],The blog:hello key is the name the command is called by. By convention it starts with the module name.
Running
vendor/bin/laminas blog:helloThe terminal prints "The blog is running.".
A command with a service and an argument
A command receives services through its constructor, like a controller. Command-line arguments are described in the configure() method:
// module/Blog/src/Command/LatestPostsCommand.php
namespace Blog\Command;
use Blog\Model\PostTable;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class LatestPostsCommand extends Command
{
public function __construct(private PostTable $posts)
{
parent::__construct();
}
protected function configure(): void
{
$this->setDescription('Shows the latest posts');
$this->addArgument('limit', InputArgument::OPTIONAL, 'How many posts to show', 5);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$limit = (int) $input->getArgument('limit');
foreach ($this->posts->fetchLatest($limit) as $post) {
$output->writeln($post->id . ': ' . $post->title);
}
return Command::SUCCESS;
}
}Register it under the name blog:latest with the ReflectionBasedAbstractFactory::class factory. The command vendor/bin/laminas blog:latest 3 prints the three newest posts, and without the number — five.
| Method | Purpose |
|---|---|
setDescription() | The description in the command list |
addArgument() | A positional value: blog:latest 3 |
addOption() | A named parameter: --format=json |
$input->getArgument(), $input->getOption() | Reading the values |
Common mistake
If you forget parent::__construct(); in the constructor of a command with dependencies, the command is not initialised, and running it produces an error saying it has no name or definition. The parent constructor is always called — configure() runs after it.