diff --git a/config/help/MakeCommand.txt b/config/help/MakeCommand.txt
index a88b9d430..f555cd4f0 100644
--- a/config/help/MakeCommand.txt
+++ b/config/help/MakeCommand.txt
@@ -3,3 +3,7 @@ The %command.name% command generates a new command:
php %command.full_name% app:do-something
If the argument is missing, the command will ask for the command name interactively.
+
+Symfony can also generate the command with the old structure (configure and execute method) instead of an invokable command.
+
+php %command.full_name% --invokable
\ No newline at end of file
diff --git a/src/Maker/MakeCommand.php b/src/Maker/MakeCommand.php
index 52d331c53..34cafb177 100644
--- a/src/Maker/MakeCommand.php
+++ b/src/Maker/MakeCommand.php
@@ -18,14 +18,19 @@
use Symfony\Bundle\MakerBundle\Str;
use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil;
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
+use Symfony\Component\Console\Attribute\Argument;
use Symfony\Component\Console\Attribute\AsCommand;
+use Symfony\Component\Console\Attribute\Option;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\LazyCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\HttpKernel\Kernel;
+
/**
* @author Javier Eguiluz
@@ -58,10 +63,22 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
{
$command
->addArgument('name', InputArgument::OPTIONAL, \sprintf('Choose a command name (e.g. app:%s>)', Str::asCommand(Str::getRandomTerm())))
+ ->addOption('invokable', null, InputOption::VALUE_NEGATABLE, 'Generate an invokable command (using PHP attributes) starting from Symfony 7.3?')
->setHelp($this->getHelpFileContents('MakeCommand.txt'))
;
}
+ public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
+ {
+ if ($input->getOption('invokable') === null) {
+ $description = $command->getDefinition()->getOption('invokable')->getDescription();
+ $question = new ConfirmationQuestion($description, Kernel::VERSION_ID >= 70300);
+ $invokable = $io->askQuestion($question);
+
+ $input->setOption('invokable', $invokable);
+ }
+ }
+
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
{
$commandName = trim($input->getArgument('name'));
@@ -74,25 +91,44 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
\sprintf('The "%s" command name is not valid because it would be implemented by "%s" class, which is not valid as a PHP class name (it must start with a letter or underscore, followed by any number of letters, numbers, or underscores).', $commandName, Str::asClassName($commandName, 'Command'))
);
- $useStatements = new UseStatementGenerator([
- Command::class,
- InputArgument::class,
- InputInterface::class,
- InputOption::class,
- OutputInterface::class,
- SymfonyStyle::class,
- AsCommand::class,
- ]);
+ if ($input->getOption('invokable')) {
+ $useStatements = new UseStatementGenerator([
+ Command::class,
+ SymfonyStyle::class,
+ AsCommand::class,
+ Argument::class,
+ Option::class,
+ ]);
- $generator->generateClass(
- $commandClassNameDetails->getFullName(),
- 'command/Command.tpl.php',
- [
- 'use_statements' => $useStatements,
- 'command_name' => $commandName,
- 'set_description' => !class_exists(LazyCommand::class),
- ]
- );
+ $generator->generateClass(
+ $commandClassNameDetails->getFullName(),
+ 'command/InvokableCommand.tpl.php',
+ [
+ 'use_statements' => $useStatements,
+ 'command_name' => $commandName,
+ ]
+ );
+ } else {
+ $useStatements = new UseStatementGenerator([
+ Command::class,
+ InputArgument::class,
+ InputInterface::class,
+ InputOption::class,
+ OutputInterface::class,
+ SymfonyStyle::class,
+ AsCommand::class,
+ ]);
+
+ $generator->generateClass(
+ $commandClassNameDetails->getFullName(),
+ 'command/Command.tpl.php',
+ [
+ 'use_statements' => $useStatements,
+ 'command_name' => $commandName,
+ 'set_description' => !class_exists(LazyCommand::class),
+ ]
+ );
+ }
$generator->writeChanges();
diff --git a/templates/command/InvokableCommand.tpl.php b/templates/command/InvokableCommand.tpl.php
new file mode 100644
index 000000000..3a775bfe2
--- /dev/null
+++ b/templates/command/InvokableCommand.tpl.php
@@ -0,0 +1,25 @@
+= "
+
+namespace = $namespace; ?>;
+
+= $use_statements; ?>
+
+#[AsCommand(
+ name: '= $command_name; ?>',
+ description: 'Add a short description for your command',
+)]
+class = $class_name; ?>
+{
+ public function __invoke(
+ SymfonyStyle $io,
+ #[Argument('Argument description')] string $arg,
+ #[Option('Option description')] bool $enable = false",
+ ): int {
+ $io->note(sprintf('The value of $arg is: %s', $arg));
+ $io->note(sprintf('The value of $enable is: %s', $enable ? 'true' : 'false'));
+
+ $io->success('You have a new command! Now make it your own! Pass --help to see your options.');
+
+ return Command::SUCCESS;
+ }
+}
diff --git a/tests/Maker/MakeCommandTest.php b/tests/Maker/MakeCommandTest.php
index 1ff279c19..65e5cde35 100644
--- a/tests/Maker/MakeCommandTest.php
+++ b/tests/Maker/MakeCommandTest.php
@@ -25,11 +25,26 @@ protected function getMakerClass(): string
public function getTestDetails(): \Generator
{
- yield 'it_makes_a_command_no_attributes' => [$this->createMakerTest()
+ yield 'it_makes_an_invokable_command_no_attributes' => [$this->createMakerTest()
->run(function (MakerTestRunner $runner) {
$runner->runMaker([
// command name
'app:foo',
+ // create invokable command by default
+ '',
+ ]);
+
+ $this->runCommandTest($runner, 'it_makes_a_command.php');
+ }),
+ ];
+
+ yield 'it_makes_an_old_structured_command_no_attributes' => [$this->createMakerTest()
+ ->run(function (MakerTestRunner $runner) {
+ $runner->runMaker([
+ // command name
+ 'app:foo',
+ // create a command with the old structure by default
+ 'yes',
]);
$this->runCommandTest($runner, 'it_makes_a_command.php');
@@ -41,6 +56,8 @@ public function getTestDetails(): \Generator
$runner->runMaker([
// command name
'app:foo',
+ // create invokable command by default
+ '',
]);
$this->runCommandTest($runner, 'it_makes_a_command.php');
@@ -63,6 +80,8 @@ public function getTestDetails(): \Generator
$runner->runMaker([
// command name
'app:foo',
+ // create invokable command by default
+ '',
]);
$this->runCommandTest($runner, 'it_makes_a_command_in_custom_namespace.php');