bug #20377 [Console] Fix infinite loop on missing input (chalasr)

This PR was merged into the 2.7 branch.

Discussion
----------

[Console] Fix infinite loop on missing input

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #20277
| License       | MIT
| Doc PR        | n/a

This fixes the infinite loop occurring when no input is provided for a question which has a validator and no max attempts (`null`), e.g. when using `SymfonyStyle::ask()` which automatically adds a validator.

Commits
-------

e64de1e [Console] Fix infinite loop on missing input
This commit is contained in:
Fabien Potencier 2016-11-04 08:09:28 -07:00
commit 74e65e1631
4 changed files with 68 additions and 3 deletions

View File

@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Exception;
/**
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
class RuntimeException extends \RuntimeException
{
}

View File

@ -17,6 +17,7 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Exception\RuntimeException;
/**
* The QuestionHelper class provides helpers to interact with the user.
@ -134,7 +135,7 @@ class QuestionHelper extends Helper
if (false === $ret) {
$ret = fgets($inputStream, 4096);
if (false === $ret) {
throw new \RuntimeException('Aborted');
throw new RuntimeException('Aborted');
}
$ret = trim($ret);
}
@ -354,7 +355,7 @@ class QuestionHelper extends Helper
shell_exec(sprintf('stty %s', $sttyMode));
if (false === $value) {
throw new \RuntimeException('Aborted');
throw new RuntimeException('Aborted');
}
$value = trim($value);
@ -372,7 +373,7 @@ class QuestionHelper extends Helper
return $value;
}
throw new \RuntimeException('Unable to hide the response.');
throw new RuntimeException('Unable to hide the response.');
}
/**
@ -397,6 +398,8 @@ class QuestionHelper extends Helper
try {
return call_user_func($question->getValidator(), $interviewer());
} catch (RuntimeException $e) {
throw $e;
} catch (\Exception $error) {
}
}

View File

@ -402,6 +402,37 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase
$dialog->ask($this->createInputInterfaceMock(), $output, $question);
}
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
* @expectedExceptionMessage Aborted
*/
public function testAskThrowsExceptionOnMissingInput()
{
$dialog = new QuestionHelper();
$dialog->setInputStream($this->getInputStream(''));
$dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), new Question('What\'s your name?'));
}
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
* @expectedExceptionMessage Aborted
*/
public function testAskThrowsExceptionOnMissingInputWithValidator()
{
$dialog = new QuestionHelper();
$dialog->setInputStream($this->getInputStream(''));
$question = new Question('What\'s your name?');
$question->setValidator(function () {
if (!$value) {
throw new \Exception('A value is required.');
}
});
$dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
}
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);

View File

@ -101,6 +101,18 @@ class SymfonyQuestionHelperTest extends \PHPUnit_Framework_TestCase
$this->assertOutputContains('Do you want a \?', $output);
}
/**
* @expectedException \Symfony\Component\Console\Exception\RuntimeException
* @expectedExceptionMessage Aborted
*/
public function testAskThrowsExceptionOnMissingInput()
{
$dialog = new SymfonyQuestionHelper();
$dialog->setInputStream($this->getInputStream(''));
$dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), new Question('What\'s your name?'));
}
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);