Talk To Us For FREE

We are in the Business of Support and that starts before you even become a client.

Symfony 2 – How to create console command

Categories : Article, Symfony

For automatically creating console commands with Symfony 2, you will need to create a Command directory within your bundle and also create a PHP file with command.php suffix. This will be required for each command that you want to provide.

The following example shows how to extend the AcmeDemoBundle to greet you from the command line, create GreetCommand.php

// src/Acme/DemoBundle/Command/GreetCommand.php
namespace Acme\DemoBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class GreetCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this
            ->setName('demo:greet')
            ->setDescription('Greet someone')
            ->addArgument('name', InputArgument::OPTIONAL, 'Who do you want to greet?')
            ->addOption('yell', null, InputOption::VALUE_NONE, 'If set, the task will yell in uppercase letters')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $name = $input->getArgument('name');
        if ($name) {
            $text = 'Hello '.$name;
        } else {
            $text = 'Hello';
        }

        if ($input->getOption('yell')) {
            $text = strtoupper($text);
        }

        $output->writeln($text);
    }
}

The command will now be able to automatically run.
Commands can also be declared as services – similar to controllers.

How to get services from the service container
You can get access to the service container by using the ContainerAwareCommand. This will enable you to have access to any configured service.

protected function execute(InputInterface $input, OutputInterface $output)
{
    $name = $input->getArgument('name');
    $logger = $this->getContainer()->get('logger');

    $logger->info('Executing command for '.$name);
    // ...
}

For some services, this code does not work, due to container scopes. If you try to get the request service, the following error occurs:
You cannot create a service (“request”) of an inactive scope (“request”).

Check the following example for the translator service

protected function execute(InputInterface $input, OutputInterface $output)
{
    $name = $input->getArgument('name');
    $translator = $this->getContainer()->get('translator');
    if ($name) {
        $output->writeln($translator->trans('Hello %name%!', array('%name%' => $name)));
    } else {
        $output->writeln($translator->trans('Hello!'));
    }
}

Looking further into the translator components, you’ll see that the request service is required

// vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
public function getLocale()
{
    if (null === $this->locale && $this->container->isScopeActive('request')
        && $this->container->has('request')) {
        $this->locale = $this->container->get('request')->getLocale();
    }

    return $this->locale;
}

When using the translator service, inside a command, you get the following error: You cannot create a service of an inactive scope”
The solution is easy.

protected function execute(InputInterface $input, OutputInterface $output)
{
    $name = $input->getArgument('name');
    $locale = $input->getArgument('locale');

    $translator = $this->getContainer()->get('translator');
    $translator->setLocale($locale);

    if ($name) {
        $output->writeln($translator->trans('Hello %name%!', array('%name%' => $name)));
    } else {
        $output->writeln($translator->trans('Hello!'));
    }
}

Testing Commands

While testing, Symfony\Bundle\FrameworkBundle\Console\Application should be used instead of Symfony\Component\Console\Application:
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Acme\DemoBundle\Command\GreetCommand;

class ListCommandTest extends \PHPUnit_Framework_TestCase
{
    public function testExecute()
    {
        // mock the Kernel or create one depending on your needs
        $application = new Application($kernel);
        $application->add(new GreetCommand());

        $command = $application->find('demo:greet');
        $commandTester = new CommandTester($command);
        $commandTester->execute(
            array(
                'name'    => 'Fabien',
                '--yell'  => true,
            )
        );

        $this->assertRegExp('/.../', $commandTester->getDisplay());

        // ...
    }
}

You can extend your test from KernelTestCase to be able to use the fully set up service container for your console test.

use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Acme\DemoBundle\Command\GreetCommand;

class ListCommandTest extends KernelTestCase
{
    public function testExecute()
    {
        $kernel = $this->createKernel();
        $kernel->boot();

        $application = new Application($kernel);
        $application->add(new GreetCommand());

        $command = $application->find('demo:greet');
        $commandTester = new CommandTester($command);
        $commandTester->execute(
            array(
                'name'    => 'Fabien',
                '--yell'  => true,
            )
        );

        $this->assertRegExp('/.../', $commandTester->getDisplay());

        // ...
    }
}

To be able to use the fully set up service container for your console tests you can extend your test from KernelTestCase:

[Total: 0    Average: 0/5]