How to Implement Custom CLI Commands in Magento 2: A Complete Developer’s Guide

To add a custom command to Magento 2’s CLI, create a module with proper registration files, define the command class extending Symfony’s Command, inject it via etc/di.xml using the console.command tag, and run bin/magento setup:upgrade to register it.

The mageres repository curated by aleron75 catalogs essential Magento development resources, including patterns for extending the platform’s command-line interface. While the repository itself serves as a resource index rather than source code, the standard approach to implement custom CLI commands in Magento 2 relies on the framework’s built-in Symfony Console integration and object manager configuration.

Module Structure and Registration

Every custom CLI command lives within a properly registered Magento module located in app/code/Vendor/YourModule. The framework discovers commands through a combination of module registration, dependency injection configuration, and class implementation.

Create the registration file at app/code/Vendor/YourModule/registration.php to enable autoloading:

<?php
use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Vendor_YourModule',
    __DIR__
);

Declare the module metadata in app/code/Vendor/YourModule/etc/module.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Vendor_YourModule" setup_version="1.0.0"/>
</config>

Dependency Injection Configuration

Magento’s \Magento\Framework\Console\CommandList class collects all console commands during bootstrap. To register your command, inject it into the command list via etc/di.xml using the console.command virtual type pattern:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\Console\CommandList">
        <arguments>
            <argument name="commands" xsi:type="array">
                <item name="vendor:yourcommand" xsi:type="object">Vendor\YourModule\Console\Command\YourCommand</item>
            </argument>
        </arguments>
    </type>
</config>

The name attribute in the item tag serves as the unique identifier for the command instance within the object manager array.

Implementing the Command Class

The command class must extend \Symfony\Component\Console\Command\Command and implement two critical methods: configure() for metadata definition and execute() for business logic.

Create app/code/Vendor/YourModule/Console/Command/YourCommand.php:

<?php
declare(strict_types=1);

namespace Vendor\YourModule\Console\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;

/**
 * Example CLI command that greets a user.
 */
class YourCommand extends Command
{
    /** @var string */
    private const NAME = 'vendor:greet';

    /** @var string */
    private const DESCRIPTION = 'Greets the supplied name';

    protected function configure(): void
    {
        $this
            ->setName(self::NAME)
            ->setDescription(self::DESCRIPTION)
            ->addArgument(
                'name',
                InputArgument::OPTIONAL,
                'Name to greet',
                'World'
            );
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $name = $input->getArgument('name');
        $output->writeln("<info>Hello, {$name}!</info>");
        return Command::SUCCESS;
    }
}

The configure() Method

Use configure() to define the command signature, description, and input expectations. Call $this->setName() to establish the command syntax (e.g., vendor:greet), and chain addArgument() or addOption() to specify input parameters.

The execute() Method

The execute() method receives InputInterface and OutputInterface parameters. Extract input values via $input->getArgument() or $input->getOption(), implement your business logic, and use $output->writeln() for formatted feedback. Always return an integer exit code—typically Command::SUCCESS (0) or Command::FAILURE (1).

Deployment and Verification

After implementing the files, execute the following sequence to activate the command:


# Register the module and update the database schema

bin/magento setup:upgrade

# Verify the command appears in the list

bin/magento list | grep vendor:greet

# Execute the command with an argument

bin/magento vendor:greet John

The output should display: Hello, John!

Summary

  • Extend Symfony’s base class: All Magento 2 CLI commands must extend \Symfony\Component\Console\Command\Command and implement configure() and execute() methods.
  • Configure DI in etc/di.xml: Inject command classes into \Magento\Framework\Console\CommandList using the commands argument array.
  • Follow module structure: Place files in app/code/Vendor/Module with proper registration.php and etc/module.xml declarations.
  • Support dependency injection: Use the constructor to inject Magento services like repositories, factories, or helper classes.
  • Test with bin/magento list: Always verify registration before executing custom logic.

Frequently Asked Questions

Where should custom CLI command files be located in Magento 2?

Place command classes within the Console/Command/ directory of your module, following the pattern app/code/Vendor/YourModule/Console/Command/YourCommand.php. This PSR-4 compliant structure ensures Magento’s autoloader locates the class correctly when referenced in etc/di.xml.

How do I add optional flags and arguments to a custom CLI command?

In the configure() method, chain $this->addArgument() for positional parameters or $this->addOption() for named flags. Specify InputArgument::REQUIRED, InputArgument::OPTIONAL, or InputOption::VALUE_OPTIONAL to control input behavior, and provide default values as the fourth parameter.

Can I inject Magento services into a custom CLI command constructor?

Yes, the object manager supports constructor dependency injection for CLI commands. Inject any service interface—such as \Magento\Catalog\Api\ProductRepositoryInterface or custom factories—into the constructor, and Magento instantiates the dependencies automatically when the command executes.

Why does my custom command not appear in bin/magento list?

Verify three common issues: the module is enabled via bin/magento module:status, the etc/di.xml correctly references the command class FQCN in the CommandList arguments array, and bin/magento setup:upgrade has run successfully to clear generated code and rebuild the dependency injection container.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →