Как оформить консольную команду

Как оформить консольную команду

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/AppBundle/Command/GreetCommand.php
namespace AppBundle\Command;

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

class GreetCommand extends ContainerAwareCommand
{
    // ...

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln(array(
            '<info>Lorem Ipsum Dolor Sit Amet</>',
            '<info>==========================</>',
            '',
        ));

        // ...
    }
}

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

Для того, чтобы уменьшить этот шаблонный код, команды Symfony могут по желанию использовать Справочник стилей Symfony. Эти стили реализуются, как набор методов-помощников, которые позволяют создавать семантические команды и забыть об их оформлении.

Базовое использование

В вашей команде, инстанциируйте класс SymfonyStyle и передайте переменные $input и $output в качестве его аргументов. Далее, вы можете начать использовать любой из его помощников, как, например, title(), который отображает заглавие команды:

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

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class GreetCommand extends ContainerAwareCommand
{
    // ...

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $io = new SymfonyStyle($input, $output);
        $io->title('Lorem Ipsum Dolor Sit Amet');

        // ...
    }
}

Методы-помощники

Класс SymfonyStyle определяет некоторые методы-помощники, которые охватывают наиболее распространённый действия, выполняемые консольными командами.

Заглавия методов

title()

Отображает заданную строку, как заглавие команды. Этот метод должен был бы использваться в данной команде только единожды, но ничто не останавливает вас от его повторного использования:

1
$io->title('Lorem ipsum dolor sit amet');
section()

Отображает заданную строку, как заглавие некоторой части команды. Он необходим только в сложных командах, которые требуют лучшего разделения своего содержимого:

1
2
3
4
5
6
7
$io->section('Adding a User');

// ...

$io->section('Generating the Password');

// ...

Методы содержимого

text()

Отображает заданную строку или массив строк, как обычный текст. Это полезно для отображения сообщений помощи и инструкций для пользователя, выполняющего команду:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// используйте простые строки для коротких сообщений
$io->text('Lorem ipsum dolor sit amet');

// ...

// рассмотрите использование массивов при отображении длинных сообщений
$io->text(array(
    'Lorem ipsum dolor sit amet',
    'Consectetur adipiscing elit',
    'Aenean sit amet arcu vitae sem faucibus porta',
));
listing()

Отображает неупорядоченный список элементов, переданных в виде массива:

1
2
3
4
5
$io->listing(array(
    'Element #1 Lorem ipsum dolor sit amet',
    'Element #2 Lorem ipsum dolor sit amet',
    'Element #3 Lorem ipsum dolor sit amet',
));
table()

Отображает заданный массив заголовков и строк в виде компактной таблицы:

1
2
3
4
5
6
7
8
$io->table(
    array('Header 1', 'Header 2'),
    array(
        array('Cell 1-1', 'Cell 1-2'),
        array('Cell 2-1', 'Cell 2-2'),
        array('Cell 3-1', 'Cell 3-2'),
    )
);
newLine()

Отображает пустую строчку в выводе команды. Несмотря на то, что это может выглядеть полезным, в большинстве случаев, он не понадобится вам вообще. Причина кроется в том, что каждый помощник уже добавляет собственные пустые строки, так что вам не придётся заботиться о вертикальной разбивке:

1
2
3
4
5
// выводит одну пустую строку
$io->newLine();

// выводит три пустых строки подряд
$io->newLine(3);

Методы предупреждения

note()

Отображает заданную строку или массив строк, как выделенное предупреждение. Используйте этого помощника осторожно, чтобы избежать засорения вывода команды:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// используйте простые строки для коротких заметок
$io->note('Lorem ipsum dolor sit amet');

// ...

// рассмотрите исползование массивов при отображении длиных заметок
$io->note(array(
    'Lorem ipsum dolor sit amet',
    'Consectetur adipiscing elit',
    'Aenean sit amet arcu vitae sem faucibus porta',
));
caution()

Схож с помощником note(), но содержимое выделяется более заметно. Результирующее содержимое имеет сходство с сообщенем об ошибке,так что вам стоит избегать использования этого помощника кроме случаев, когда это крайне необходимо:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// используйте простые строки для коротких предупреждающих сообщений
$io->caution('Lorem ipsum dolor sit amet');

// ...

// рассмотрите использование массивов при отображении длинных предупреждающих сообщений
$io->caution(array(
    'Lorem ipsum dolor sit amet',
    'Consectetur adipiscing elit',
    'Aenean sit amet arcu vitae sem faucibus porta',
));

Методы индикатора выполнения

progressStart()

Отображает индикатор выполнения с количеством шагов, равным аргументу, переданному методу (не передавайте значение, если длина индикатора выполнения неизвестна):

1
2
3
4
5
// отображает индикатор выполнения неизвестной длины
$io->progressStart();

// отображает индикатор выполнения с длиной в 100 шагов
$io->progressStart(100);
progressAdvance()

Заставляет индикатор выполнения продвинуться на заданное количество шагов (или 1 шаг, если не было передано аргументов):

1
2
3
4
5
// продвигает индикатор выполнения на 1 шаг
$io->progressAdvance();

// продвигает индикатор выполнения на 10 шагов
$io->progressAdvance(10);
progressFinish()

Завершает индикатор выполнения (заполняет все оставшиеся шаги при неизвестной длине):

1
$io->progressFinish();

Методы ввода пользователя

ask()

Просит пользователя предоставить какие-то данные:

1
$io->ask('What is your name?');

Вы можете передать значение по умолчанию в качестве второго аргумента, чтобы пользователь мог просто нажать клавишу <Enter> для выбора этого значения:

1
$io->ask('Where are you from?', 'United States');

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

1
2
3
4
5
6
7
$io->ask('Number of workers to start', 1, function ($number) {
    if (!is_integer($number)) {
        throw new \RuntimeException('You must type an integer.');
    }

    return $number;
});
askHidden()

Очень похож на метод ask(), но ввод пользователя будет скрыт и не может определять значение по умолчанию. Используйте его, когда запрашиваете чувствительную информацию:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$io->askHidden('What is your password?');

// валидирует данный ответ
$io->askHidden('What is your password?', function ($password) {
    if (empty($password)) {
        throw new \RuntimeException('Password cannot be empty.');
    }

    return $password;
});
confirm()

Задаёт пользователю вопрос, на который можно ответить Да/Нет и возвращает только true или false:

1
$io->confirm('Restart the web server?');

Вы можете передать значение по умолчанию в качестве второго аргумента, чтобы пользователь мог просто нажать клавишу <Enter>, чтобы выбрать это значение:

1
$io->confirm('Restart the web server?', true);
choice()

Задаёт вопрос, ответ которого ограничен заданным списком валидных ответов:

1
$io->choice('Select the queue to analyze', array('queue1', 'queue2', 'queue3'));

Вы можете передать значение по умолчанию в качестве третьего аргумента, чтобы пользователь мог просто нажать клавишу <Enter>, чтобы выбрать это значение:

1
$io->choice('Select the queue to analyze', array('queue1', 'queue2', 'queue3'), 'queue1');

Результирующие методы

success()

Отображает заданную строку или массив строк, выделенных, как сообщение об успехе (зелёный фон с ярлыком [OK]). Должен был бы использоваться единожды для отображения финального результата выполнения данной команды, но вы можете использовать его повторно во время выполнения команды:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// используйте простые строки для коротких сообщений об успехе
$io->success('Lorem ipsum dolor sit amet');

// ...

// рассмотрите использование массивов при отображении длинных сообщений об успехе
$io->success(array(
    'Lorem ipsum dolor sit amet',
    'Consectetur adipiscing elit',
));
warning()

Отображает заданную строку или массив строк, выделенных, как сообщение предостережение (с красным фоном и ярлыком [WARNING]). Должен был бы использоваться единожды для отображения финального результата выполнения данной команды, но вы можете использовать его повторно во время выполнения команды:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// используйте простые строки для коротких сообщений предостережения
$io->warning('Lorem ipsum dolor sit amet');

// ...

// рассмотрите использование массивов при отображении длинных сообщений предостережения
$io->warning(array(
    'Lorem ipsum dolor sit amet',
    'Consectetur adipiscing elit',
));
error()

Отображает заданную строку или массив строк, выделенных, как сообщение об ошибке (с красным фоном и ярлыком [ERROR]). Должен был бы использоваться единожды для отображения финального результата выполнения данной команды, но вы можете использовать его повторно во время выполнения команды:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// используйте простые строки для коротких сообщений об ошибке
$io->error('Lorem ipsum dolor sit amet');

// ...

// рассмотрите использование массивов при отображении длинных сообщений об ошибке
$io->error(array(
    'Lorem ipsum dolor sit amet',
    'Consectetur adipiscing elit',
));

Определение ваших собственных стилей

Если вам не нравится дизайн команд, использующих Оформление Symfony, вы можете определить ваш собственный набор стилей консоли. Просто создайте класс, реализующий StyleInterface:

1
2
3
4
5
6
7
8
namespace AppBundle\Console;

use Symfony\Component\Console\Style\StyleInterface;

class CustomStyle implements StyleInterface
{
    // ...реализуйте методы интерфейса
}

Далее, инстанциируйте в ваших командах этот пользовательский класс вместо класса по умолчанию SymfonyStyle. Благодаря StyleInterface вам не понадобится изменять код ваших команд, чтобы изменить их внешний вид:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
namespace AppBundle\Console;

use AppBundle\Console\CustomStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class GreetCommand extends ContainerAwareCommand
{
    // ...

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // До
        // $io = new SymfonyStyle($input, $output);

        // После
        $io = new CustomStyle($input, $output);

        // ...
    }
}

Запись в вывод ошибки

Если вы повторно используете вывод команды в качестве ввода других команд, или сбрасываете его в файл для последующего использования, вы наверное хотите исключить индикаторы выполнения, заметки и другой вывод, который не имеет реального значения.

Команды могут выводить информацию в двух разных потоках: stdout (стандартный вывод) - это поток, где должно выводиться настоящее содержимое, а stderr (стандартная ошибка) - это поток, где должны выводиться ошибки и сообщения отладки.

Класс SymfonyStyle предоставляет удобный метод под названием getErrorStyle() для переключения между потоками. Этот метод возвращает новый экземпляр SymfonyStyle, который использует вывод ошибок:

1
2
3
4
5
6
7
$io = new SymfonyStyle($input, $output);

// Написать в стандартный вывод
$io->write('Reusable information');

// Написать в вывод ошибки
$io->getErrorStyle()->warning('Debugging information or errors');

Note

Если вы создадите экземпляр SymfonyStyle с объектом OutputInterface, который не является экземпляром ConsoleOutputInterface, то метод getErrorStyle() не будет иметь никакого эффекта, а возвращённый объект будет всё равно писать в стандартный вывод, вместо вывода ошибки.

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