Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Console] Support binary / negatable options #26292

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

Closed

Conversation

greg-1-anderson
Copy link
Contributor

@greg-1-anderson greg-1-anderson commented Feb 24, 2018

e.g. --foo and --no-foo.

Work-in-progress.

Q A
Branch? master
Bug fix? no
New feature? yes (CHANGELOG.md updates needed)
BC breaks? no
Deprecations? no
Tests pass? no (Need updating; work-in-progress)
Fixed tickets #24314
License MIT
Doc PR Still needed (symfony/symfony-docs#...)

Hoping for some feedback here before updating tests and docs, so that I do not need to do that twice.

The implementation here is slightly more capable than the description in the original issue (#24314). Two new InputOption constants are added:

Constant Description
VALUE_NEGATABLE Option --foo may also be represented as --no-foo
VALUE_BINARY This is just VALUE_NEGATABLE

This allows for flexible use of the negatable option.

  • Default value may be true, false or null (the last differentiates between --foo, --no-foo, and no option specifice)
  • Options with values may also be specified, e.g. --browser=firefox or --no-browser to clear, otherwise returns a default like chrome
  • Options with values may also be required, e.g. --foo=bar or --no-foo are accepted, but --foo throws an exception

I can fine-tune this further if different behavior is desired.

@frankdejonge
Copy link
Contributor

I've looked at the implementation, it looks like this feature is handled in the "handling" side. It seems to me that this (nice feature) is richer way to define an option. In that trail of thought it would seem logical to move the implementation of this to the input side. A possible place for this would be the InputDefinition::setDefinitions method, here we could check if the option isNegatable and register an addition negated version of the input. This would mean the options on the output side (handling the CLI input) would remain unchanged. The benefit of this is that cause and effect are close together.

For the feature itself, I really like it. A definite DX improvement 👍

@greg-1-anderson
Copy link
Contributor Author

Registering two versions of an option is how Symfony handles --ansi / --no-ansi today, and how https://github.com/consolidation/annotated-command provides this functionality sans built-in Symfony support. While this sort of implementation is possible, please bear in mind that the primary purpose of this PR is to allow setDefault to work on binary options. Currently, if one tries to setDefault on an option that takes no value (a true/false option), an exception is thrown. This interferes with our desire to integrate the handling of our config system with input options.

To this end, having separate input definitions for the positive and negative values as we do today makes it more difficult to make this work. See #24314.

@chalasr chalasr added this to the 4.1 milestone Feb 24, 2018
@greg-1-anderson
Copy link
Contributor Author

@frankdejonge I thought of a way to implement your suggestion.

  • The second negated version of the input would have a reference to the non-negated InputOption. Its methods would simply call through, negating parameters / results as required.
  • The negated InputOption would be hidden in help.

This means that you would still define just one InputOption, as you suggested. Perhaps this is what you intended all along, and I simply did not understand. I was thinking that having two InputOptions would get in the way of the configuration system; however, if the negated option was merely a reflection of the primary InputOption, then your configuration could define either foo: false or no-foo: true, to equivalent effect.

I'll update the PR here to reflect this idea.

…rrayInput classes to InputOption class. Make InputDefinition handle negative options by creating a NegatedInputOption.
@greg-1-anderson
Copy link
Contributor Author

@frankdejonge I pushed a new commit that reimplements this feature as selected above.

One challenge with this approach is that originally, the input object did not ask the input definition to process the value in any way. Instead, the handling of each option was hardcoded in ArgvInput, with similar logic duplicated in ArrayInput.

To get around this, I refactored the code somewhat so that both ArgvInput and ArrayInput pass their value through the InputOption. Doing this also provided the opportunity to reduce some duplicate code in these classes. The result does work just fine, and has some benefits, but I am ambivalent about which implementation is preferable. The new methods checkValue and effectiveName are perhaps not optimal. (Suggestions for better names for these methods gladly accepted.)

In the end, it does not matter to me how this is implemented, as long as the final result works. If this version is not an improvement, we can try something else, or perhaps just revert to a8ad2be.

@nicolas-grekas nicolas-grekas changed the title [WIP] Implements #24314: Support binary / negatable options [Console] Support binary / negatable options Mar 19, 2018
@nicolas-grekas
Copy link
Member

Status: needs work

@greg-1-anderson
Copy link
Contributor Author

greg-1-anderson commented Mar 19, 2018

I was holding off on fixing the tests and documentation pending feedback on the implementation approach. Since there has been no further feedback there, I'll move forward with the rest of the work.

@nicolas-grekas nicolas-grekas modified the milestones: 4.1, next Apr 20, 2018
Copy link
Member

@fabpot fabpot left a comment

Choose a reason for hiding this comment

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

I've done a quick code review and made some minor comments along the way. This feature is really nice. Would love to have it in 4.4 :)

@@ -200,4 +194,20 @@ public function getStream()
{
return $this->stream;
}

/**
* Look up the option definition for the given option name.
Copy link
Member

Choose a reason for hiding this comment

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

Looks

/**
* Look up the option definition for the given option name.
*
* @param string $name
Copy link
Member

Choose a reason for hiding this comment

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

As this would target 4.4, you can use type hints instead of phpdocs.

@@ -106,6 +110,11 @@ public function getName()
return $this->name;
}

public function effectiveName()
Copy link
Member

Choose a reason for hiding this comment

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

Should probably be getEffectiveName() instead.

@Tobion
Copy link
Contributor

Tobion commented Jul 9, 2019

The wonder about the negatable option allowing --no-foo. Is that part of some CLI arguments standard? Haven't seen that before. The linked issue mentions the travis CI. Are there more examples?
What are the advantages of the new option type compared to people just adding a no-foo option themselves? Only the synopsis that combines both options?

I agree that we need better support for different console argument/option types.
An idea of mine would be to separate the expected value (boolean, array, int, string) and the way the value can be passed

  • boolean as --foo, --no-foo or alternatively --foo=1, --foo=0 or just --foo
  • array as --foo=value1 --foo=value2 or alternatively as csv --foo=value1,value2

@greg-1-anderson
Copy link
Contributor Author

@Tobion Please see the original issue #24314. A primary goal of this PR is to allow setDefault to work the same way for boolean options as it does for other option types.

The idea to allow csv for array options is nice, but perhaps is a separate issue.

@fabpot
Copy link
Member

fabpot commented Feb 3, 2020

I'm wondering the exact use case. Also, would it allow things like --foo --no-foo which would be equivalent to --no-foo?

@greg-1-anderson
Copy link
Contributor Author

In Drush, it is possible to change the default value for any commandline option of any command by adding a configuration value to the drush.yml config file. This is supported by the package consolidation/config, which can be used with any Symfony Console program. (Note also that consolidation/config provides features orthoganal to symfony/config, and the two packages may be used together.)

The current implementation works fine for everything except binary options. Binary options do not work, because setDefault does not work with binary options in Symfony Console. consolidation/config had a workaround for binary options that used to work in older versions of Symfony, but the workaround is no longer effective.

The main requirement for this PR, then, is for setDefault to work correctly. Whether the standard uses --foo and --no-foo or --foo=0 and --foo=1 or -x and -X or something else is not really important. I have not had time to do a survey of a wide breadth of commandline tools to figure out which methods are most common. See, however, the following text from man tar:

   --no-OPTION
          You may turn off one or more implied options by prefixing the option name with "no-".  Not all options may be prefixed with a
          "no-": only options that are implied by other options (e.g. --no-D, --no-perms) or have different defaults in various circum-
          stances  (e.g. --no-whole-file, --no-blocking-io, --no-dirs).  You may specify either the short or the long option name after
          the "no-" prefix (e.g. --no-R is the same as --no-relative).

          For example: if you want to use -a (--archive) but don't want -o (--owner), instead of converting -a into -rlptgD, you  could
          specify -a --no-o (or -a --no-owner).

          The order of the options is important:  if you specify --no-r -a, the -r option would end up being turned on, the opposite of
          -a --no-r.  Note also that the side-effects of the --files-from option are NOT positional, as it affects the default state of
          several options and slightly changes the meaning of -a (see the --files-from option for more details).

Generally speaking, binary commandline options do not take values, so --foo=0 and --foo=1 is fairly uncommon. Old versions of Drush used to do this, but I can't think of any Unix tools that do this.

@greg-1-anderson
Copy link
Contributor Author

Oh, and regarding the question of whether conflicting binary options such as --foo --no-foo are allowed, I am fairly indifferent to that. It might be useful e.g. if using up-arrow in the shell to edit a very long command line, adding a --no-foo at the end rather than having to left-cursor over to edit the --foo in the middle would be handy, so I'd say that last-binary-option-specified wins is the right answer.

Any behavior would be okay, though, as long as it is consistent and documented.

@jvasseur
Copy link
Contributor

jvasseur commented Feb 11, 2020

Another use case for allowing both --foo and --no-foo is shell aliases: you can create the alias alias bar="command --foo" to always have the option by default and later on use bar --no-foo if you don't need the foo options (it could even be useful to allow the options multiple time and only keep the last one but that's more of an edge case).

@fabpot fabpot closed this Oct 7, 2020
@fabpot
Copy link
Member

fabpot commented Oct 7, 2020

We've just moved away from master as the main branch to use 5.x instead. Unfortunately, I cannot reopen the PR and change the target branch to 5.x. Can you open a new PR referencing this one to not loose the discussion? Thank you for your understanding and for your help.

fabpot added a commit that referenced this pull request Jan 5, 2021
…nderson, jderusse)

This PR was merged into the 5.3-dev branch.

Discussion
----------

[Console] Support binary / negatable options

| Q             | A
| ------------- | ---
| Branch?       | 5.x
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | Fix #24314
| License       | MIT
| Doc PR        | TODO

This PR take over #26292 to introduce a new option mode `NEGATABLE` that ease creation of option and their negation (ie. `--foo`, `--no-foo`)

Negated options applies only to flag options (`VALUE_NONE`)
```
./cli.php --foo
var_dump(getOption('foo')); // true

./cli.php --no-foo
var_dump(getOption('foo')); // false
```

Commits
-------

1a9a3c3 Implement negatable option
8d455db [WIP] Implements #24314: Support binary / negatable options, e.g. --foo and --no-foo.
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.

8 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.