8000 [Console] Explicitly passed options without value (or empty) should remain empty by chalasr · Pull Request #21228 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Console] Explicitly passed options without value (or empty) should remain empty #21228

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

Merged
Merged
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
56 changes: 56 additions & 0 deletions UPGRADE-3.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,62 @@ ClassLoader

* The component is deprecated and will be removed in 4.0. Use Composer instead.

Console
-------

* `Input::getOption()` no longer returns the default value for options
with value optional explicitly passed empty.

For:

```php
protected function configure()
{
$this
// ...
->setName('command')
->addOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default')
;
}

protected function execute(InputInterface $input, OutputInterface $output)
{
var_dump($input->getOption('foo'));
}
```

Before:

```
$ php console.php command
"default"

$ php console.php command --foo
"default"

$ php console.php command --foo ""
"default"

$ php console.php command --foo=
"default"
```

After:

```
$ php console.php command
"default"

$ php console.php command --foo
NULL

$ php console.php command --foo ""
""

$ php console.php command --foo=
""
```

Debug
-----

Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/Console/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ CHANGELOG

* added `ExceptionListener`
* added `AddConsoleCommandPass` (originally in FrameworkBundle)
* [BC BREAK] `Input::getOption()` no longer returns the default value for options
with value optional explicitly passed empty

3.2.0
------
Expand Down
22 changes: 10 additions & 12 deletions src/Symfony/Component/Console/Input/ArgvInput.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,12 @@ private function parseLongOption($token)

if (false !== $pos = strpos($name, '=')) {
if (0 === strlen($value = substr($name, $pos + 1))) {
array_unshift($this->parsed, null);
// if no value after "=" then substr() returns "" since php7 only, false before
// see http://php.net/manual/fr/migration70.incompatible.php#119151
if (PHP_VERSION_ID < 70000 && false === $value) {
$value = '';
}
array_unshift($this->parsed, $value);
}
$this->addLongOption(substr($name, 0, $pos), $value);
} else {
Expand Down Expand Up @@ -221,23 +226,16 @@ private function addLongOption($name, $value)

$option = $this->definition->getOption($name);

// Convert empty values to null
if (!isset($value[0])) {
$value = null;
}

if (null !== $value && !$option->acceptValue()) {
throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
}

if (null === $value && $option->acceptValue() && count($this->parsed)) {
if (in_array($value, array('', null), true) && $option->acceptValue() && count($this->parsed)) {
// if option accepts an optional or mandatory argument
// let's see if there is one provided
$next = array_shift($this->parsed);
if (isset($next[0]) && '-' !== $next[0]) {
if ((isset($next[0]) && '-' !== $next[0]) || in_array($next, array('', null), true)) {
$value = $next;
} elseif (empty($next)) {
$value = null;
} else {
array_unshift($this->parsed, $next);
}
Expand All @@ -248,8 +246,8 @@ private function addLongOption($name, $value)
throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
}

if (!$option->isArray()) {
$value = $option->isValueOptional() ? $option->getDefault() : true;
if (!$option->isArray() && !$option->isValueOptional()) {
$value = true;
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/Symfony/Component/Console/Input/ArrayInput.php
8000
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ private function addLongOption($name, $value)
throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name));
}

$value = $option->isValueOptional() ? $option->getDefault() : true;
if (!$option->isValueOptional()) {
$value = true;
}
}

$this->options[$name] = $value;
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/Console/Input/Input.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public function getOption($name)
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}

return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
return array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
}

/**
Expand Down
30 changes: 24 additions & 6 deletions src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function testParseOptions($input, $options, $expectedOptions, $message)
$input = new ArgvInput($input);
$input->bind(new InputDefinition($options));

$this->assertEquals($expectedOptions, $input->getOptions(), $message);
$this->assertSame($expectedOptions, $input->getOptions(), $message);
}

public function provideOptions()
Expand All @@ -75,14 +75,32 @@ public function provideOptions()
array(
array('cli.php', '--foo='),
array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)),
array('foo' => null),
'->parse() parses long options with optional value which is empty (with a = separator) as null',
array('foo' => ''),
'->parse() parses long options with optional value which is empty (with a = separator) as empty string',
),
array(
array('cli.php', '--foo=', 'bar'),
array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::REQUIRED)),
array('foo' => ''),
'->parse() parses long options with optional value without value specified or an empty string (with a = separator) followed by an argument as empty string',
),
array(
array('cli.php', 'bar', '--foo'),
array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::REQUIRED)),
array('foo' => null),
'->parse() parses long options with optional value which is empty (with a = separator) preceded by an argument',
),
array(
array('cli.php', '--foo', '', 'bar'),
array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::REQUIRED)),
array('foo' => ''),
'->parse() parses long options with optional value which is empty as empty string even followed by an argument',
),
array(
array('cli.php', '--foo'),
array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)),
array('foo' => null),
'->parse() parses long options with optional value which is empty (with a = separator) followed by an argument',
'->parse() parses long options with optional value specified with no separator and no value as null',
),
array(
array('cli.php', '-f'),
Expand Down Expand Up @@ -252,14 +270,14 @@ public function testParseArrayOption()

$input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name='));
$input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY))));
$this->assertSame(array('name' => array('foo', 'bar', null)), $input->getOptions(), '->parse() parses empty array options as null ("--option=value" syntax)');
$this->assertSame(array('name' => array('foo', 'bar', '')), $input->getOptions(), '->parse() parses empty array options as null ("--option=value" syntax)');

$input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', '--anotherOption'));
$input->bind(new InputDefinition(array(
new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY),
new InputOption('anotherOption', null, InputOption::VALUE_NONE),
)));
$this->assertSame(array('name' => array('foo', 'bar', null), 'anotherOption' => true), $input->getOptions(), '->parse() parses empty array options as null ("--option value" syntax)');
$this->assertSame(array('name' => array('foo', 'bar', null), 'anotherOption' => true), $input->getOptions(), '->parse() parses empty array options ("--option value" syntax)');
}

public function testParseNegativeNumberAfterDoubleDash()
Expand Down
8 changes: 7 additions & 1 deletion src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,15 @@ public function provideOptions()
'->parse() parses long options with a default value',
),
array(
array('--foo' => null),
array(),
array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')),
array('foo' => 'default'),
'->parse() uses the default value for long options with value optional which are not passed',
),
array(
array('--foo' => null),
array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')),
array('foo' => null),
'->parse() parses long options with a default value',
),
array(
Expand Down
8 changes: 8 additions & 0 deletions src/Symfony/Component/Console/Tests/Input/InputTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ public function testOptions()
$input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default'))));
$this->assertEquals('default', $input->getOption('bar'), '->getOption() returns the default value for optional options');
$this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getOptions(), '->getOptions() returns all option values, even optional ones');

$input = new ArrayInput(array('--name' => 'foo', '--bar' => ''), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default'))));
$this->assertEquals('', $input->getOption('bar'), '->getOption() returns null for options explicitly passed without value (or an empty value)');
$this->assertEquals(array('name' => 'foo', 'bar' => ''), $input->getOptions(), '->getOptions() returns all option values.');

$input = new ArrayInput(array('--name' => 'foo', '--bar' => null), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default'))));
$this->assertNull($input->getOption('bar'), '->getOption() returns null for options explicitly passed without value (or an empty value)');
$this->assertEquals(array('name' => 'foo', 'bar' => null), $input->getOptions(), '->getOptions() returns all option values');
}

/**
Expand Down
0