Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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-cli

The project gets the program vendor/bin/laminas. The list of available commands is shown by:

vendor/bin/laminas list

The 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:hello

The 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.

MethodPurpose
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.

Thanks for reading!

Donate to the site — we work hard for you.