Skip to content

Commit 888e580

Browse files
committed
Allow to create an InvokableCommand using the make:command command
Move the "Old" Inheritance based command template to a separate template file
1 parent 349c696 commit 888e580

File tree

3 files changed

+105
-25
lines changed

3 files changed

+105
-25
lines changed

src/Maker/MakeCommand.php

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,20 @@
1616
use Symfony\Bundle\MakerBundle\Generator;
1717
use Symfony\Bundle\MakerBundle\InputConfiguration;
1818
use Symfony\Bundle\MakerBundle\Str;
19+
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
1920
use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil;
2021
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
22+
use Symfony\Component\Console\Attribute\Argument;
2123
use Symfony\Component\Console\Attribute\AsCommand;
24+
use Symfony\Component\Console\Attribute\Option;
2225
use Symfony\Component\Console\Command\Command;
2326
use Symfony\Component\Console\Command\LazyCommand;
2427
use Symfony\Component\Console\Input\InputArgument;
2528
use Symfony\Component\Console\Input\InputInterface;
2629
use Symfony\Component\Console\Input\InputOption;
2730
use Symfony\Component\Console\Output\OutputInterface;
2831
use Symfony\Component\Console\Style\SymfonyStyle;
32+
use Symfony\Component\HttpKernel\Kernel;
2933

3034
/**
3135
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
@@ -60,15 +64,19 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
6064
->addArgument('name', InputArgument::OPTIONAL, \sprintf('Choose a command name (e.g. <fg=yellow>app:%s</>)', Str::asCommand(Str::getRandomTerm())))
6165
->setHelp($this->getHelpFileContents('MakeCommand.txt'))
6266
;
67+
68+
if ($this->supportsInvokableCommand()) {
69+
$command->addOption('invokable', 'i', InputOption::VALUE_NONE, 'Use this option to create an invokable command');
70+
}
6371
}
6472

6573
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
6674
{
67-
$this->generateInheritanceCommand($input, $io, $generator);
68-
}
75+
if (true !== $input->getOption('invokable') && $this->supportsInvokableCommand()) {
76+
$wantsInvokable = $io->confirm('Would you like this command to be inokvable?', false);
77+
$input->setOption('invokable', $wantsInvokable);
78+
}
6979

70-
private function generateInheritanceCommand(InputInterface $input, ConsoleStyle $io, Generator $generator): void
71-
{
7280
$commandName = trim($input->getArgument('name'));
7381
$commandNameHasAppPrefix = str_starts_with($commandName, 'app:');
7482

@@ -79,6 +87,13 @@ private function generateInheritanceCommand(InputInterface $input, ConsoleStyle
7987
\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'))
8088
);
8189

90+
$input->getOption('invokable') ?
91+
$this->generateInvokableCommand($commandName, $commandClassNameDetails, $io, $generator) :
92+
$this->generateInheritanceCommand($commandName, $commandClassNameDetails, $io, $generator);
93+
}
94+
95+
private function generateInheritanceCommand(string $commandName, ClassNameDetails $commandClassNameDetails, ConsoleStyle $io, Generator $generator): void
96+
{
8297
$useStatements = new UseStatementGenerator([
8398
Command::class,
8499
InputArgument::class,
@@ -91,7 +106,7 @@ private function generateInheritanceCommand(InputInterface $input, ConsoleStyle
91106

92107
$generator->generateClass(
93108
$commandClassNameDetails->getFullName(),
94-
'command/Command.tpl.php',
109+
'command/InheritanceCommand.tpl.php',
95110
[
96111
'use_statements' => $useStatements,
97112
'command_name' => $commandName,
@@ -108,11 +123,44 @@ private function generateInheritanceCommand(InputInterface $input, ConsoleStyle
108123
]);
109124
}
110125

126+
private function generateInvokableCommand(string $commandName, ClassNameDetails $commandClassNameDetails, ConsoleStyle $io, Generator $generator): void
127+
{
128+
$useStatements = new UseStatementGenerator([
129+
Argument::class,
130+
AsCommand::class,
131+
Command::class,
132+
Option::class,
133+
SymfonyStyle::class,
134+
]);
135+
136+
$generator->generateClass(
137+
$commandClassNameDetails->getFullName(),
138+
'command/Command.tpl.php',
139+
[
140+
'use_statements' => $useStatements,
141+
'command_name' => $commandName,
142+
]
143+
);
144+
145+
$generator->writeChanges();
146+
147+
$this->writeSuccessMessage($io);
148+
$io->text([
149+
'Next: open your new command class and customize it!',
150+
'Find the documentation at <fg=yellow>https://symfony.com/doc/current/console.html</>',
151+
]);
152+
}
153+
111154
public function configureDependencies(DependencyBuilder $dependencies): void
112155
{
113156
$dependencies->addClassDependency(
114157
Command::class,
115158
'console'
116159
);
117160
}
161+
162+
private function supportsInvokableCommand(): bool
163+
{
164+
return Kernel::VERSION_ID >= 70300;
165+
}
118166
}

templates/command/Command.tpl.php

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,19 @@
88
name: '<?= $command_name; ?>',
99
description: 'Add a short description for your command',
1010
)]
11-
class <?= $class_name; ?> extends Command
11+
class <?= $class_name; ?>
1212
{
13-
public function __construct()
13+
public function __invoke(
14+
SymfonyStyle $io,
15+
#[Argument] string $arg1 = null,
16+
#[Option] bool $option1 = false,
17+
): int
1418
{
15-
parent::__construct();
16-
}
17-
18-
protected function configure(): void
19-
{
20-
$this
21-
<?= $set_description ? " ->setDescription(self::\$defaultDescription)\n" : '' ?>
22-
->addArgument('arg1', InputArgument::OPTIONAL, 'Argument description')
23-
->addOption('option1', null, InputOption::VALUE_NONE, 'Option description')
24-
;
25-
}
26-
27-
protected function execute(InputInterface $input, OutputInterface $output): int
28-
{
29-
$io = new SymfonyStyle($input, $output);
30-
$arg1 = $input->getArgument('arg1');
31-
3219
if ($arg1) {
3320
$io->note(sprintf('You passed an argument: %s', $arg1));
3421
}
3522

36-
if ($input->getOption('option1')) {
23+
if ($option1) {
3724
// ...
3825
}
3926

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?= "<?php\n"; ?>
2+
3+
namespace <?= $namespace; ?>;
4+
5+
<?= $use_statements; ?>
6+
7+
#[AsCommand(
8+
name: '<?= $command_name; ?>',
9+
description: 'Add a short description for your command',
10+
)]
11+
class <?= $class_name; ?> extends Command
12+
{
13+
public function __construct()
14+
{
15+
parent::__construct();
16+
}
17+
18+
protected function configure(): void
19+
{
20+
$this
21+
<?= $set_description ? " ->setDescription(self::\$defaultDescription)\n" : '' ?>
22+
->addArgument('arg1', InputArgument::OPTIONAL, 'Argument description')
23+
->addOption('option1', null, InputOption::VALUE_NONE, 'Option description')
24+
;
25+
}
26+
27+
protected function execute(InputInterface $input, OutputInterface $output): int
28+
{
29+
$io = new SymfonyStyle($input, $output);
30+
$arg1 = $input->getArgument('arg1');
31+
32+
if ($arg1) {
33+
$io->note(sprintf('You passed an argument: %s', $arg1));
34+
}
35+
36+
if ($input->getOption('option1')) {
37+
// ...
38+
}
39+
40+
$io->success('You have a new command! Now make it your own! Pass --help to see your options.');
41+
42+
return Command::SUCCESS;
43+
}
44+
}
45+

0 commit comments

Comments
 (0)