Ввод консоли (аргументы и опции)
Дата обновления перевода 2024-07-17
Ввод консоли (аргументы и опции)
Наиболее интересной частью команд являются аргументы и опции, которые вы можете сделать доступными. Эти аргументы и опции позволяют вам передавать динамическую информацию из терминала в команду.
Использование аргументов команды
Аргументы - это строки, разделённые пробелами, которые идут после самого
имени команды. Они упорядочены и могут быть обязательными или необязательными.
Например, чтобы добавить необязательный аргумент last_name
в команду и сделать
аргумент name
обязательным:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// ...
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
class GreetCommand extends Command
{
// ...
protected function configure(): void
{
$this
// ...
->addArgument('name', InputArgument::REQUIRED, 'Who do you want to greet?')
->addArgument('last_name', InputArgument::OPTIONAL, 'Your last name?')
;
}
}
Теперь у вас есть доступ к аргументу last_name
в вашей команде:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// ...
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class GreetCommand extends Command
{
// ...
protected function execute(InputInterface $input, OutputInterface $output): int
{
$text = 'Hi '.$input->getArgument('name');
$lastName = $input->getArgument('last_name');
if ($lastName) {
$text .= ' '.$lastName;
}
$output->writeln($text.'!');
return Command::SUCCESS;
}
}
Теперь команда может быть использована любым из следующих способов:
1 2 3 4 5
$ php bin/console app:greet Fabien
Привет, Фабиен!
$ php bin/console app:greet Fabien Potencier
Привет, Фабиен Потенсье!
Также возможно позволить аргументу использовать список значений (представьте, что вы хотите поздороваться со всеми вашими друзьями). Только последний аргумент может быть списком:
1 2 3 4 5 6 7
$this
// ...
->addArgument(
'names',
InputArgument::IS_ARRAY,
'С кем вы хотите поздороваться (разделите несколько имён пробелом)?'
);
Чтобы использовать это, просто укажите столько имён, сколько хотите:
1
$ php bin/console app:greet Fabien Ryan Bernhard
Вы можете получить доступ к аргументу names
, как к массиву:
1 2 3 4
$names = $input->getArgument('names');
if (count($names) > 0) {
$text .= ' '.implode(', ', $names);
}
Существует три варианта аргументов, которые вы можете использовать:
InputArgument::REQUIRED
- Аргумент обязателен. Команда не будет выполнена, если этого аргумента нет;
InputArgument::OPTIONAL
- Аргумент необязателен и поэтому может быть опущен. Это поведение аргументов по уполчанию;
InputArgument::IS_ARRAY
- Аргумент может содержать любое количество значений. По этой причине, он может быть использован в конце списка аргументов.
Вы можете комбинировать IS_ARRAY
с REQUIRED
и OPTIONAL
таким образом:
1 2 3 4 5 6 7
$this
// ...
->addArgument(
'names',
InputArgument::IS_ARRAY | InputArgument::REQUIRED,
'С кем вы хотите поздороваться (разделите несколько имён пробелом)?'
);
Использование опций команды
В отличие от аргументов, опции не упорядочены (то есть вы можете указывать их
в любом порядке) и указываются с двумя дефисами (например, --yell
). Опции
всегда необязательны, и могут быть настроены так, чтобы принимать значение
(например, --dir=src
) или просто как булев флажок без значения (например,
--yell
).
К примеру, добавьте новую опцию к команде, которая может быть использована для указания того, сколько раз подряд должно быть напечатано сообщение:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// ...
use Symfony\Component\Console\Input\InputOption;
$this
// ...
->addOption(
// это имя, которое должны ввести пользователи, чтобы передать эту опцию (например, --iterations=5)
'iterations',
// это необязательное сокращение имени опции, которое обычно является просто буквой
// (например, `i`, чтобы пользователи передавали её как `-i`); используйте это для часто используемых опций
// или опций с длинными именами
null,
// это тип опции (например, требует значения, может быть передана больше одного раза и т.д.)
InputOption::VALUE_REQUIRED,
// описание опции, отображённое при демонстрации помощи команды
'How many times should the message be printed?',
// значение опции по умолчанию (для тех, которые разрешают передачу значений)
1
)
;
Далее, используйте это в команде, чтобы напечатать сообщение несколько раз:
1 2 3
for ($i = 0; $i < $input->getOption('iterations'); $i++) {
$output->writeln($text);
}
Теперь, когда вы выполняете команду, вы можете по желанию указать флажок
--iterations
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# --iterations не предоставлены, используется значение по умолчанию (1)
$ php bin/console app:greet Fabien
Привет, Фабиен!
$ php bin/console app:greet Fabien --iterations=5
Привет, Фабиен
Привет, Фабиен
Привет, Фабиен
Привет, Фабиен
Привет, Фабиен
Привет, Фабиен
# порядок опций неважен
$ php bin/console app:greet Fabien --iterations=5 --yell
$ php bin/console app:greet Fabien --yell --iterations=5
$ php bin/console app:greet --yell --iterations=5 Fabien
Tip
Вы также можете объявить шорткат из одной буквы, который вы можете вызвать
с помощью одного дефиса, например, -i
:
1 2 3 4 5 6 7 8 9
$this
// ...
->addOption(
'iterations',
'i',
InputOption::VALUE_REQUIRED,
'Сколько раз должно быть напечатано сообщение?',
1
);
Отметьте, что в соответствии с требованиями стандарта docopt, длинные опции могут
указывать свои значения после пробела или символа =
(например, --iterations 5
или --iterations=5
), а короткие опции могут использовать только пробелы или вообще
никакого разделения (например, -i 5
или -i5
).
Caution
Хотя возможно отделить опцию от её значения с помощью пробела, использование этой
формы приводит к двусмысленности, если опция появляется до имени команды. Например,
php bin/console --iterations 5 app:greet Fabien
- двусмысленно; Symfony посчитает
5
именем команды. Чтобы избежать такой ситуации, всегда помещайте опции после
имени команды или избегайте использования пробелов для разделения имени опции и её
значения.
Существует пять вариантов опций, которые вы можете использовать:
InputOption::VALUE_IS_ARRAY
-
Эта опция принимает несколько значений (например,
--dir=/foo --dir=/bar
); InputOption::VALUE_NONE
-
Не принимает ввод для этой опции (например,
--yell
). Возвращённое значение будет булевым (false
, если опция не предоставлена). Это поведение аргументов по умолчанию; InputOption::VALUE_REQUIRED
-
Это значение обязательно (например,
--iterations=5
), но сама опция всё ещё необязательна; InputOption::VALUE_OPTIONAL
-
Эта опция может иметь или не иметь значения (например,
--yell
или--yell=loud
). InputOption::VALUE_NEGATABLE
-
Принимает либо флажок (например,
--yell
) или его отрицание (например,--no-yell
).
Вы можете комбинировать VALUE_IS_ARRAY
с VALUE_REQUIRED
или
VALUE_OPTIONAL
таким образом:
1 2 3 4 5 6 7 8 9
$this
// ...
->addOption(
'colors',
null,
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Какие цвета вам нравятся?',
array('blue', 'red')
);
Опции с необязательными аргументами
Ничего не запрещает вам создать команду с опцией, которая опционально принимает значение, но это немного сложно. Рассмотрите этот пример:
1 2 3 4 5 6 7 8 9 10 11 12
// ...
use Symfony\Component\Console\Input\InputOption;
$this
// ...
->addOption(
'yell',
null,
InputOption::VALUE_OPTIONAL,
'Should I yell while greeting?'
)
;
Эта опция может быть использована 3 способами: greet --yell
, greet --yell=louder
и greet
. Однако, сложно различить передачу опции без значения (greet --yell
) и
отсутствие передачи опции (greet
).
Чтобы решить эту проблему, вам нужно установить значение опции по умолчанию как
false
:
1 2 3 4 5 6 7 8 9 10 11 12 13
// ...
use Symfony\Component\Console\Input\InputOption;
$this
// ...
->addOption(
'yell',
null,
InputOption::VALUE_OPTIONAL,
'Should I yell while greeting?',
false // это значение по умолчанию вместо null
)
;
Теперь возможно различать между отсутствием передачи опции и отсутствием передачи значения опции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
$optionValue = $input->getOption('yell');
if (false === $optionValue) {
// в этом случае, опция не была передана при выполнении команды
$yell = false;
$yellLouder = false;
} elseif (null === $optionValue) {
// в этом случае, опция была передана при выполнении команды,
// но ей не было дано значение
$yell = true;
$yellLouder = false;
} else {
// в этом случае, опция была передана при выполнении команды и
// ей было присвоено какое-то конкретное значение
$yell = true;
if ('louder' === $optionValue) {
$yellLouder = true;
} else {
$yellLouder = false;
}
}
Код выше можно упростить следующим образом, потому как false !== null
:
1 2 3
$optionValue = $input->getOption('yell');
$yell = ($optionValue !== false);
$yellLouder = ($optionValue === 'louder');
Добавление заполнения значения аргумента/опции
Если установлено заполнение Console , имена команды
и опции будут автозаполнены оболочкой. Однако, вы также можете реализовать заполнение
значения для ввода в ваших командах. Например, вы можете захотеть заполнить все имена
пользователей из базы данных в аргументе name
вашей команды приветствия.
Чтобы достичь этого, используйте 5ый аргумент addArgument()
/ addOption
:
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
// ...
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
class GreetCommand extends Command
{
// ...
protected function configure(): void
{
$this
->addArgument(
'names',
InputArgument::IS_ARRAY,
'Who do you want to greet (separate multiple names with a space)?',
null,
function (CompletionInput $input): array {
// значение пользователя уже введено, например, при вводе "app:greet Fa" ранее
// pressing Tab, this will contain "Fa"
$currentValue = $input->getCompletionValue();
// получить список имен пользователей откуда-то (например, из базы данных)
// вы можете использовать $currentValue, чтобы отфильтровать имена
$availableUsernames = ...;
// затем предложенные имена пользователей в качестве значений
return $availableUsernames;
}
)
;
}
}
Это всё, что вам нужно! Предполагая, что пользователи "Fabien" и "Fabrice" существуют,
нажатие таба после ввода app:greet Fa
даст вам эти имена в качестве предложения.
Tip
Скрипт оболочки может обрабатывать огромные количества предложений будет автоматически фильтровать предложенные значения, основываясь на существующем вводе пользователя. Вам не нужно реализовывать никакую логику фильтрации в команде.
Вы можете использовать CompletionInput::getCompletionValue()
, чтобы получить
текущий ввод, если это помогает улучшить производительность (например, уменьшив
количество рядов, полученных из базы данных).
Тестирование скрипта заполнения
Компонент Console поставляется со специальным классом CommandCompletionTester помогает вам модульно тестировать логику заполнения:
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
// ...
use Symfony\Component\Console\Application;
class GreetCommandTest extends TestCase
{
public function testComplete(): void
{
$application = new Application();
$application->add(new GreetCommand());
// создать новый тестер с командой приветствия
$tester = new CommandCompletionTester($application->get('app:greet'));
// заполнить ввод без существующего ввода (пустая строка представляет позицию
// курсора)
$suggestions = $tester->complete(['']);
$this->assertSame(['Fabien', 'Fabrice', 'Wouter'], $suggestions);
// Если вы отфильтровываете значения внутри вашего кода (не рекомендуется, если вам не
// нужно улучшить производительность, к примеру, запроса базы данных), вы можете протестировать
// это, передав ввод пользователя
$suggestions = $tester->complete(['Fa']);
$this->assertSame(['Fabien', 'Fabrice'], $suggestions);
}
}
Глобальные опции команды
Компонент Console добавляет некоторые предопределенные опции ко всем командам:
--verbose
: устанавливает уровень подробности (например,1
по умолчанию,2
и3
, или вы можете использовать соответствующие сокращения-v
,-vv
и-vv
)--quiet
: отключает вывод и взаимодействие--no-interaction
: отключает взаимодействие--version
: выводит номер версии приложения консоли--help
: выводит справку по команде--ansi|--no-ansi
: отключать ли раскраску вывода принудительно
При использовании ``FrameworkBundle'' предопределены еще две опции:
--env
: устанавливает окружение конфигурации ядра (по умолчаниюAPP_ENV
)--no-debug
: отключает отладку ядра (по умолчаниюAPP_DEBUG
).
Таким образом, ваши пользовательские команды тоже могут использовать их сразу после установки.