Greg Anderson Jérémy Derussé
Contributed by Greg Anderson and Jérémy Derussé in #39642

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).

Published in #Living on the edge