Skip to content

Commit 9dc2299

Browse files
Merge branch '3.4' into 4.2
* 3.4: (24 commits) Apply php-cs-fixer rule for array_key_exists() [Security] Change FormAuthenticator if condition handles multi-byte characters in autocomplete speed up tests running them without debug flag [Translations] added missing Croatian validators Fix getItems() performance issue with RedisCluster (php-redis) [VarDumper] Keep a ref to objects to ensure their handle cannot be reused while cloning IntegerType: reject submitted non-integer numbers be keen to newcomers [HttpKernel] Fix possible infinite loop of exceptions fixed CS [Validator] Added missing translations for Afrikaans do not validate non-submitted form fields in PATCH requests Update usage example in ArrayInput doc block. [Console] Prevent ArgvInput::getFirstArgument() from returning an option value [Validator] Fixed duplicate UUID fixed CS [EventDispatcher] Fix unknown priority Avoid mutating the Finder when building the iterator [Validator] Add the missing translations for the Greek (el) locale ...
2 parents 2b46396 + 71ce77f commit 9dc2299

File tree

10 files changed

+96
-6
lines changed

10 files changed

+96
-6
lines changed

Application.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,13 @@ public function doRun(InputInterface $input, OutputInterface $output)
199199
return 0;
200200
}
201201

202+
try {
203+
// Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument.
204+
$input->bind($this->getDefinition());
205+
} catch (ExceptionInterface $e) {
206+
// Errors must be ignored, full binding/validation happens later when the command is known.
207+
}
208+
202209
$name = $this->getCommandName($input);
203210
if (true === $input->hasParameterOption(['--help', '-h'], true)) {
204211
if (!$name) {

Helper/QuestionHelper.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
269269

270270
continue;
271271
} else {
272+
if ("\x80" <= $c) {
273+
$c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]);
274+
}
275+
272276
$output->write($c);
273277
$ret .= $c;
274278
++$i;

Input/ArgvInput.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,27 @@ private function addLongOption($name, $value)
257257
*/
258258
public function getFirstArgument()
259259
{
260-
foreach ($this->tokens as $token) {
260+
$isOption = false;
261+
foreach ($this->tokens as $i => $token) {
261262
if ($token && '-' === $token[0]) {
263+
if (false !== strpos($token, '=') || !isset($this->tokens[$i + 1])) {
264+
continue;
265+
}
266+
267+
// If it's a long option, consider that everything after "--" is the option name.
268+
// Otherwise, use the last char (if it's a short option set, only the last one can take a value with space separator)
269+
$name = '-' === $token[1] ? substr($token, 2) : substr($token, -1);
270+
if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) {
271+
// noop
272+
} elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) {
273+
$isOption = true;
274+
}
275+
276+
continue;
277+
}
278+
279+
if ($isOption) {
280+
$isOption = false;
262281
continue;
263282
}
264283

Input/ArrayInput.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
*
2020
* Usage:
2121
*
22-
* $input = new ArrayInput(['name' => 'foo', '--bar' => 'foobar']);
22+
* $input = new ArrayInput(['command' => 'foo:bar', 'foo' => 'bar', '--bar' => 'foobar']);
2323
*
2424
* @author Fabien Potencier <[email protected]>
2525
*/

Input/Input.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function validate()
6969
$givenArguments = $this->arguments;
7070

7171
$missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) {
72-
return !array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
72+
return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
7373
});
7474

7575
if (\count($missingArguments) > 0) {
@@ -150,7 +150,7 @@ public function getOption($name)
150150
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
151151
}
152152

153-
return array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
153+
return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
154154
}
155155

156156
/**

Input/InputDefinition.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,8 +338,10 @@ public function getOptionDefaults()
338338
* @return string The InputOption name
339339
*
340340
* @throws InvalidArgumentException When option given does not exist
341+
*
342+
* @internal
341343
*/
342-
private function shortcutToName($shortcut)
344+
public function shortcutToName($shortcut)
343345
{
344346
if (!isset($this->shortcuts[$shortcut])) {
345347
throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));

Tester/TesterTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public function setInputs(array $inputs)
126126
*/
127127
private function initOutput(array $options)
128128
{
129-
$this->captureStreamsIndependently = array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately'];
129+
$this->captureStreamsIndependently = \array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately'];
130130
if (!$this->captureStreamsIndependently) {
131131
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
132132
if (isset($options['decorated'])) {

Tests/ApplicationTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,19 @@ public function testRun()
968968
$this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed');
969969
}
970970

971+
public function testRunWithGlobalOptionAndNoCommand()
972+
{
973+
$application = new Application();
974+
$application->setAutoExit(false);
975+
$application->setCatchExceptions(false);
976+
$application->getDefinition()->addOption(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL));
977+
978+
$output = new StreamOutput(fopen('php://memory', 'w', false));
979+
$input = new ArgvInput(['cli.php', '--foo', 'bar']);
980+
981+
$this->assertSame(0, $application->run($input, $output));
982+
}
983+
971984
/**
972985
* Issue #9285.
973986
*

Tests/Helper/QuestionHelperTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,43 @@ public function testAskWithAutocompleteWithExactMatch()
237237
$this->assertSame('b', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
238238
}
239239

240+
public function getInputs()
241+
{
242+
return [
243+
['$'], // 1 byte character
244+
['¢'], // 2 bytes character
245+
[''], // 3 bytes character
246+
['𐍈'], // 4 bytes character
247+
];
248+
}
249+
250+
/**
251+
* @dataProvider getInputs
252+
*/
253+
public function testAskWithAutocompleteWithMultiByteCharacter($character)
254+
{
255+
if (!$this->hasSttyAvailable()) {
256+
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
257+
}
258+
259+
$inputStream = $this->getInputStream("$character\n");
260+
261+
$possibleChoices = [
262+
'$' => '1 byte character',
263+
'¢' => '2 bytes character',
264+
'' => '3 bytes character',
265+
'𐍈' => '4 bytes character',
266+
];
267+
268+
$dialog = new QuestionHelper();
269+
$dialog->setHelperSet(new HelperSet([new FormatterHelper()]));
270+
271+
$question = new ChoiceQuestion('Please select a character', $possibleChoices);
272+
$question->setMaxAttempts(1);
273+
274+
$this->assertSame($character, $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
275+
}
276+
240277
public function testAutocompleteWithTrailingBackslash()
241278
{
242279
if (!$this->hasSttyAvailable()) {

Tests/Input/ArgvInputTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,14 @@ public function testGetFirstArgument()
312312

313313
$input = new ArgvInput(['cli.php', '-fbbar', 'foo']);
314314
$this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input');
315+
316+
$input = new ArgvInput(['cli.php', '--foo', 'fooval', 'bar']);
317+
$input->bind(new InputDefinition([new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('arg')]));
318+
$this->assertSame('bar', $input->getFirstArgument());
319+
320+
$input = new ArgvInput(['cli.php', '-bf', 'fooval', 'argval']);
321+
$input->bind(new InputDefinition([new InputOption('bar', 'b', InputOption::VALUE_NONE), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('arg')]));
322+
$this->assertSame('argval', $input->getFirstArgument());
315323
}
316324

317325
public function testHasParameterOption()

0 commit comments

Comments
 (0)