Skip to content

[RFC] add input to the arguments of dialog helper #8366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions src/Symfony/Component/Console/Helper/DialogHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;

Expand All @@ -28,6 +29,7 @@ class DialogHelper extends Helper
/**
* Asks the user to select a value.
*
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param array $choices List of choices to pick from
Expand All @@ -40,7 +42,7 @@ class DialogHelper extends Helper
*
* @throws \InvalidArgumentException
*/
public function select(OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false)
public function select(InputInterface $input, OutputInterface $output, $question, $choices, $default = null, $attempts = false, $errorMessage = 'Value "%s" is invalid', $multiselect = false)
{
$width = max(array_map('strlen', array_keys($choices)));

Expand All @@ -51,7 +53,7 @@ public function select(OutputInterface $output, $question, $choices, $default =

$output->writeln($messages);

$result = $this->askAndValidate($output, '> ', function ($picked) use ($choices, $errorMessage, $multiselect) {
$result = $this->askAndValidate($input, $output, '> ', function ($picked) use ($choices, $errorMessage, $multiselect) {
// Collapse all spaces.
$selectedChoices = str_replace(" ", "", $picked);

Expand Down Expand Up @@ -87,6 +89,7 @@ public function select(OutputInterface $output, $question, $choices, $default =
/**
* Asks a question to the user.
*
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param string $default The default answer if none is given by the user
Expand All @@ -96,10 +99,14 @@ public function select(OutputInterface $output, $question, $choices, $default =
*
* @throws \RuntimeException If there is no data to read in the input stream
*/
public function ask(OutputInterface $output, $question, $default = null, array $autocomplete = null)
public function ask(InputInterface $input, OutputInterface $output, $question, $default = null, array $autocomplete = null)
{
$output->write($question);

if (!$input->isInteractive()) {
return $default;
}

$inputStream = $this->inputStream ?: STDIN;

if (null === $autocomplete || !$this->hasSttyAvailable()) {
Expand Down Expand Up @@ -221,17 +228,18 @@ public function ask(OutputInterface $output, $question, $default = null, array $
*
* The question will be asked until the user answers by nothing, yes, or no.
*
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param Boolean $default The default answer if the user enters nothing
*
* @return Boolean true if the user has confirmed, false otherwise
*/
public function askConfirmation(OutputInterface $output, $question, $default = true)
public function askConfirmation(InputInterface $input, OutputInterface $output, $question, $default = true)
{
$answer = 'z';
while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) {
$answer = $this->ask($output, $question);
$answer = $this->ask($input, $output, $question);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely to be an infinite loop in non-interactive mode, as $this->ask() will return true, not the expected string being in array('y', 'n')

}

if (false === $default) {
Expand All @@ -252,7 +260,7 @@ public function askConfirmation(OutputInterface $output, $question, $default = t
*
* @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden
*/
public function askHiddenResponse(OutputInterface $output, $question, $fallback = true)
public function askHiddenResponse(InputInterface $input, OutputInterface $output, $question, $fallback = true)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
Expand Down Expand Up @@ -305,7 +313,7 @@ public function askHiddenResponse(OutputInterface $output, $question, $fallback
}

if ($fallback) {
return $this->ask($output, $question);
return $this->ask($input, $output, $question);
}

throw new \RuntimeException('Unable to hide the response');
Expand All @@ -318,6 +326,7 @@ public function askHiddenResponse(OutputInterface $output, $question, $fallback
* validated data when the data is valid and throw an exception
* otherwise.
*
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param callable $validator A PHP callback
Expand All @@ -329,12 +338,12 @@ public function askHiddenResponse(OutputInterface $output, $question, $fallback
*
* @throws \Exception When any of the validators return an error
*/
public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null, array $autocomplete = null)
public function askAndValidate(InputInterface $input, OutputInterface $output, $question, $validator, $attempts = false, $default = null, array $autocomplete = null)
{
$that = $this;

$interviewer = function() use ($output, $question, $default, $autocomplete, $that) {
return $that->ask($output, $question, $default, $autocomplete);
$interviewer = function() use ($input, $output, $question, $default, $autocomplete, $that) {
return $that->ask($input, $output, $question, $default, $autocomplete);
};

return $this->validateAttempts($interviewer, $output, $validator, $attempts);
Expand All @@ -347,6 +356,7 @@ public function askAndValidate(OutputInterface $output, $question, $validator, $
* validated data when the data is valid and throw an exception
* otherwise.
*
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
* @param string|array $question The question to ask
* @param callable $validator A PHP callback
Expand All @@ -359,12 +369,12 @@ public function askAndValidate(OutputInterface $output, $question, $validator, $
* @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden
*
*/
public function askHiddenResponseAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $fallback = true)
public function askHiddenResponseAndValidate(InputInterface $input, OutputInterface $output, $question, $validator, $attempts = false, $fallback = true)
{
$that = $this;

$interviewer = function() use ($output, $question, $fallback, $that) {
return $that->askHiddenResponse($output, $question, $fallback);
$interviewer = function() use ($input, $output, $question, $fallback, $that) {
return $that->askHiddenResponse($input, $output, $question, $fallback);
};

return $this->validateAttempts($interviewer, $output, $validator, $attempts);
Expand Down
79 changes: 47 additions & 32 deletions src/Symfony/Component/Console/Tests/Helper/DialogHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Symfony\Component\Console\Helper\DialogHelper;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\StreamOutput;

class DialogHelperTest extends \PHPUnit_Framework_TestCase
Expand All @@ -26,38 +27,40 @@ public function testSelect()
$dialog->setHelperSet($helperSet);

$heroes = array('Superman', 'Batman', 'Spiderman');

$dialog->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n"));
$this->assertEquals('2', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '2'));
$this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes));
$this->assertEquals('1', $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes));
$this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', false));
$input = new ArrayInput(array());

$this->assertEquals('2', $dialog->select($input, $this->getOutputStream(), 'What is your favorite superhero?', $heroes, '2'));
$this->assertEquals('1', $dialog->select($input, $this->getOutputStream(), 'What is your favorite superhero?', $heroes));
$this->assertEquals('1', $dialog->select($input, $this->getOutputStream(), 'What is your favorite superhero?', $heroes));
$this->assertEquals('1', $dialog->select($input, $output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', false));

rewind($output->getStream());
$this->assertContains('Input "Fabien" is not a superhero!', stream_get_contents($output->getStream()));

try {
$this->assertEquals('1', $dialog->select($output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, 1));
$this->assertEquals('1', $dialog->select($input, $output = $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, 1));
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
}

$this->assertEquals(array('1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true));
$this->assertEquals(array('0', '2'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true));
$this->assertEquals(array('0', '2'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true));
$this->assertEquals(array('0', '1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, '0,1', false, 'Input "%s" is not a superhero!', true));
$this->assertEquals(array('0', '1'), $dialog->select($this->getOutputStream(), 'What is your favorite superhero?', $heroes, ' 0 , 1 ', false, 'Input "%s" is not a superhero!', true));
$this->assertEquals(array('1'), $dialog->select($input, $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true));
$this->assertEquals(array('0', '2'), $dialog->select($input, $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true));
$this->assertEquals(array('0', '2'), $dialog->select($input, $this->getOutputStream(), 'What is your favorite superhero?', $heroes, null, false, 'Input "%s" is not a superhero!', true));
$this->assertEquals(array('0', '1'), $dialog->select($input, $this->getOutputStream(), 'What is your favorite superhero?', $heroes, '0,1', false, 'Input "%s" is not a superhero!', true));
$this->assertEquals(array('0', '1'), $dialog->select($input, $this->getOutputStream(), 'What is your favorite superhero?', $heroes, ' 0 , 1 ', false, 'Input "%s" is not a superhero!', true));
}

public function testAsk()
{
$dialog = new DialogHelper();

$dialog->setInputStream($this->getInputStream("\n8AM\n"));
$input = new ArrayInput(array());

$this->assertEquals('2PM', $dialog->ask($this->getOutputStream(), 'What time is it?', '2PM'));
$this->assertEquals('8AM', $dialog->ask($output = $this->getOutputStream(), 'What time is it?', '2PM'));
$this->assertEquals('2PM', $dialog->ask($input, $this->getOutputStream(), 'What time is it?', '2PM'));
$this->assertEquals('8AM', $dialog->ask($input, $output = $this->getOutputStream(), 'What time is it?', '2PM'));

rewind($output->getStream());
$this->assertEquals('What time is it?', stream_get_contents($output->getStream()));
Expand All @@ -83,15 +86,16 @@ public function testAskWithAutocomplete()
$dialog->setInputStream($inputStream);

$bundles = array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle');

$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('AsseticBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('FrameworkBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('SecurityBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('FooBundleTest', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('AsseticBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('FooBundle', $dialog->ask($this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$input = new ArrayInput(array());

$this->assertEquals('AcmeDemoBundle', $dialog->ask($input, $this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('AsseticBundleTest', $dialog->ask($input, $this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('FrameworkBundle', $dialog->ask($input, $this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('SecurityBundle', $dialog->ask($input, $this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('FooBundleTest', $dialog->ask($input, $this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('AcmeDemoBundle', $dialog->ask($input, $this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('AsseticBundle', $dialog->ask($input, $this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
$this->assertEquals('FooBundle', $dialog->ask($input, $this->getOutputStream(), 'Please select a bundle', 'FrameworkBundle', $bundles));
}

public function testAskHiddenResponse()
Expand All @@ -103,25 +107,27 @@ public function testAskHiddenResponse()
$dialog = new DialogHelper();

$dialog->setInputStream($this->getInputStream("8AM\n"));
$input = new ArrayInput(array());

$this->assertEquals('8AM', $dialog->askHiddenResponse($this->getOutputStream(), 'What time is it?'));
$this->assertEquals('8AM', $dialog->askHiddenResponse($input, $this->getOutputStream(), 'What time is it?'));
}

public function testAskConfirmation()
{
$dialog = new DialogHelper();

$dialog->setInputStream($this->getInputStream("\n\n"));
$this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?'));
$this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false));
$input = new ArrayInput(array());
$this->assertTrue($dialog->askConfirmation($input, $this->getOutputStream(), 'Do you like French fries?'));
$this->assertFalse($dialog->askConfirmation($input, $this->getOutputStream(), 'Do you like French fries?', false));

$dialog->setInputStream($this->getInputStream("y\nyes\n"));
$this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false));
$this->assertTrue($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', false));
$this->assertTrue($dialog->askConfirmation($input, $this->getOutputStream(), 'Do you like French fries?', false));
$this->assertTrue($dialog->askConfirmation($input, $this->getOutputStream(), 'Do you like French fries?', false));

$dialog->setInputStream($this->getInputStream("n\nno\n"));
$this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true));
$this->assertFalse($dialog->askConfirmation($this->getOutputStream(), 'Do you like French fries?', true));
$this->assertFalse($dialog->askConfirmation($input, $this->getOutputStream(), 'Do you like French fries?', true));
$this->assertFalse($dialog->askConfirmation($input, $this->getOutputStream(), 'Do you like French fries?', true));
}

public function testAskAndValidate()
Expand All @@ -141,18 +147,27 @@ public function testAskAndValidate()
};

$dialog->setInputStream($this->getInputStream("\nblack\n"));
$this->assertEquals('white', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white'));
$this->assertEquals('black', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white'));
$input = new ArrayInput(array());
$this->assertEquals('white', $dialog->askAndValidate($input, $this->getOutputStream(), $question, $validator, 2, 'white'));
$this->assertEquals('black', $dialog->askAndValidate($input, $this->getOutputStream(), $question, $validator, 2, 'white'));

$dialog->setInputStream($this->getInputStream("green\nyellow\norange\n"));
try {
$this->assertEquals('white', $dialog->askAndValidate($this->getOutputStream(), $question, $validator, 2, 'white'));
$this->assertEquals('white', $dialog->askAndValidate($input, $this->getOutputStream(), $question, $validator, 2, 'white'));
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertEquals($error, $e->getMessage());
}
}

public function testNoInteraction()
{
$dialog = new DialogHelper();
$input = new ArrayInput(array());
$input->setInteractive(false);
$this->assertEquals('not yet', $dialog->ask($input, $this->getOutputStream(), 'Do you have a job?', 'not yet'));
}

protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
Expand Down