In some console commands it's common to define two related options with opposite
behaviors. For example, the default options applied to all Symfony commands
include the --ansi
and --no-ansi
options:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// ...
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
class SomeCommand extends Command
{
// ...
protected function configure(): void
{
$this
// ...
->addOption('ansi', null, InputOption::VALUE_NONE, 'Force ANSI output')
->addOption('no-ansi', null, InputOption::VALUE_NONE, 'Disable ANSI output')
;
}
}
In Symfony 5.3 we've introduced negatable command options to simplify these
commands. A single negatable option creates two options in the command,
following the pattern --xxx
and --no-xxx
. In practice, the following is
equivalent to the previous example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
// ...
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class SomeCommand extends Command
{
// ...
protected function configure(): void
{
$this
// ...
->addOption('ansi', null, InputOption::VALUE_NEGATABLE, 'Force/disable ANSI output')
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
// if command is run as `command-name`, $useAnsi = null
// if command is run as `command-name --ansi`, $useAnsi = true
// if command is run as `command-name --no-ansi`, $useAnsi = false
$useAnsi = $input->getOption('ansi');
// ...
}
}
Negatable options are only available for options that don't allow passing any
value to them (their previous type should be InputOption::VALUE_NONE
).
So gooood !!
In the above example, without passing any --ansi or --no-ansi, will $useAnsi be true or false as default?
Awesome! 🎉
@Martin if you don't pass any of the two options, the value is FALSE.
@martin - I believe you can do
->addOption('ansi', null, InputOption::VALUE_NEGATABLE, 'Force/disable ANSI output' , 'DEFAULT HERE')
but otherwise, false as per @javier
@Javier I don't see the purpose of this if default value would be FALSE - it'd do the same work as InputOption::VALUE_NONE (not defined option returns FALSE, why to use --no-value to get the same result?) ... I think the most useful behaviour would be returning NULL by default to behave like nullable/3-state boolean - YES / NO / DON'T CARE ...
It's nice. Thank you :)
I agree with @Jan Egert bin/console would then be default as --no-ansi - and therefor all the default commands would run without any color coding.
So get color coding, you need to run bin/console --ansi then?
I see that a few posters already mentioned the desire for tri-state logic for this, as well as the possible confusion about usage of the feature (is it tri-state or 2-state by default ?).
So, for once, not a great thumbs up from my part. It seems a confusing API that needs documentation to clarify its default behaviour...
I'd really appreciate 3-state boolean behavior with possibility to mark option as REQUIRED to be able to force user to choose between --option and --no-option and possibility to define default value if optional ... it'd make perfect sense for me. But as mentioned above, it was said default value is FALSE = by default it behaves the same as VALUE_NONE.
3-state boolean would be very useful - I often use this pattern for example for data filtering - aka to get list of activated/deactivated/all users it could be --activated, --no-activated or not defined to get all.
Also - it'd be nice to support also keyword "not" just for better readability (--not-activated instead of --no-activated) but I understand it could be confusing in documentation.
Thanks to your feedbacks, "3 states" negatables are now implemented: https://github.com/symfony/symfony/pull/40986
Thank you all for your feedback. As Nicolas said, we've changed this a bit, so I've updated the example of this blog post to reflect the latest changes. Thanks!
:-x