ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# 如何使用提问 [TOC] ## 要求用户确认 假设你希望在一个方法被执行之前先行确认。添加以下代码到你的命令中: ``` <?php namespace app\console; use think\console\Command; use think\console\Output; use think\console\Input; class QuestionCommand extends Command { protected function configure() { $this // 命令的名字("think" 后面的部分) ->setName('demo:question') ->setDescription('Test question'); } public function execute(Input $input, Output $output) { $question = $output->confirm($input, 'Continue with this action?', false); if (!$question) { return; } } } ``` 本例中,用户会被问到 "Continue with this action?"。如果用户回答 `yes` 或者 `y` 开头的字符串 它就返回 `true`,如果答案是 `no` 或者 `n` 开头的字符串的话,它就返回 `false`。 `confirm()` 的第三个参数,是当用户不键入任何有效input时,返回的默认值。如果没有提供第三个参数, true 会被取用。 现在,你可以传入用户名到命令中: ```bash $ php think demo:question Continue with this action? (yes/no) [no]: > yes ``` 如果我们想输入其他的字符(如:`j`) 也表示 `yes` 的意思,该怎么处理呢? 前面的示例调用的是 Output 中的方法, 我们可以参考其代码: ```php // ... public function confirm(Input $input, $question, $default = true) { return $this->askQuestion($input, new Confirmation($question, $default)); } // ... protected function askQuestion(Input $input, Question $question) { $ask = new Ask($input, $this, $question); $answer = $ask->run(); if ($input->isInteractive()) { $this->newLine(); } return $answer; } // ... ``` 我们再看下类 `\think\console\output\question\Confirmation` 的构造方法 `__construct()` ``` // ... public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i') { // ... } ``` 我们可以看到构造器的第三个参数中自定义一个正则表达式,用于判断答案是否是 "yes"的意思(默认的正则表达式是 `/^y/i`)。 我们将上面的示例代码进行改造: ```php <?php namespace app\console; use think\console\Command; use think\console\Output; use think\console\Input; use think\console\output\Ask; use think\console\output\question\Confirmation; class QuestionCommand extends Command { protected function configure() { $this // 命令的名字("think" 后面的部分) ->setName('demo:question') ->setDescription('Test question'); } public function execute(Input $input, Output $output) { $question = new Confirmation('Continue with this action?', false, '/^(y|j)/i'); $ask = new Ask($input, $output, $question); $answer = $ask->run(); // if ($input->isInteractive()) { // // 输出空行 // $output->newLine(); // } if (!$answer) { $output->writeln('false'); return; } $output->writeln('true'); } } ``` 测试一下成果: ```bash php think demo:question Continue with this action? (yes/no) [no]: > j true ``` ## 询问用户信息 你也可以用超过一个简单的 `yes/no` 的答案来向用户提问。例如,如果你想要知道 behavior 的名称,可以把下面代码添加到你的命令中: ```php <?php namespace app\console; use think\console\Command; use think\console\Output; use think\console\Input; use think\console\output\Ask; use think\console\output\question\Confirmation; class QuestionCommand extends Command { protected function configure() { $this // 命令的名字("think" 后面的部分) ->setName('demo:question') ->setDescription('Test question'); } public function execute(Input $input, Output $output) { $behavior = $output->ask($input, 'Please enter the name of the behavior', 'initBehavior'); $output->writeln($behavior); } } ``` 用户会被问 "Please enter the name of the behavior"。我们可以输入一些会被 ask() 方法返回的名称。如果用户留空,默认值 (此处是 initBehavior) 会被返回。 ## 让用户从答案列表中选择 如果你预定义了一组答案让用户从中选择,你可以使用 `choice`,它确保用户只能从预定义列表中输入有效字符串: ```php <?php namespace app\console; use think\console\Command; use think\console\Output; use think\console\Input; class QuestionCommand extends Command { protected function configure() { $this // 命令的名字("think" 后面的部分) ->setName('demo:question') ->setDescription('Test question'); } public function execute(Input $input, Output $output) { $color = $output->choice( $input, 'Please select your favorite color (defaults to red)', ['red', 'blue', 'yellow'], 'red' ); $output->writeln('You have just selected: ' . $color); } } ``` 现在,我们可以传入颜色到命令中: ```bash $ php think demo:question Please select your favorite color (defaults to red) [red]: [0] red [1] blue [2] yellow > 0 red $ php think demo:question Please select your favorite color (defaults to red) [red]: [0] red [1] blue [2] yellow > 10 Value "10" is invalid Please select your favorite color (defaults to red) [red]: [0] red [1] blue [2] yellow > ``` 上面的示例可以看到,当我们输入的数据超出范围后,出现错误并让我们重新输入, 但错误提示不怎么友好,因此我们需要对其进行改造: 我们看下 `choice()` 代码: ```php // ... public function choice(Input $input, $question, array $choices, $default = null) { if (null !== $default) { $values = array_flip($choices); $default = $values[$default]; } return $this->askQuestion($input, new Choice($question, $choices, $default)); } protected function askQuestion(Input $input, Question $question) { $ask = new Ask($input, $this, $question); $answer = $ask->run(); if ($input->isInteractive()) { $this->newLine(); } return $answer; } // ... ``` 我们可以看到,choice 中使用了 `\think\console\output\question\Choice`,分析代码可以看到,修改错误提示的方法是: ```php // ... /** * 设置错误提示信息 * @param string $errorMessage * @return self */ public function setErrorMessage($errorMessage) { // ... } // ... ``` 我们将上面的示例代码进行改造: ```php <?php namespace app\console; use think\console\Command; use think\console\Output; use think\console\Input; use think\console\output\Ask; use think\console\output\question\Choice; class QuestionCommand extends Command { protected function configure() { $this // 命令的名字("think" 后面的部分) ->setName('demo:question') ->setDescription('Test question'); } public function execute(Input $input, Output $output) { $question = new Choice( 'Please select your favorite color (defaults to red)', ['red', 'blue', 'yellow'], 0 ); $question->setErrorMessage('Color %s is invalid.'); $ask = new Ask($input, $output, $question); $color = $ask->run(); $output->writeln('You have just selected: '.$color); } } ``` 现在,你可以传入颜色到命令中: ```bash $ php think demo:question Please select your favorite color (defaults to red) [red]: [0] red [1] blue [2] yellow > 0 red $ php think demo:question Please select your favorite color (defaults to red) [red]: [0] red [1] blue [2] yellow > 10 Color 10 is invalid Please select your favorite color (defaults to red) [red]: [0] red [1] blue [2] yellow > ``` 默认被选中的选项由构造器的第三个参数提供。默认是 `null`,代表没有默认的选项。 如果用户输入了无效字符串,会显示一个错误信息,用户会被要求再一次提供答案,直到他们输入一个有效字符串,或是达到了尝试上限为止。默认的最大尝试次数是 `null`,代表可以无限次尝试。你可以使用 setErrorMessage() 定义自己的错误信息。 ### 多选 有时,可以给出多个答案。 `Choice` 使用逗号分隔的值,提供了此项功能。默认是禁用的,开启它可使用 `setMultiselect()`: > `\think\Console` 中没有提供该方法 现在,你可以传入颜色到命令中: ```bash $ php think demo:question Please select your favorite color (defaults to red) [red, blue]: [0] red [1] blue [2] yellow > 1,2 You have just selected: blue,yellow ``` 如果用户不输入任何内容,结果是: `You have just selected: red, blue`。 ## 自动完成 对于给定的问题,你也可以提供一个默认的答案数组。它们将根据用户的操作而自动完成: ```php <?php namespace app\console; use think\console\Command; use think\console\Output; use think\console\Input; use think\console\output\Ask; use think\console\output\Question; use think\console\output\question\Choice; class QuestionCommand extends Command { protected function configure() { $this // 命令的名字("think" 后面的部分) ->setName('demo:question') ->setDescription('Test question'); } public function execute(Input $input, Output $output) { $question = new Question('Please enter the name of a color', 'pink'); $question->setAutocompleterValues(['red', 'blue', 'yellow']); $ask = new Ask($input, $output, $question); $color = $ask->run(); $output->writeln('You have just selected: ' . $color); } } ``` ## 隐藏用户输入 你也可以在问问题时隐藏输入。这对密码来说极为方便: ```php <?php namespace app\console; use think\console\Command; use think\console\Output; use think\console\Input; use think\console\output\Ask; use think\console\output\Question; use think\console\output\question\Choice; class QuestionCommand extends Command { protected function configure() { $this // 命令的名字("think" 后面的部分) ->setName('demo:question') ->setDescription('Test question'); } public function execute(Input $input, Output $output) { $password = $output->askHidden($input, 'What is the database password?'); $output->writeln($password); } } ``` 现在,你可以传入登录密码到命令中: ```bash $ php think demo:question What is the login password?: > 123456 ``` ## 验证答案 你甚至可以验证答案。例如,前面例子中你曾询问过 behavior 名称。假设我们设置了后缀 Behavior,那么我们可以使用 `setValidator()` 方法 来验证它: ```php <?php namespace app\console; use think\console\Command; use think\console\Output; use think\console\Input; class QuestionCommand extends Command { protected function configure() { $this // 命令的名字("think" 后面的部分) ->setName('demo:question') ->setDescription('Test question'); } public function execute(Input $input, Output $output) { $name = $output->ask($input, 'Please enter the name of the behavior', 'demoBehavior', function ($answer) { if ('Behavior' !== substr($answer, -8)) { throw new \RuntimeException( 'The name of the behavior should be suffixed with \'Behavior\'' ); } return $answer; }); $output->writeln($name); } } ``` `$validator` 是一个 `callback` ,专门处理验证。它在有错误发生时应抛出一个异常。异常信息会被显示在控制台中,所以在里面放入一些有用的信息是一个很好的实践。回调函数在验证通过时,应该返回用户的`input`。 如果我们想设置最大提问次数该怎么办呢? 你可以用 `setMaxAttempts()` 方法来设置(验证失败时的)最大的提问次数。如果达到最大值,它将使用默认值。使用 `null` 代表可以无限次尝试回答(直到验证通过)。用户将被始终提问,直到他们提供了有效答案为止,也只有输入有效时命令才会继续执行。 ```php <?php namespace app\console; use think\console\Command; use think\console\Output; use think\console\Input; use think\console\output\Ask; use think\console\output\Question; class QuestionCommand extends Command { protected function configure() { $this // 命令的名字("think" 后面的部分) ->setName('demo:question') ->setDescription('Test question'); } public function execute(Input $input, Output $output) { // ... $question = new Question('Please enter the name of the behavior', 'demoBehavior'); $question->setValidator(function ($answer) { if ('Behavior' !== substr($answer, -8)) { throw new \RuntimeException( 'The name of the behavior should be suffixed with \'Behavior\'' ); } return $answer; }); // 设置最大提问数 $question->setMaxAttempts(2); $ask = new Ask($input, $output, $question); $name = $ask->run(); $output->writeln($name); } } ``` ## 验证一个隐藏的响应 你也可以在隐藏(答案输入)的提问中使用validator: ```php <?php namespace app\console; use think\console\Command; use think\console\Output; use think\console\Input; class QuestionCommand extends Command { protected function configure() { $this // 命令的名字("think" 后面的部分) ->setName('demo:question') ->setDescription('Test question'); } public function execute(Input $input, Output $output) { $name = $output->askHidden($input, 'Please enter your password', 'demoBehavior', function ($value) { if (trim($value) == '') { throw new \Exception('The password can not be empty'); } return $value; }); $output->writeln($name); } } ``` 或者使用下面的方式: ```php <?php namespace app\console; use think\console\Command; use think\console\Output; use think\console\Input; use think\console\output\Ask; use think\console\output\Question; class QuestionCommand extends Command { protected function configure() { $this // 命令的名字("think" 后面的部分) ->setName('demo:question') ->setDescription('Test question'); } public function execute(Input $input, Output $output) { // ... $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(2); $ask = new Ask($input, $output, $question); $name = $ask->run(); $output->writeln($name); } } ```