Skip to content

[Console] Fix parsing optionnal options with empty value in argv #19946

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 1 commit into from
Sep 17, 2016

Conversation

chalasr
Copy link
Member

@chalasr chalasr commented Sep 15, 2016

Q A
Branch? 2.7
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #19884
License MIT

If a command takes an option accepting an optional value, passing an empty value to this option will make it parsed as null, e.g:

bin/console dummy --foo "" gives ['foo' => null].
bin/console dummy --foo="" gives ['foo' => null].

Problems appear when adding an argument with a required value (let's call it bar):

bin/console dummy --foo "" "bar-val" gives ['foo' => null, 'bar' => 'bar-val'] which is OK.

But:

bin/console dummy --foo="" "bar-val"

[RuntimeException]
Not enough arguments (missing: "bar").

The empty value is never considered, as $argv just return "--foo=" for the option, the current implementation doesn't handle the empty value when using an equal as separator, so the bar argument value is considered as the foo one, giving a missing required argument at runtime.

This fixes it by explicitly considering the empty value if there is nothing immediately after the equal sign, so args/options correctly take their respective values.

@@ -232,7 +235,7 @@ private function addLongOption($name, $value)
if (isset($next[0]) && '-' !== $next[0]) {
$value = $next;
} elseif (empty($next)) {
$value = '';
$value = null;
Copy link
Member Author

@chalasr chalasr Sep 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is needed, making the behavior always consistent with the expected one.

@@ -145,7 +145,10 @@ private function parseLongOption($token)
$name = substr($token, 2);

if (false !== $pos = strpos($name, '=')) {
$this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
if (!$value = substr($name, $pos + 1)) {
Copy link
Contributor

@ogizanagi ogizanagi Sep 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still sure something like --foo=0 won't cause any issue with this check ? :/ (Maybe add this test case below ?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, thank you @ogizanagi

@chalasr chalasr changed the base branch from master to 2.7 September 16, 2016 16:41
@chalasr chalasr force-pushed the console/fix_option_emptyval_withequal branch from b569ebb to 1177289 Compare September 16, 2016 17:18
@chalasr
Copy link
Member Author

chalasr commented Sep 16, 2016

Added a test case, base switched on 2.7, it's ok now.

array('cli.php', '--foo='),
array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)),
array('foo' => null),
'->parse() parses long options with optionnal value which is empty (with a = separator) as null',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*optional

And below also.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @ro0NL

@chalasr chalasr force-pushed the console/fix_option_emptyval_withequal branch from 1177289 to 6b05e7f Compare September 17, 2016 12:38
@chalasr chalasr force-pushed the console/fix_option_emptyval_withequal branch from 6b05e7f to 8952155 Compare September 17, 2016 12:42
@fabpot
Copy link
Member

fabpot commented Sep 17, 2016

Thank you @chalasr.

@fabpot fabpot merged commit 8952155 into symfony:2.7 Sep 17, 2016
fabpot added a commit that referenced this pull request Sep 17, 2016
…n argv (chalasr)

This PR was merged into the 2.7 branch.

Discussion
----------

[Console] Fix parsing optionnal options with empty value in argv

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #19884
| License       | MIT

If a command takes an option accepting an optional value, passing an empty value to this option will make it parsed as `null`, e.g:

`bin/console dummy --foo ""` gives `['foo' => null]`.
`bin/console dummy --foo=""` gives `['foo' => null]`.

Problems appear when adding an argument with a required value (let's call it `bar`):

`bin/console dummy --foo "" "bar-val"` gives `['foo' => null, 'bar' => 'bar-val']` which is OK.

But:

`bin/console dummy --foo="" "bar-val"`

>  [RuntimeException]
  Not enough arguments (missing: "bar").

The empty value is never considered, as `$argv` just return `"--foo="` for the option, the current implementation doesn't handle the empty value when using an equal as separator, so the `bar` argument value is considered  as the `foo` one, giving a missing required argument at runtime.

This fixes it by explicitly considering the empty value if there is nothing immediately after the equal sign, so args/options correctly take their respective values.

Commits
-------

8952155 [Console] Fix empty optionnal options with = separator in argv
@chalasr chalasr deleted the console/fix_option_emptyval_withequal branch September 17, 2016 16:55
@@ -145,7 +145,10 @@ private function parseLongOption($token)
$name = substr($token, 2);

if (false !== $pos = strpos($name, '=')) {
$this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
if (0 === strlen($value = substr($name, $pos + 1))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using '' === $value = substr($name, $pos + 1) instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine. See #20008
Btw, could you take a look on this check? I think we can do the same

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, looks like a candidate that could be changed.

This was referenced Oct 3, 2016
fabpot added a commit that referenced this pull request Oct 9, 2016
…n translation:update (chalasr)

This PR was merged into the 2.7 branch.

Discussion
----------

[FrameworkBundle] Convert null prefix to an empty string in translation:update

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

This command needs the ability to use an empty string as prefix, which is not possible using `bin/console translation:update --prefix=""` because `$argv` doesn't parse empty strings thus the value is converted to `null` by `ArgvInput` (only since #19946, before the option was not considered to be set, giving the default `'__'` thus this should be fine from a BC pov).

Here I propose to explicitly convert the `prefix` value to an empty string if set to `null`, as it is a very specific need and we can't guess that from `ArgvInput`.
An other way to fix it could be to add a `--no-prefix` option to the command but I don't think it is worth it, and it couldn't be treated as a bug fix thus not fixed before `3.2`.

Commits
-------

f02b687 [FrameworkBundle] Convert null prefix to an empty string in translation:update command
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants