Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/help/MakeCommand.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ The <info>%command.name%</info> command generates a new command:
<info>php %command.full_name% app:do-something</info>

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.

<info>php %command.full_name% --invokable</info>
72 changes: 54 additions & 18 deletions src/Maker/MakeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 <javier.eguiluz@gmail.com>
Expand Down Expand Up @@ -58,10 +63,22 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
{
$command
->addArgument('name', InputArgument::OPTIONAL, \sprintf('Choose a command name (e.g. <fg=yellow>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'));
Expand All @@ -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();

Expand Down
25 changes: 25 additions & 0 deletions templates/command/InvokableCommand.tpl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?= "<?php\n"; ?>

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;
}
}
21 changes: 20 additions & 1 deletion tests/Maker/MakeCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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');
Expand All @@ -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');
Expand Down
Loading