Question Helper

The QuestionHelper provides functions to ask the user for more information. It is included in the default helper set and you can get it by calling getHelper():

QuestionHelper は、ユーザーに詳細を尋ねる機能を提供します。これは defaulthelper セットに含まれており、getHelper() を呼び出すことで取得できます。
1
$helper = $this->getHelper('question');

The Question Helper has a single method ask() that needs an InputInterface instance as the first argument, an OutputInterface instance as the second argument and a Question as last argument.

Question Helper には、最初の引数として InputInterface インスタンス、2 番目の引数として OutputInterface インスタンス、最後の引数として Question を必要とする単一のメソッド ask() があります。

Asking the User for Confirmation

Suppose you want to confirm an action before actually executing it. Add the following to your command:

アクションを実際に実行する前に確認したいとします。コマンドに次を追加します。
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\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;

class YourCommand extends Command
{
    // ...

    public function execute(InputInterface $input, OutputInterface $output): int
    {
        $helper = $this->getHelper('question');
        $question = new ConfirmationQuestion('Continue with this action?', false);

        if (!$helper->ask($input, $output, $question)) {
            return Command::SUCCESS;
        }

        // ... do something here

        return Command::SUCCESS;
    }
}

In this case, the user will be asked "Continue with this action?". If the user answers with y it returns true or false if they answer with n. The second argument to __construct() is the default value to return if the user doesn't enter any valid input. If the second argument is not provided, true is assumed.

この場合、ユーザーは「このアクションを続行しますか?」と尋ねられます。ユーザーが y で答えた場合、n で答えた場合は true または false を返します。to__construct() の 2 番目の引数は、ユーザーが有効な入力を何も入力しなかった場合に返されるデフォルト値です。 2 番目の引数が指定されていない場合は、true が想定されます。

Tip

ヒント

You can customize the regex used to check if the answer means "yes" in the third argument of the constructor. For instance, to allow anything that starts with either y or j, you would set it to:

コンストラクターの 3 番目の引数で、答えが「はい」を意味するかどうかを確認するために使用される正規表現をカスタマイズできます。たとえば、y または j で始まるものをすべて許可するには、次のように設定します。
1
2
3
4
5
$question = new ConfirmationQuestion(
    'Continue with this action?',
    false,
    '/^(y|j)/i'
);

The regex defaults to /^y/i.

正規表現のデフォルトは /^y/i です。

Asking the User for Information

You can also ask a question with more than a simple yes/no answer. For instance, if you want to know a bundle name, you can add this to your command:

単純な「はい/いいえ」以上の質問をすることもできます。たとえば、バンドル名を知りたい場合は、これをコマンドに追加できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
use Symfony\Component\Console\Question\Question;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    // ...
    $question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle');

    $bundleName = $helper->ask($input, $output, $question);
    
    // ... do something with the bundleName
    
    return Command::SUCCESS;
}

The user will be asked "Please enter the name of the bundle". They can type some name which will be returned by the ask() method. If they leave it empty, the default value (AcmeDemoBundle here) is returned.

ユーザーは「バンドルの名前を入力してください」と尋ねられます。 ask() メソッドによって返される名前を入力できます。空のままにすると、デフォルト値 (ここでは AcmeDemoBundle) が返されます。

Let the User Choose from a List of Answers

If you have a predefined set of answers the user can choose from, you could use a ChoiceQuestion which makes sure that the user can only enter a valid string from a predefined list:

ユーザーが選択できる定義済みの回答セットがある場合は、ユーザーが定義済みのリストから有効な文字列のみを入力できるようにする ChoiceQuestion を使用できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use Symfony\Component\Console\Question\ChoiceQuestion;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    // ...
    $helper = $this->getHelper('question');
    $question = new ChoiceQuestion(
        'Please select your favorite color (defaults to red)',
        // choices can also be PHP objects that implement __toString() method
        ['red', 'blue', 'yellow'],
        0
    );
    $question->setErrorMessage('Color %s is invalid.');

    $color = $helper->ask($input, $output, $question);
    $output->writeln('You have just selected: '.$color);

    // ... do something with the color
    
    return Command::SUCCESS;
}

The option which should be selected by default is provided with the third argument of the constructor. The default is null, which means that no option is the default one.

デフォルトで選択されるべきオプションは、コンストラクターの 3 番目の引数で提供されます。デフォルトは null です。つまり、nooption がデフォルトです。

If the user enters an invalid string, an error message is shown and the user is asked to provide the answer another time, until they enter a valid string or reach the maximum number of attempts. The default value for the maximum number of attempts is null, which means an infinite number of attempts. You can define your own error message using setErrorMessage().

ユーザーが無効な文字列を入力すると、エラー メッセージが表示され、ユーザーは有効な文字列を入力するか、最大試行回数に達するまで、もう一度回答を求めるよう求められます。最大試行回数のデフォルト値は null です。これは、試行回数が無限であることを意味します。 setErrorMessage() を使用して、独自のエラー メッセージを定義できます。

Multiple Choices

Sometimes, multiple answers can be given. The ChoiceQuestion provides this feature using comma separated values. This is disabled by default, to enable this use setMultiselect():

場合によっては、複数の回答を与えることができます。 ChoiceQuestion は、コンマ区切りの値を使用してこの機能を提供します。これはデフォルトで無効になっています。これを有効にするには、setMultiselect() を使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use Symfony\Component\Console\Question\ChoiceQuestion;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    // ...
    $helper = $this->getHelper('question');
    $question = new ChoiceQuestion(
        'Please select your favorite colors (defaults to red and blue)',
        ['red', 'blue', 'yellow'],
        '0,1'
    );
    $question->setMultiselect(true);

    $colors = $helper->ask($input, $output, $question);
    $output->writeln('You have just selected: ' . implode(', ', $colors));
    
    return Command::SUCCESS;
}

Now, when the user enters 1,2, the result will be: You have just selected: blue, yellow.

ここで、ユーザーが 1,2 を入力すると、結果は次のようになります: You have just selected: blue, yellow.

If the user does not enter anything, the result will be: You have just selected: red, blue.

ユーザーが何も入力しない場合、結果は次のようになります。You have just selected: red, blue.

Autocompletion

You can also specify an array of potential answers for a given question. These will be autocompleted as the user types:

特定の質問に対する潜在的な回答の配列を指定することもできます。これらは、ユーザーが入力するとオートコンプリートされます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Symfony\Component\Console\Question\Question;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    // ...
    $helper = $this->getHelper('question');

    $bundles = ['AcmeDemoBundle', 'AcmeBlogBundle', 'AcmeStoreBundle'];
    $question = new Question('Please enter the name of a bundle', 'FooBundle');
    $question->setAutocompleterValues($bundles);

    $bundleName = $helper->ask($input, $output, $question);
    
    // ... do something with the bundleName
    
    return Command::SUCCESS;
}

In more complex use cases, it may be necessary to generate suggestions on the fly, for instance if you wish to autocomplete a file path. In that case, you can provide a callback function to dynamically generate suggestions:

より複雑な使用例では、たとえばファイル パスをオートコンプリートしたい場合など、その場で提案を生成する必要がある場合があります。その場合、コールバック関数を提供して、提案を動的に生成できます。
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
use Symfony\Component\Console\Question\Question;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    $helper = $this->getHelper('question');

    // This function is called whenever the input changes and new
    // suggestions are needed.
    $callback = function (string $userInput): array {
        // Strip any characters from the last slash to the end of the string
        // to keep only the last directory and generate suggestions for it
        $inputPath = preg_replace('%(/|^)[^/]*$%', '$1', $userInput);
        $inputPath = '' === $inputPath ? '.' : $inputPath;

        // CAUTION - this example code allows unrestricted access to the
        // entire filesystem. In real applications, restrict the directories
        // where files and dirs can be found
        $foundFilesAndDirs = @scandir($inputPath) ?: [];

        return array_map(function ($dirOrFile) use ($inputPath) {
            return $inputPath.$dirOrFile;
        }, $foundFilesAndDirs);
    };

    $question = new Question('Please provide the full path of a file to parse');
    $question->setAutocompleterCallback($callback);

    $filePath = $helper->ask($input, $output, $question);
    
    // ... do something with the filePath
    
    return Command::SUCCESS;
}

Do not Trim the Answer

You can also specify if you want to not trim the answer by setting it directly with setTrimmable():

setTrimmable() で直接設定することにより、回答をトリミングしないかどうかを指定することもできます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Symfony\Component\Console\Question\Question;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    // ...
    $helper = $this->getHelper('question');

    $question = new Question('What is the name of the child?');
    $question->setTrimmable(false);
    // if the users inputs 'elsa ' it will not be trimmed and you will get 'elsa ' as value
    $name = $helper->ask($input, $output, $question);
    
    // ... do something with the name
    
    return Command::SUCCESS;
}

Accept Multiline Answers

By default, the question helper stops reading user input when it receives a newline character (i.e., when the user hits ENTER once). However, you may specify that the response to a question should allow multiline answers by passing true to setMultiline():

デフォルトでは、質問ヘルパーは、改行文字を受け取ると (つまり、ユーザーが Enter キーを 1 回押すと)、ユーザー入力の読み取りを停止します。ただし、setMultiline() に true を渡すことで、質問への応答で複数行の回答を許可するように指定できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Symfony\Component\Console\Question\Question;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    // ...
    $helper = $this->getHelper('question');

    $question = new Question('How do you solve world peace?');
    $question->setMultiline(true);

    $answer = $helper->ask($input, $output, $question);
    
    // ... do something with the answer
    
    return Command::SUCCESS;
}

Multiline questions stop reading user input after receiving an end-of-transmission control character (Ctrl-D on Unix systems or Ctrl-Z on Windows).

複数行の質問は、送信終了制御文字 (Unix システムでは Ctrl-D、Windows では Ctrl-Z) を受け取ると、ユーザー入力の読み取りを停止します。

Hiding the User's Response

You can also ask a question and hide the response. This is particularly convenient for passwords:

質問をして、回答を非表示にすることもできます。これは、パスワードの場合に特に便利です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Symfony\Component\Console\Question\Question;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    // ...
    $helper = $this->getHelper('question');

    $question = new Question('What is the database password?');
    $question->setHidden(true);
    $question->setHiddenFallback(false);

    $password = $helper->ask($input, $output, $question);
    
    // ... do something with the password
    
    return Command::SUCCESS;
}

Caution

注意

When you ask for a hidden response, Symfony will use either a binary, change stty mode or use another trick to hide the response. If none is available, it will fallback and allow the response to be visible unless you set this behavior to false using setHiddenFallback() like in the example above. In this case, a RuntimeException would be thrown.

非表示の応答を要求すると、Symfony はバイナリ、changestty モード、または別のトリックを使用して応答を非表示にします。何も利用できない場合は、上記の例のように setHiddenFallback() を使用してこの動作を false に設定しない限り、フォールバックして応答を表示できるようにします。この場合、RuntimeException がスローされます。

Note

ノート

The stty command is used to get and set properties of the command line (such as getting the number of rows and columns or hiding the input text). On Windows systems, this stty command may generate gibberish output and mangle the input text. If that's your case, disable it with this command:

stty コマンドは、コマンド ラインのプロパティを取得および設定するために使用されます (行数と列数の取得、入力テキストの非表示など)。Windows システムでは、この stty コマンドは意味不明な出力を生成し、入力テキストを台無しにすることがあります。その場合は、次のコマンドで無効にします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Question\ChoiceQuestion;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    // ...
    $helper = $this->getHelper('question');
    QuestionHelper::disableStty();

    // ...
    
    return Command::SUCCESS;
}

Normalizing the Answer

Before validating the answer, you can "normalize" it to fix minor errors or tweak it as needed. For instance, in a previous example you asked for the bundle name. In case the user adds white spaces around the name by mistake, you can trim the name before validating it. To do so, configure a normalizer using the setNormalizer() method:

回答を検証する前に、「正規化」して小さなエラーを修正したり、必要に応じて調整したりできます。たとえば、前の例では、バンドル名を要求しました。ユーザーが誤って名前の周りに空白を追加した場合は、検証する前に名前を削除できます。これを行うには、setNormalizer() メソッドを使用してノーマライザーを構成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use Symfony\Component\Console\Question\Question;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    // ...
    $helper = $this->getHelper('question');

    $question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle');
    $question->setNormalizer(function ($value) {
        // $value can be null here
        return $value ? trim($value) : '';
    });

    $bundleName = $helper->ask($input, $output, $question);
    
    // ... do something with the bundleName
    
    return Command::SUCCESS;
}

Caution

注意

The normalizer is called first and the returned value is used as the input of the validator. If the answer is invalid, don't throw exceptions in the normalizer and let the validator handle those errors.

ノーマライザーが最初に呼び出され、戻り値がバリデーターの入力として使用されます。答えが無効な場合は、ノーマライザーで例外をスローせず、バリデーターにそれらのエラーを処理させます。

Validating the Answer

You can even validate the answer. For instance, in a previous example you asked for the bundle name. Following the Symfony naming conventions, it should be suffixed with Bundle. You can validate that by using the setValidator() method:

答えを検証することもできます。たとえば、前の例では、バンドル名を尋ねました。 Symfony の命名規則に従って、末尾に Bundle を付ける必要があります。 setValidator() メソッドを使用して検証できます。
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\Question\Question;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    // ...
    $helper = $this->getHelper('question');

    $question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle');
    $question->setValidator(function ($answer) {
        if (!is_string($answer) || 'Bundle' !== substr($answer, -6)) {
            throw new \RuntimeException(
                'The name of the bundle should be suffixed with \'Bundle\''
            );
        }

        return $answer;
    });
    $question->setMaxAttempts(2);

    $bundleName = $helper->ask($input, $output, $question);
    
    // ... do something with the bundleName
    
    return Command::SUCCESS;
}

The $validator is a callback which handles the validation. It should throw an exception if there is something wrong. The exception message is displayed in the console, so it is a good practice to put some useful information in it. The callback function should also return the value of the user's input if the validation was successful.

$validator は、検証を処理するコールバックです。何か問題がある場合は、例外をスローする必要があります。例外メッセージはコンソールに表示されるので、有用な情報をいくつか入れることをお勧めします。検証が成功した場合、コールバック関数はユーザーの入力の値も返す必要があります。

You can set the max number of times to ask with the setMaxAttempts() method. If you reach this max number it will use the default value. Using null means the number of attempts is infinite. The user will be asked as long as they provide an invalid answer and will only be able to proceed if their input is valid.

setMaxAttempts() メソッドで尋ねる最大回数を設定できます。この最大回数に達すると、デフォルト値が使用されます。 null を使用すると、試行回数は無限になります。ユーザーは、無効な回答を提供する限り尋ねられ、入力が有効な場合にのみ続行できます。

Tip

ヒント

You can even use the Validator component to validate the input by using the createCallable() method:

createCallable() メソッドを使用して Validator コンポーネントを使用して入力を検証することもできます。
1
2
3
4
5
6
7
8
9
use Symfony\Component\Validator\Constraints\Regex;
use Symfony\Component\Validator\Validation;

$question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle');
$validation = Validation::createCallable(new Regex([
    'pattern' => '/^[a-zA-Z]+Bundle$/',
    'message' => 'The name of the bundle should be suffixed with \'Bundle\'',
]));
$question->setValidator($validation);

Validating a Hidden Response

You can also use a validator with a hidden question:

非表示の質問でバリデーターを使用することもできます。
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
use Symfony\Component\Console\Question\Question;

// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
    // ...
    $helper = $this->getHelper('question');

    $question = new Question('Please enter your password');
    $question->setNormalizer(function ($value) {
        return $value ?? '';
    });
    $question->setValidator(function ($value) {
        if ('' === trim($value)) {
            throw new \Exception('The password cannot be empty');
        }

        return $value;
    });
    $question->setHidden(true);
    $question->setMaxAttempts(20);

    $password = $helper->ask($input, $output, $question);
    
    // ... do something with the password
    
    return Command::SUCCESS;
}

Testing a Command that Expects Input

If you want to write a unit test for a command which expects some kind of input from the command line, you need to set the inputs that the command expects:

コマンド ラインからの何らかの入力を期待するコマンドの単体テストを作成する場合は、コマンドが期待する入力を設定する必要があります。
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\Tester\CommandTester;

// ...
public function testExecute()
{
    // ...
    $commandTester = new CommandTester($command);

    // Equals to a user inputting "Test" and hitting ENTER
    $commandTester->setInputs(['Test']);

    // Equals to a user inputting "This", "That" and hitting ENTER
    // This can be used for answering two separated questions for instance
    $commandTester->setInputs(['This', 'That']);

    // For simulating a positive answer to a confirmation question, adding an
    // additional input saying "yes" will work
    $commandTester->setInputs(['yes']);

    $commandTester->execute(['command' => $command->getName()]);

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

By calling setInputs(), you imitate what the console would do internally with all user input through the CLI. This method takes an array as only argument with, for each input that the command expects, a string representing what the user would have typed. This way you can test any user interaction (even complex ones) by passing the appropriate inputs.

setInputs() を呼び出すことにより、CLI を介したすべてのユーザー入力に対してコンソールが内部で行うことを模倣します。このメソッドは、コマンドが期待する各入力に対して、ユーザーが入力したであろう内容を表す文字列を引数として配列を取ります。このようにして、適切な入力を渡すことで、あらゆるユーザー インタラクション (複雑なものであっても) をテストできます。

Note

ノート

The CommandTester automatically simulates a user hitting ENTER after each input, no need for passing an additional input.

CommandTester は、各入力後に Enter キーを押すユーザーを自動的にシミュレートします。追加の入力を渡す必要はありません。

Caution

注意

On Windows systems Symfony uses a special binary to implement hidden questions. This means that those questions don't use the default Input console object and therefore you can't test them on Windows.

Windows システムでは、symfony は特別なバイナリを使用して隠し質問を実装します。これは、これらの質問がデフォルトの Inputconsole オブジェクトを使用しないため、Windows でテストできないことを意味します。