How to create and execute a custom console command in Symfony 3

The Symfony framework provide a lot of commands through the bin/console script (e.g. the well-known bin/console cache:clear command). These commands are created with the Console component.

You can also use it to create your own commands easily using the same console component, in this article you'll learn how to create your first custom console command with arguments and parameters.

Your first command

To understand the required structure and how does a command works, we are going to implement your first command which will basically print some text in the console. Commands are defined in classes which must be created in the Command namespace of your bundle (e.g. sandboxBundle\Command) and their names must end with the Command suffix.

To get started, locate your bundle where you want to create your command (in this case, our bundle will be sandboxBundle) and create a folder on it with the name Command. Inside the Command folder, create a new class with the name TestCommand.php.

Your bundle structure should be similar to :

Command folder with the TestCommand class on it

Finally, add the following code to the previously created class (do not forget to change the namespace according to your own bundle):

<?php
// myapplication/src/sandboxBundle/Command/TestCommand.php
// Change the namespace according to your bundle
namespace sandboxBundle\Command;

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

class TestCommand extends Command
{
    protected function configure()
    {
        $this
            // the name of the command (the part after "bin/console")
            ->setName('app:test-command')
            // the short description shown while running "php bin/console list"
            ->setDescription('Prints some text into the console.')
            // the full command description shown when running the command with
            // the "--help" option
            ->setHelp("This command allows you to print some text in the console")
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // outputs multiple lines to the console (adding "\n" at the end of each line)
        $output->writeln([
            'My First Symfony command',// A line
            '============',// Another line
            '',// Empty line
        ]);

        // outputs a message followed by a "\n"
        $output->writeln('Hey welcome to the test command wizard.');
        $output->writeln('Thanks for read the article');
        
        // outputs a message without adding a "\n" at the end of the line
        $output->write("You've succesfully implemented your first command");
    }
}

Now, our command is ready to be executed. As defined in the configure method, the name of the previous command will be app:test-command and can be executed using the following line in the command prompt :

php bin/console app:test-command

And the output in the command prompt :

Symfony custom console output

A piece of cake isn't, now that you understand how does a command works, you're ready to create more complex commands.

Handle and set command arguments (input)

Sometimes you'll need to provide some parameters to your commands as number, identifiers, text etc.

To allow the use of arguments in our commands, we need to include the InputArgument class in our command to add arguments, use the addArgument method in the configure method.

<?php
// myapplication/src/sandboxBundle/Command/TestCommand.php
// Change the namespace according to your bundle
namespace sandboxBundle\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
// Add the InputArgument class
use Symfony\Component\Console\Input\InputArgument;

class TestCommand extends Command
{
    protected function configure()
    {
        $this
            // the name of the command (the part after "bin/console")
            ->setName('app:print-text')
            // the short description shown while running "php bin/console list"
            ->setHelp("This command allows you to print some text in the console")
            // the full command description shown when running the command with
            ->setDescription('Prints some text into the console with given parameters.')
            // Arguments
            ->addArgument('text', InputArgument::REQUIRED, 'The text to print')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // outputs multiple lines to the console (adding "\n" at the end of each line)
        $output->writeln([
            'My Second Symfony command',// A line
            '============',// Another line
            '',// Empty line
        ]);
        
        // Get providen text using the $input->getArgument method.
        $text = $input->getArgument('text');
        
        $output->writeln("Providen text : ".$text);
    }
}

Now our command is ready to be executed. As defined in the configure method, the name of the previous command will be app:print-text and it expects as parameter a string to print. It can be executed using the following line in the command prompt:

php bin/console app:print-text "Hello world, my first argument"

And the output in the command prompt :

Symfony print argument

Handle and set command options (input parameters)

Sometimes you'll need to provide some parameters to your commands as numbers, identifiers etc. as any existing command does. By default, the command can handle parameters if they're previously configured in the command code, otherwise you'll get error messages like Too many arguments or the [option] does not exist.

To add options to your command, we need to include the InputDefinition and InputOption classes, then we'll add the options using the setDefinition method and the options as first parameter of InputDefinition within an array.

<?php
// myapplication/src/sandboxBundle/Command/TestCommand.php
// Change the namespace according to your bundle
namespace sandboxBundle\Command;

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

// Add the required classes
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;


class TestCommand extends Command
{
    protected function configure()
    {
        $this
            // the name of the command (the part after "bin/console")
            ->setName('app:print-lines')
            // the short description shown while running "php bin/console list"
            ->setHelp("This command allows you to print some text in the console")
            // the full command description shown when running the command with
            ->setDescription('Prints some text into the console with given parameters.')
            // Set options
            ->setDefinition(
                new InputDefinition(array(
                    new InputOption('firstline', 'a', InputOption::VALUE_REQUIRED,"The first line to be printed","Default First Line Value"),
                    new InputOption('secondline', 'b', InputOption::VALUE_OPTIONAL,"The second line to be printed","Default First Line Value"),
                ))
            )
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // outputs multiple lines to the console (adding "\n" at the end of each line)
        $output->writeln([
            'My Third Symfony command',// A line
            '============',// Another line
            '',// Empty line
        ]);
        
        $firstLine = $input->getOption('firstline');
        $secondline = $input->getOption('secondline');
        
        $output->writeln("First line value : ".$firstLine);
        
        if($secondline){
            $output->writeln("Second line value : ".$secondline);
        }
        
        // Instead of retrieve line per line every option, you can get an array of all the providen options :
        //$output->writeln(json_encode($input->getOptions()));
    }
}

Note: the shortcuts needs to be of 1 character maximum. If you need to add only 1 parameter, you can use the setOption method in the configure function.

Now our command is ready to be executed. As defined in the configure method, the name of the previous command will be app:print-lines and it expects as parameter a string to print. It can be executed using the following line in the command prompt:

php bin/console app:print-lines --firstline="Hello" --secondline="World"

#Or with the shortcuts

php bin/console app:print-lines -a "Hello" -b "World"

Accesing services from container

Probably, your consoles are not only for write some text in the console. You can access all the services from the container within a command i.e Doctrine, Translator etc.

You just need to include the ContainerAwareCommand class into your command and instead of extend the Command class it needs to extend the ContainerAwareCommand class and you'll be able to access the container as described below, otherwise you'll find errors like Attempted to call and undefined method named "getContainer" of class YourCommand.

In this case, we are going to get the Doctrine service to get the entity manager and create some queries to our database :

<?php
// myapplication/src/sandboxBundle/Command/TestCommand.php
// Change the namespace according to your bundle
namespace sandboxBundle\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
// Add the Container
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;

//Extend ContainerAwareCommand instead of Command
class TestCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this
            // the name of the command (the part after "bin/console")
            ->setName('app:verify-doctrine')
            // the short description shown while running "php bin/console list"
            ->setHelp("This command allows you to print some text in the console")
            // the full command description shown when running the command with
            ->setDescription('Prints some text into the console with given parameters.')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln([
            'My Final Symfony command',// A line
            '============',// Another line
            '',// Empty line
        ]);
        
        $doctrine = $this->getContainer()->get('doctrine');
        $em = $doctrine->getEntityManager();
        
        // Now you can get repositories
        // $usersRepo = $em->getRepository("myBundle:Users");
        // $user = $usersRepo->find(1);
        
        // outputs multiple lines to the console (adding "\n" at the end of each line)
        
        $output->writeln("Doctrine worked, it didn't crashed :) ");
        
        // Instead of retrieve line per line every option, you can get an array of all the providen options :
        //$output->writeln(json_encode($input->getOptions()));
    }
}

And the output :

Doctrine symfony 3 command

Note that is recommended and is a good practice to create your own custom services to handle all the logic (create,edit, find etc.).

Conclusion

It can be difficult to understand the way arguments are handled by the console application, however the Symfony Console application, like many other CLI utility tools, follows the behavior described in the docopt standards.

Have fun !

Become a more social person