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
161 changes: 68 additions & 93 deletions system/Commands/Generators/CommandGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,111 +13,86 @@

namespace CodeIgniter\Commands\Generators;

use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\AbstractGeneratorCommand;
use CodeIgniter\CLI\Attributes\Command;
use CodeIgniter\CLI\Attributes\GeneratorCommand;
use CodeIgniter\CLI\CLI;
use CodeIgniter\CLI\GeneratorTrait;

/**
* Generates a skeleton command file.
*/
class CommandGenerator extends BaseCommand
use CodeIgniter\CLI\Input\Option;

#[Command(name: 'make:command', description: 'Generates a new spark command.', group: 'Generators')]
#[GeneratorCommand(
component: 'Command',
template: 'command.tpl.php',
directory: 'Commands',
classNameLang: 'CLI.generator.className.command',
)]
class CommandGenerator extends AbstractGeneratorCommand
{
use GeneratorTrait;

/**
* The Command's Group
*
* @var string
*/
protected $group = 'Generators';

/**
* The Command's Name
*
* @var string
*/
protected $name = 'make:command';

/**
* The Command's Description
*
* @var string
*/
protected $description = 'Generates a new spark command.';

/**
* The Command's Usage
*
* @var string
*/
protected $usage = 'make:command <name> [options]';

/**
* The Command's Arguments
*
* @var array<string, string>
*/
protected $arguments = [
'name' => 'The command class name.',
];

/**
* The Command's Options
*
* @var array<string, string>
*/
protected $options = [
'--command' => 'The command name. Default: "command:name"',
'--type' => 'The command type. Options [basic, generator]. Default: "basic".',
'--group' => 'The command group. Default: [basic -> "App", generator -> "Generators"].',
'--namespace' => 'Set root namespace. Default: "APP_NAMESPACE".',
'--suffix' => 'Append the component title to the class name (e.g. User => UserCommand).',
'--force' => 'Force overwrite existing file.',
];

/**
* Actually execute a command.
*/
public function run(array $params)
protected function configure(): void
{
parent::configure();

$this
->addOption(new Option(
name: 'command',
shortcut: 'c',
description: 'The command name.',
requiresValue: true,
valueLabel: 'name',
default: 'command:name',
))
->addOption(new Option(
name: 'type',
shortcut: 't',
description: 'The command type: "basic" or "generator".',
requiresValue: true,
default: 'basic',
))
->addOption(new Option(
name: 'group',
shortcut: 'g',
description: 'The command group. Defaults to "App" for basic and "Generators" for generator commands.',
acceptsValue: true,
));
}

protected function interact(array &$arguments, array &$options): void
{
$this->component = 'Command';
$this->directory = 'Commands';
$this->template = 'command.tpl.php';
$type = $this->getUnboundOption('type', $options);

$this->classNameLang = 'CLI.generator.className.command';
$this->generateClass($params);
if (! is_string($type) || $type === 'basic' || $type === 'generator') {
return;
}

return EXIT_SUCCESS;
$options['type'] = CLI::prompt(lang('CLI.generator.commandType'), ['basic', 'generator'], 'required');
}

/**
* Prepare options and do the necessary replacements.
*/
protected function prepare(string $class): string
protected function execute(array $arguments, array $options): int
{
$command = $this->getOption('command');
$group = $this->getOption('group');
$type = $this->getOption('type');

$command = is_string($command) ? $command : 'command:name';
$type = is_string($type) ? $type : 'basic';

if (! in_array($type, ['basic', 'generator'], true)) {
// @codeCoverageIgnoreStart
$type = CLI::prompt(lang('CLI.generator.commandType'), ['basic', 'generator'], 'required');
CLI::newLine();
// @codeCoverageIgnoreEnd
$type = $this->getValidatedOption('type');

if ($type !== 'basic' && $type !== 'generator') {
CLI::error(lang('CLI.generator.invalidCommandType', [$type]));

return EXIT_ERROR;
}

return $this->generateClass();
}

protected function getReplacements(string $class): array
{
$group = $this->getValidatedOption('group');

if (! is_string($group)) {
$group = $type === 'generator' ? 'Generators' : 'App';
$group = $this->getValidatedOption('type') === 'generator' ? 'Generators' : 'App';
}

return $this->parseTemplate(
$class,
['{group}', '{command}'],
[$group, $command],
['type' => $type],
);
return ['{command}' => $this->getValidatedOption('command'), '{group}' => $group];
}

protected function getTemplateData(string $class): array
{
return ['type' => $this->getValidatedOption('type')];
}
}
81 changes: 16 additions & 65 deletions system/Commands/Generators/Views/command.tpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,77 +2,28 @@

namespace {namespace};

use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
<?php if ($type === 'generator'): ?>
use CodeIgniter\CLI\GeneratorTrait;
<?php endif ?>
use CodeIgniter\CLI\AbstractGeneratorCommand;
use CodeIgniter\CLI\Attributes\Command;
use CodeIgniter\CLI\Attributes\GeneratorCommand;

class {class} extends BaseCommand
#[Command(name: '{command}', description: '', group: '{group}')]
#[GeneratorCommand(component: 'Command', template: 'command.tpl.php', directory: 'Commands')]
class {class} extends AbstractGeneratorCommand
{
<?php if ($type === 'generator'): ?>
use GeneratorTrait;

<?php endif ?>
/**
* The Command's Group
*
* @var string
*/
protected $group = '{group}';

/**
* The Command's Name
*
* @var string
*/
protected $name = '{command}';

/**
* The Command's Description
*
* @var string
*/
protected $description = '';

/**
* The Command's Usage
*
* @var string
*/
protected $usage = '{command} [arguments] [options]';

/**
* The Command's Arguments
*
* @var array
*/
protected $arguments = [];

/**
* The Command's Options
*
* @var array
*/
protected $options = [];
}
<?php else: ?>
use CodeIgniter\CLI\AbstractCommand;
use CodeIgniter\CLI\Attributes\Command;

/**
* Actually execute a command.
*
* @param array $params
*/
public function run(array $params)
#[Command(name: '{command}', description: '', group: '{group}')]
class {class} extends AbstractCommand
{
protected function execute(array $arguments, array $options): int
{
<?php if ($type === 'generator'): ?>
$this->component = 'Command';
$this->directory = 'Commands';
$this->template = 'command.tpl.php';

$this->generateClass($params);
<?php else: ?>
// your command logic here
<?php endif ?>
//

return EXIT_SUCCESS;
}
}
<?php endif ?>
1 change: 1 addition & 0 deletions system/Language/en/CLI.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
'fileExist' => 'File exists: "{0}"',
'fileOverwrite' => 'File overwritten: "{0}"',
'invalidClassName' => 'Class name "{0}" is not valid.',
'invalidCommandType' => 'Command type "{0}" is not valid.',
'invalidParentClass' => 'Parent class "{0}" is not valid.',
'parentClass' => 'Parent class',
'returnType' => 'Return type',
Expand Down
34 changes: 34 additions & 0 deletions tests/system/CLI/AbstractGeneratorCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,40 @@ public function testInteriorRootNamespaceSegmentIsPreserved(): void
$this->assertStringContainsString('namespace App\Widgets\Sub\App;', $content);
}

/**
* @see https://github.com/codeigniter4/CodeIgniter4/issues/4495
*/
public function testInteriorCaseIsPreserved(): void
{
$command = new GeneratorFixtureCommand(new Commands());
$command->setInteractive(false);

$exitCode = $command->run(['TestModule'], []);

$this->assertSame(EXIT_SUCCESS, $exitCode);
$this->assertFileExists(APPPATH . 'Widgets' . DIRECTORY_SEPARATOR . 'TestModule.php');
}

/**
* @see https://github.com/codeigniter4/CodeIgniter4/issues/4857
*/
public function testNamespaceLikeNameIsNotTreatedAsNamespace(): void
{
$command = new GeneratorFixtureCommand(new Commands());
$command->setInteractive(false);

$exitCode = $command->run(['App_Lesson'], []);

$this->assertSame(EXIT_SUCCESS, $exitCode);

$target = APPPATH . 'Widgets' . DIRECTORY_SEPARATOR . 'AppLesson.php';
$this->assertFileExists($target);
$content = file_get_contents($target);
$this->assertIsString($content);
$this->assertStringContainsString('namespace App\Widgets;', $content);
$this->assertStringContainsString('class AppLesson extends BaseConfig', $content);
}

public function testTrailingSeparatorInNameIsIgnored(): void
{
$command = new GeneratorFixtureCommand(new Commands());
Expand Down
Loading
Loading