企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持知识库和私有化部署方案 广告
# 如何使用提问 * [如何使用提问](https://www.kancloud.cn/uuling/tp5-console/366134#_0) * [要求用户确认](https://www.kancloud.cn/uuling/tp5-console/366134#_4) * [询问用户信息](https://www.kancloud.cn/uuling/tp5-console/366134#_146) * [让用户从答案列表中选择](https://www.kancloud.cn/uuling/tp5-console/366134#_183) * [多选](https://www.kancloud.cn/uuling/tp5-console/366134#_376) * [自动完成](https://www.kancloud.cn/uuling/tp5-console/366134#_397) * [隐藏用户输入](https://www.kancloud.cn/uuling/tp5-console/366134#_440) * [验证答案](https://www.kancloud.cn/uuling/tp5-console/366134#_488) * [验证一个隐藏的响应](https://www.kancloud.cn/uuling/tp5-console/366134#_581) ## 要求用户确认 假设你希望在一个方法被执行之前先行确认。添加以下代码到你的命令中: ~~~ <?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 会被取用。 现在,你可以传入用户名到命令中: ~~~ $ php think demo:question Continue with this action? (yes/no) [no]: > yes ~~~ 如果我们想输入其他的字符(如:`j`) 也表示`yes`的意思,该怎么处理呢? 前面的示例调用的是 Output 中的方法, 我们可以参考其代码: ~~~ // ... 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 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'); } } ~~~ 测试一下成果: ~~~ php think demo:question Continue with this action? (yes/no) [no]: > j true ~~~ ## 询问用户信息 你也可以用超过一个简单的`yes/no`的答案来向用户提问。例如,如果你想要知道 behavior 的名称,可以把下面代码添加到你的命令中: ~~~ <?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 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); } } ~~~ 现在,我们可以传入颜色到命令中: ~~~ $ 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()`代码: ~~~ // ... 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`,分析代码可以看到,修改错误提示的方法是: ~~~ // ... /** * 设置错误提示信息 * @param string $errorMessage * @return self */ public function setErrorMessage($errorMessage) { // ... } // ... ~~~ 我们将上面的示例代码进行改造: ~~~ <?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); } } ~~~ 现在,你可以传入颜色到命令中: ~~~ $ 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`中没有提供该方法 现在,你可以传入颜色到命令中: ~~~ $ 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 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 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); } } ~~~ 现在,你可以传入登录密码到命令中: ~~~ $ php think demo:question What is the login password?: > 123456 ~~~ ## 验证答案 你甚至可以验证答案。例如,前面例子中你曾询问过 behavior 名称。假设我们设置了后缀 Behavior,那么我们可以使用`setValidator()`方法 来验证它: ~~~ <?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 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 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 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); } } ~~~