Skip to content

Commit 6cef233

Browse files
committed
[Console] allow answer to be trimmed by adding a flag
1 parent 5dba412 commit 6cef233

File tree

4 files changed

+101
-9
lines changed

4 files changed

+101
-9
lines changed

src/Symfony/Component/Console/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
4.4.0
5+
-----
6+
7+
* added `Question::setTrimmable` default to true to allow the anwser to be trimmed or not
8+
49
4.3.0
510
-----
611

src/Symfony/Component/Console/Helper/QuestionHelper.php

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public function ask(InputInterface $input, OutputInterface $output, Question $qu
6464

6565
$default = explode(',', $default);
6666
foreach ($default as $k => $v) {
67-
$v = trim($v);
67+
$v = $question->isTrimmable() ? trim($v) : $v;
6868
$default[$k] = isset($choices[$v]) ? $choices[$v] : $v;
6969
}
7070
}
@@ -121,7 +121,8 @@ private function doAsk(OutputInterface $output, Question $question)
121121
$ret = false;
122122
if ($question->isHidden()) {
123123
try {
124-
$ret = trim($this->getHiddenResponse($output, $inputStream));
124+
$hiddenResponse = $this->getHiddenResponse($output, $inputStream, $question->isTrimmable());
125+
$ret = $question->isTrimmable() ? trim($hiddenResponse) : $hiddenResponse;
125126
} catch (RuntimeException $e) {
126127
if (!$question->isHiddenFallback()) {
127128
throw $e;
@@ -134,10 +135,13 @@ private function doAsk(OutputInterface $output, Question $question)
134135
if (false === $ret) {
135136
throw new RuntimeException('Aborted.');
136137
}
137-
$ret = trim($ret);
138+
if ($question->isTrimmable()) {
139+
$ret = trim($ret);
140+
}
138141
}
139142
} else {
140-
$ret = trim($this->autocomplete($output, $question, $inputStream, $autocomplete));
143+
$autocomplete = $this->autocomplete($output, $question, $inputStream, $autocomplete);
144+
$ret = $question->isTrimmable() ? trim($autocomplete) : $autocomplete;
141145
}
142146

143147
if ($output instanceof ConsoleSectionOutput) {
@@ -351,10 +355,11 @@ private function mostRecentlyEnteredValue($entered)
351355
*
352356
* @param OutputInterface $output An Output instance
353357
* @param resource $inputStream The handler resource
358+
* @param bool $trimmable Is the answer trimmable
354359
*
355360
* @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
356361
*/
357-
private function getHiddenResponse(OutputInterface $output, $inputStream): string
362+
private function getHiddenResponse(OutputInterface $output, $inputStream, $trimmable = true): string
358363
{
359364
if ('\\' === \DIRECTORY_SEPARATOR) {
360365
$exe = __DIR__.'/../Resources/bin/hiddeninput.exe';
@@ -366,7 +371,8 @@ private function getHiddenResponse(OutputInterface $output, $inputStream): strin
366371
$exe = $tmpExe;
367372
}
368373

369-
$value = rtrim(shell_exec($exe));
374+
$sExec = shell_exec($exe);
375+
$value = $trimmable ? rtrim($sExec) : $sExec;
370376
$output->writeln('');
371377

372378
if (isset($tmpExe)) {
@@ -386,8 +392,9 @@ private function getHiddenResponse(OutputInterface $output, $inputStream): strin
386392
if (false === $value) {
387393
throw new RuntimeException('Aborted.');
388394
}
389-
390-
$value = trim($value);
395+
if ($trimmable) {
396+
$value = trim($value);
397+
}
391398
$output->writeln('');
392399

393400
return $value;
@@ -396,7 +403,8 @@ private function getHiddenResponse(OutputInterface $output, $inputStream): strin
396403
if (false !== $shell = $this->getShell()) {
397404
$readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword';
398405
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
399-
$value = rtrim(shell_exec($command));
406+
$sCommand = shell_exec($command);
407+
$value = $trimmable ? rtrim($sCommand) : $sCommand;
400408
$output->writeln('');
401409

402410
return $value;

src/Symfony/Component/Console/Question/Question.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Question
2929
private $validator;
3030
private $default;
3131
private $normalizer;
32+
private $trimmable = true;
3233

3334
/**
3435
* @param string $question The question to ask to the user
@@ -274,4 +275,19 @@ protected function isAssoc($array)
274275
{
275276
return (bool) \count(array_filter(array_keys($array), 'is_string'));
276277
}
278+
279+
public function isTrimmable(): bool
280+
{
281+
return $this->trimmable;
282+
}
283+
284+
/**
285+
* @return $this
286+
*/
287+
public function setTrimmable(bool $trimmable): self
288+
{
289+
$this->trimmable = $trimmable;
290+
291+
return $this;
292+
}
277293
}

src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,20 @@ public function testAsk()
165165
$this->assertEquals('What time is it?', stream_get_contents($output->getStream()));
166166
}
167167

168+
public function testAskNonTrimmed()
169+
{
170+
$dialog = new QuestionHelper();
171+
172+
$inputStream = $this->getInputStream(' 8AM ');
173+
174+
$question = new Question('What time is it?', '2PM');
175+
$question->setTrimmable(false);
176+
$this->assertEquals(' 8AM ', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
177+
178+
rewind($output->getStream());
179+
$this->assertEquals('What time is it?', stream_get_contents($output->getStream()));
180+
}
181+
168182
public function testAskWithAutocomplete()
169183
{
170184
if (!$this->hasSttyAvailable()) {
@@ -198,6 +212,40 @@ public function testAskWithAutocomplete()
198212
$this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
199213
}
200214

215+
public function testAskWithAutocompleteTrimmable()
216+
{
217+
if (!$this->hasSttyAvailable()) {
218+
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
219+
}
220+
221+
// Acm<NEWLINE>
222+
// Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE>
223+
// <NEWLINE>
224+
// <UP ARROW><UP ARROW><NEWLINE>
225+
// <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE>
226+
// <DOWN ARROW><NEWLINE>
227+
// S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE>
228+
// F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE>
229+
$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");
230+
231+
$dialog = new QuestionHelper();
232+
$helperSet = new HelperSet([new FormatterHelper()]);
233+
$dialog->setHelperSet($helperSet);
234+
235+
$question = new Question('Please select a bundle', 'FrameworkBundle');
236+
$question->setAutocompleterValues(['AcmeDemoBundle ', 'AsseticBundle', ' SecurityBundle ', 'FooBundle']);
237+
$question->setTrimmable(false);
238+
239+
$this->assertEquals('AcmeDemoBundle ', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
240+
$this->assertEquals('AsseticBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
241+
$this->assertEquals('FrameworkBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
242+
$this->assertEquals(' SecurityBundle ', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
243+
$this->assertEquals('FooBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
244+
$this->assertEquals('AcmeDemoBundle ', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
245+
$this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
246+
$this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
247+
}
248+
201249
public function testAskWithAutocompleteCallback()
202250
{
203251
if (!$this->hasSttyAvailable()) {
@@ -373,6 +421,21 @@ public function testAskHiddenResponse()
373421
$this->assertEquals('8AM', $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("8AM\n")), $this->createOutputInterface(), $question));
374422
}
375423

424+
public function testAskHiddenResponseTrimmed()
425+
{
426+
if ('\\' === \DIRECTORY_SEPARATOR) {
427+
$this->markTestSkipped('This test is not supported on Windows');
428+
}
429+
430+
$dialog = new QuestionHelper();
431+
432+
$question = new Question('What time is it?');
433+
$question->setHidden(true);
434+
$question->setTrimmable(false);
435+
436+
$this->assertEquals(' 8AM', $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream(' 8AM')), $this->createOutputInterface(), $question));
437+
}
438+
376439
/**
377440
* @dataProvider getAskConfirmationData
378441
*/

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy