支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
QuestionHelper
提供的方法用于向用户询问更多信息。它已经包括在默认的助手集中,你可以调用 getHelperSet()
来获取它:
1 | $helper = $this->getHelper('question'); |
提问助手有一个独立的方法 ask()
,需要一个
InputInterface
实例作为第一个参数,一个 OutputInterface
实例作为第二个参数, 以及一个 Question
作为最后一个参数:
假设你希望在一个action被执行之前先行确认。添加以下代码到你的命令中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // ...
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)
{
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('Continue with this action?', false);
if (!$helper->ask($input, $output, $question)) {
return;
}
}
} |
本例中,用户会被问到 "Continue with this action?"。如果用户回答 y
它就返回 true
,或者 false
,如果答案是 n
的话。__construct()
的第二个参数,是当用户不键入任何有效input时,返回的默认值。如果没有提供第二个参数, true
会被取用。
你可以在构造器的第三个参数中自定义一个正则表达式,用于判断答案是否是 "yes"的意思。例如,允许任何以 y
或 j
开头的(input),你可以这样设置:
1 2 3 4 5 | $question = new ConfirmationQuestion(
'Continue with this action?',
false,
'/^(y|j)/i'
); |
默认的regex是 /^y/i
。
你也可以用超过一个简单的 yes/no 的答案来向用户提问。例如,如果你想要知道bundle的名称,可以把下面代码添加到你的命令中:
1 2 3 4 5 6 7 8 9 10 | use Symfony\Component\Console\Question\Question;
// ...
public function execute(InputInterface $input, OutputInterface $output)
{
// ...
$question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle');
$bundle = $helper->ask($input, $output, $question);
} |
用户会被问 "Please enter the name of the bundle"。他们可以键入一些会被 ask()
方法返回的名称。如果用户留空,默认值 (此处是AcmeDemoBundle
) 会被返回。
如果你预定义了一组答案让用户从中选择,你可以使用 ChoiceQuestion
,它确保用户只能从预定义列表中输入有效字符串:
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)
{
// ...
$helper = $this->getHelper('question');
$question = new ChoiceQuestion(
'Please select your favorite color (defaults to red)',
array('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 / 进行颜色操作
} |
默认被选中的选项由构造器的第三个参数提供。默认是 null
,代表没有默认的选项。
如果用户输入了无效字符串,会显示一个错误信息,用户会被要求再一次提供答案,直到他们输入一个有效字符串,或是达到了尝试上限为止。默认的最大尝试次数是 null
,代表可以无限次尝试。你可以使用 setErrorMessage()
定义自己的错误信息。
有时,可以给出多个答案。 ChoiceQuestion
使用逗号分隔的值,提供了此项功能。默认是禁用的,开启它可使用 setMultiselect()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | use Symfony\Component\Console\Question\ChoiceQuestion;
// ...
public function execute(InputInterface $input, OutputInterface $output)
{
// ...
$helper = $this->getHelper('question');
$question = new ChoiceQuestion(
'Please select your favorite colors (defaults to red and blue)',
array('red', 'blue', 'yellow'),
'0,1'
);
$question->setMultiselect(true);
$colors = $helper->ask($input, $output, $question);
$output->writeln('You have just selected: ' . implode(', ', $colors));
} |
现在,当用户键入 1,2
,结果会是: You have just selected: blue, yellow
。
如果用户不键入任何内容,结果是: You have just selected: red, blue
。
对于给定的问题,你也可以提供一个潜在答案的数组。它们将根据用户的敲击而自动完成:
1 2 3 4 5 6 7 8 9 10 11 12 | use Symfony\Component\Console\Question\Question;
// ...
public function execute(InputInterface $input, OutputInterface $output)
{
// ...
$bundles = array('AcmeDemoBundle', 'AcmeBlogBundle', 'AcmeStoreBundle');
$question = new Question('Please enter the name of a bundle', 'FooBundle');
$question->setAutocompleterValues($bundles);
$name = $helper->ask($input, $output, $question);
} |
你也可以在问问题时隐藏响应。这对密码来说极为方便:
1 2 3 4 5 6 7 8 9 10 11 12 | use Symfony\Component\Console\Question\Question;
// ...
public function execute(InputInterface $input, OutputInterface $output)
{
// ...
$question = new Question('What is the database password?');
$question->setHidden(true);
$question->setHiddenFallback(false);
$password = $helper->ask($input, $output, $question);
} |
当你提问并隐藏响应时,Symofny将使用一个二进制的change stty mode或是使用另一种手段来隐藏之。如果都不可用,它就回滚为响应可见,除非你像上例那样,使用 setHiddenFallback()
来将此行为设置成 false
。本例中,一个 RuntimeException
异常会被抛出。
你甚至可以验证答案。例如,前面例子中你曾询问过bundle名称。根据Symfony的命名约定,它应该被施以 Bundle
后缀,你可以使用 setValidator()
方法来验证它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | use Symfony\Component\Console\Question\Question;
// ...
public function execute(InputInterface $input, OutputInterface $output)
{
// ...
$question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle');
$question->setValidator(function ($answer) {
if ('Bundle' !== substr($answer, -6)) {
throw new \RuntimeException(
'The name of the bundle should be suffixed with \'Bundle\''
);
}
return $answer;
});
$question->setMaxAttempts(2);
$name = $helper->ask($input, $output, $question);
} |
$validator
是一个callback,专门处理验证。它在有错误发生时应抛出一个异常。异常信息会被显示在控制台中,所以在里面放入一些有用的信息是一个很好的实践。回调函数在验证通过时,应该返回用户的input。
你可以用 setMaxAttempts()
方法来设置(验证失败时的)最大的提问次数。如果达到最大值,它将使用默认值。使用 null
代表可以无限次尝试回答(直到验证通过)。用户将被始终提问,直到他们提供了有效答案为止,也只有输入有效时命令才会继续执行。
你也可以在隐藏(答案输入)的提问中使用validator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | use Symfony\Component\Console\Question\Question;
// ...
public function execute(InputInterface $input, OutputInterface $output)
{
// ...
$helper = $this->getHelper('question');
$question = new Question('Please enter your password');
$question->setValidator(function ($value) {
if (trim($value) == '') {
throw new \Exception('The password can not be empty');
}
return $value;
});
$question->setHidden(true);
$question->setMaxAttempts(20);
$password = $helper->ask($input, $output, $question);
} |
如果你需要对一个命令写单元测试,而此命令又预期接到从命令行中得到的某些类型的输入,你需要设置helper input stream:
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 | use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Tester\CommandTester;
// ...
public function testExecute()
{
// ...
$commandTester = new CommandTester($command);
$helper = $command->getHelper('question');
$helper->setInputStream($this->getInputStream("Test\n"));
// Equals to a user inputting "Test" and hitting ENTER
// If you need to enter a confirmation, "yes\n" will work
// 这等同于用户输入 “Test” 并敲击ENTER
// 若你需要在输入时确认,可以使用 "yes\n"
$commandTester->execute(array('command' => $command->getName()));
// $this->assertRegExp('/.../', $commandTester->getDisplay());
}
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
fputs($stream, $input);
rewind($stream);
return $stream;
} |
通过设置 QuestionHelper
的输入流(input stream),你把用户通过CLI完成的输入,用于模拟命令行的内部运作。通过传入一个合适的input stream,这种方式可以测试任何的用户操作(甚至很复杂的)。
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。