Команды консоли

Фреймворк Symfony предоставляет множество команд через скрипт bin/console (например, хорошо известная команда bin/console cache:clear). Эти команды создаются с помощью Компонента консоль. Вы можете также использовать его для создания собственных команд.

Консоль: APP_ENV и APP_DEBUG

Консольные команды выполняются в окружении, определённом в переменной APP_ENV файла .env, по умолчанию - dev. Он также считывает значение APP_DEBUG, чтобы включать и выключать режим "отладки" (по умолчанию - 1, то есть включён).

Чтобы запустить команду в другом окружении или режиме отладки, отредактируйте значение APP_ENV и APP_DEBUG.

Создание команды

Команды определяются в классах, расширяющих Command. Например, вы можете захотеть, чтобы команда создавала пользователя:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// src/Command/CreateUserCommand.php
namespace App\Command;

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

class CreateUserCommand extends Command
{
    protected function configure()
    {
        // ...
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // ...
    }
}

Конфигурация команды

Для начала, вы должны сконфигурировать имя команды в методе configure(). После этого вы можете по желанию определить сообщение помощи и аргументы и опции ввода:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// ...
protected function configure()
{
    $this
        // имя команды (часть после "bin/console")
        ->setName('app:create-user')

        // краткое описание, отображающееся при запуске "php bin/console list"
        ->setDescription('Creates a new user.')

        // полное описание команды, отображающееся при запуске команды
        // с опцией "--help"
        ->setHelp('This command allows you to create a user...')
    ;
}

Метод configure() вызывается автоматически в конце конструктора команды. Если ваша команда определяет свой конструктор, сначала установите свойства и вызовите родительский конструктор для того, чтобы эти свойства стали доступны в методе configure():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class CreateUserCommand extends Command
{
    // ...

    public function __construct(bool $requirePassword = false)
    {
        // хорошей практикой считается вызывать сначала родительский конструктор и
        // потом установка своих свойств. Это не сработает в данном случае
        // потому что configure() нуждается в установленных свойствах в конструкторе
        $this->requirePassword = $requirePassword;

        parent::__construct();
    }

    public function configure()
    {
        $this
            // ...
            ->addArgument('password', $this->requirePassword ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'User password')
        ;
    }
}

Регистрация команды

Команды Symfony должны быть зарегистрированы как сервисы и отмечены тегом console.command. Если вы используете кофигурацию services.yaml по умолчанию, это уже сделано для вас, благодаря автоконфигурации.

Выполнение команды

После настройки и регистрации команды вы можете выполнить её в терминале:

1
$ php bin/console app:create-user

Как вы могли ожидать эта команда ничего не делает, так как вы пока не прописали никакой логики. Добавьте свою логику в метод execute().

Вывод в консоль

Метод execute() имеет доступ к потоку вывода для того, чтобы писать сообщения в консоль:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// ...
protected function execute(InputInterface $input, OutputInterface $output)
{
    // выводит множество строк в консоль (добавляя "\n" в конце каждой строки)
    $output->writeln([
        'User Creator',
        '============',
        '',
    ]);

    // значение возвращённое someMethod() может быть итератором (https://secure.php.net/iterator)
    // которое генерирует и возвращает сообщение с помощью ключевого слова PHP 'yield'
    $output->writeln($this->someMethod());

    // выводит сообщение с последующим "\n"
    $output->writeln('Ух ты!');

    // выводит сообщение, не добавляя "\n" в конце строки
    $output->write('Вы уже почти ');
    $output->write('создали пользователя.');
}

New in version 4.1: Поддержка итераторов PHP в методах write() и writeln() появилась в Symfony 4.1.

Теперь, попробуйте выполнить команду:

1
2
3
4
5
6
$ php bin/console app:create-user
User Creator
============

Ух ты!
Вы уже почти создали пользователя.

Секции вывода

New in version 4.1: Секции вывода появились в Symfony 4.1.

Обычный вывод консоли может быть разделён на несколько независимых регионов называемых "секции вывода". Создайте одну или несколько данных секций, когда вам нужно очистить и перезаписать выводимую информацию.

Секции создаются методом section(), который возвращает экземпляр ConsoleSectionOutput:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyCommand extends Command
{
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $section1 = $output->section();
        $section2 = $output->section();
        $section1->writeln('Hello');
        $section2->writeln('World!');
        // Вывод отображает "Hello\nWorld!\n"

        // overwrite() заменяет всё содержимое секции с указанным текстом
        $section1->overwrite('Goodbye');
        // Вывод сейчас отображает "Goodbye\nWorld!\n"

        // clear() удаляет всё содержимое данной секции...
        $section2->clear();
        // Вывод сейчас отображает "Goodbye\n"

        // ...но вы можете также удалить указанное число строк
        // (этот пример удаляет последние 2 строки секции)
        $section1->clear(2);
        // Вывод сейчас полностью пустой!
    }
}

Note

Новая строка добавляется автоматически при отображении информации в секции.

Секции вывода позволяют вам манипулировать выводом консоли сложными способами, например, отображение нескольких прогрессбаров, которые обновляются независимо друг от друга и добавление строк в таблицы, которые уже были выведены.

Консоль ввода

Используйте опции или аргументы ввода, чтобы передать информацию в команду:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use Symfony\Component\Console\Input\InputArgument;

// ...
protected function configure()
{
    $this
        // создать аргумент
        ->addArgument('username', InputArgument::REQUIRED, 'The username of the user.')
        // ...
    ;
}

// ...
public function execute(InputInterface $input, OutputInterface $output)
{
    $output->writeln([
        'User Creator',
        '============',
        '',
    ]);

    // получить значение аргумента, используя getArgument()
    $output->writeln('Username: '.$input->getArgument('username'));
}

Теперь вы можете передать имя пользователя в команду:

1
2
3
4
5
$ php bin/console app:create-user Wouter
User Creator
============

Username: Wouter
Смотрите Console Input (Arguments & Options), чтобы узнать больше информации об опциях и аргументах консоли.

Получение сервисов и сервис-контейнера

Чтобы действительно создать нового пользователя, команда должна получить доступ к некоторым сервисам. Так как ваша команда уже зарегистрирована, как сервис, вы можете использовать нормальное внедрение зависимости. Представьте, что у вас есть сервис App\Service\UserManager, к которому вы хотите получить доступ:

 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
// ...
use Symfony\Component\Console\Command\Command;
use App\Service\UserManager;

class CreateUserCommand extends Command
{
    private $userManager;

    public function __construct(UserManager $userManager)
    {
        $this->userManager = $userManager;

        parent::__construct();
    }

    // ...

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // ...

        $this->userManager->create($input->getArgument('username'));

        $output->writeln('User successfully generated!');
    }
}

Жизненный цикл команды

Команды имеют три метода жизненного цикла, которые активируются при выполнении команды:

initialize() (необязательный)
Этот метод выполняется до методов interact() и execute(). Его главная цель - инициализировать переменные, используемые в других методах команды.
interact() (необязательный)
Этот метод выполняется после initialize() и до execute(). Его цель - проверка наличия всех опций/аргументов и спросить пользователя значения в диалоговом режиме. Это последнее место, где вы можете запросить отсутствующие опции/аргументы. После этой команды, отсутствующие опции/аргументы будут приводить к ошибке.
execute() (обязательный)
Этот метод выполняется после``interact()`` и initialize(). Он содержит логику, которую вы хотите выполнить с помощью команды.

Тестирование команд

Symfony предоставляет несколько инструментов, чтобы помочь вам тестировать ваши команды. Самая полезная - это класс CommandTester. Он использует специальные классы ввода и вывода, чтобы облегчить тестирование без настоящей консоли:

 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
// tests/Command/CreateUserCommandTest.php
namespace App\Tests\Command;

use App\Command\CreateUserCommand;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Tester\CommandTester;

class CreateUserCommandTest extends KernelTestCase
{
    public function testExecute()
    {
        $kernel = self::bootKernel();
        $application = new Application($kernel);

        $application->add(new CreateUserCommand());

        $command = $application->find('app:create-user');
        $commandTester = new CommandTester($command);
        $commandTester->execute(array(
            'command'  => $command->getName(),

            // передать аргументы помощнику
            'username' => 'Wouter',

            // добавить к ключу префикс с двумя дефисами при передаче опций,
            // например: '--some-option' => 'option_value',
        ));

        // вывод команды в консоли
        $output = $commandTester->getDisplay();
        $this->assertContains('Username: Wouter', $output);

        // ...
    }
}

Tip

Вы также можете тестировать всю консоль приложения, используя ApplicationTester.

Note

При использовании компонена консоль в отдельностоящем проекте, используйте класс Symfony\Component\Console\Application и расширьте нормальный \PHPUnit\Framework\TestCase.

Чтобы иметь возможность использовать полностью установленный сервис-контейнер для тестов вашей консоли, вы можете расширить ваш тест из KernelTestCase:

 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
// ...
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

class CreateUserCommandTest extends KernelTestCase
{
    public function testExecute()
    {
        $kernel = static::createKernel();
        $kernel->boot();

        $application = new Application($kernel);

        $command = $application->find('app:create-user');
        $commandTester = new CommandTester($command);
        $commandTester->execute(array(
            'command'  => $command->getName(),
            'username' => 'Wouter',
        ));

        $output = $commandTester->getDisplay();
        $this->assertContains('Username: Wouter', $output);

        // ...
    }
}

Узнать больше

Компонент консоль акже содержит набор "помощников" - разных маленьких инструментов, способных помочь вам с разными заданиями:

  • Question Helper: узнать у пользователя информацию в диалоговом режиме
  • Formatter Helper: настроить расцвеичвание вывода
  • Progress Bar: показать бар прогресса
  • Table: отобразить данные в табличной форме

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.