From 965b5b5f8da80becc9476b522e23ba0eb47b300a Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Sat, 11 Nov 2017 23:09:14 +0100 Subject: [PATCH] [Console] Fix traversable autocomplete values --- .../Console/Helper/QuestionHelper.php | 6 +-- .../Component/Console/Question/Question.php | 6 +-- .../Tests/Helper/QuestionHelperTest.php | 49 +++++++++++++++++++ 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 1f3e4ff14a..2034ce14a4 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -134,7 +134,7 @@ class QuestionHelper extends Helper $ret = trim($ret); } } else { - $ret = trim($this->autocomplete($output, $question, $inputStream)); + $ret = trim($this->autocomplete($output, $question, $inputStream, is_array($autocomplete) ? $autocomplete : iterator_to_array($autocomplete, false))); } $ret = strlen($ret) > 0 ? $ret : $question->getDefault(); @@ -190,12 +190,12 @@ class QuestionHelper extends Helper * @param OutputInterface $output * @param Question $question * @param resource $inputStream + * @param array $autocomplete * * @return string */ - private function autocomplete(OutputInterface $output, Question $question, $inputStream) + private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete) { - $autocomplete = $question->getAutocompleterValues(); $ret = ''; $i = 0; diff --git a/src/Symfony/Component/Console/Question/Question.php b/src/Symfony/Component/Console/Question/Question.php index 64c9205749..bece3e5975 100644 --- a/src/Symfony/Component/Console/Question/Question.php +++ b/src/Symfony/Component/Console/Question/Question.php @@ -137,10 +137,8 @@ class Question $values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values); } - if (null !== $values && !is_array($values)) { - if (!$values instanceof \Traversable || !$values instanceof \Countable) { - throw new \InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.'); - } + if (null !== $values && !is_array($values) && !$values instanceof \Traversable) { + throw new \InvalidArgumentException('Autocompleter values can be either an array, `null` or a `Traversable` object.'); } if ($this->hidden) { diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index 69a2256efa..3538cbc0b7 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -514,6 +514,40 @@ class QuestionHelperTest extends TestCase new ChoiceQuestion('Question', array(), 'irrelevant'); } + public function testTraversableAutocomplete() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // Acm + // AcsTest + // + // + // Test + // + // S + // F00oo + $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($inputStream); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new Question('Please select a bundle', 'FrameworkBundle'); + $question->setAutocompleterValues(new AutocompleteValues(array('irrelevant' => 'AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'))); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + protected function getInputStream($input) { $stream = fopen('php://memory', 'r+', false); @@ -545,3 +579,18 @@ class QuestionHelperTest extends TestCase return 0 === $exitcode; } } + +class AutocompleteValues implements \IteratorAggregate +{ + private $values; + + public function __construct(array $values) + { + $this->values = $values; + } + + public function getIterator() + { + return new \ArrayIterator($this->values); + } +}