How to add CLI commands

Overview of adding CLI commands

Magento enables your component to add commands to our Symfony-like command-line interface (CLI).

About the Magento CLI

Magento has one command-line interface that performs both installation and configuration tasks: <magento_root>/bin/magento. The new interface performs multiple tasks, including:

  • Installing Magento (and related tasks such as creating or updating the database schema, creating the deployment configuration, and so on).
  • Clearing the cache.
  • Managing indexes, including reindexing.
  • Creating translation dictionaries and translation packages.
  • Generating non-existent classes such as factories and interceptors for plug-ins, generating the dependency injection configuration for the object manager.
  • Deploying static view files.
  • Creating CSS from Less.

Other benefits:

  • A single command (<magento_root>/bin/magento list) lists all available installation and configuration commands.
  • Consistent user interface based on Symfony.
  • The CLI is extensible so third party developers can “plug in” to it. This has the additional benefit of eliminating users’ learning curve.
  • Commands for disabled modules do not display.

Prerequisites

Before you begin, make sure you understand the following:

  • All Magento command-line interface (CLI) commands rely on the Magento application and must have access to its context, dependency injections, plug-ins, and so on.
  • All CLI commands should be implemented in the scope of your module and should depend on the module’s status.
  • Your command can use the Object Manager and Magento dependency injection features; for example, it can use constructor dependency injection.
  • Your command should have an unique name, defined in the configure() method of the Command class:

    1
    2
    3
    4
    5
    6
    7
    8
    
    protected function configure()
    {
       $this->setName('my:first:command');
       $this->setDescription('This is my first console command.');
    
       parent::configure();
    }
    ...
    

    or in the di.xml file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
       ...
       <type name="Magento\CommandExample\Console\Command\SomeCommand">
          <arguments>
             <!-- configure the command name via constructor $name argument -->
             <argument name="name" xsi:type="string">my:first:command</argument>
          </arguments>
       </type>
       ...
    </config>
    

    or in the __construct method (declaration is similar to di.xml):

    1
    2
    3
    4
    
    public function __construct()
    {
        parent::__construct('my:first:command');
    }
    

    Otherwise the Symfony framework will return an The command defined in "<Command class>" cannot have an empty name. error.

Add CLI commands using dependency injection

The Magento 2 sample modules provide a demonstration of many programming techniques, including adding a CLI command using dependency injection. Look at the sample-module-command for an example. The module’s README.md discusses how to install it.

Following is a summary of the process:

  1. Create a Command class (the recommended location is <your component root dir>/Console/Command).

    See <Magento_Store_module_dir>/Console/Command/StoreListCommand.php for example.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    
    <?php
        namespace Magento\CommandExample\Console\Command;
    
        use Symfony\Component\Console\Command\Command;
        use Symfony\Component\Console\Input\InputInterface;
        use Symfony\Component\Console\Input\InputOption;
        use Symfony\Component\Console\Output\OutputInterface;
    
        /**
         * Class SomeCommand
         */
        class SomeCommand extends Command
        {
            const NAME = 'name';
    
            /**
             * @inheritDoc
             */
            protected function configure()
            {
                $this->setName('my:first:command');
                $this->setDescription('This is my first console command.');
                $this->addOption(
                    self::NAME,
                    null,
                    InputOption::VALUE_REQUIRED,
                    'Name'
                );
    
                parent::configure();
            }
    
            /**
             * Execute the command
             *
             * @param InputInterface $input
             * @param OutputInterface $output
             *
             * @return null|int
             */
            protected function execute(InputInterface $input, OutputInterface $output)
            {
                if ($name = $input->getOption(self::NAME)) {
                    $output->writeln('<info>Provided name is `' . $name . '`</info>');
                }
    
                $output->writeln('<info>Success Message.</info>');
                $output->writeln('<error>An error encountered.</error>');
                $output->writeln('<comment>Some Comment.</comment>');
            }
        }
    

    Style the output text by using <error>, <info>, or <comment> tags. See Symfony docummentation for more information about styling.

  2. Declare your Command class in Magento\Framework\Console\CommandListInterface and configure the command name using dependency injection (<your component root dir>/etc/di.xml):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
        ...
        <type name="Magento\Framework\Console\CommandListInterface">
            <arguments>
                <argument name="commands" xsi:type="array">
                    <item name="commandexample_somecommand" xsi:type="object">Magento\CommandExample\Console\Command\SomeCommand</item>
                </argument>
            </arguments>
        </type>
        ...
    </config>
    
  3. Clean the cache:

    1
    
    bin/magento cache:clean
    
  4. Regenerate the code:

    1
    
    bin/magento setup:di:compile
    

Result

As a result, the new command my:first:command that accepts a --name parameter is ready to use.

1
bin/magento my:first:command --name 'John'

Related topic

Command naming guidelines