diff --git a/src/Console/src/Attribute/AsCommand.php b/src/Console/src/Attribute/AsCommand.php index 31a9ffcc7..bd20b8530 100644 --- a/src/Console/src/Attribute/AsCommand.php +++ b/src/Console/src/Attribute/AsCommand.php @@ -13,5 +13,7 @@ public function __construct( public readonly string $name, public readonly ?string $description = null, public readonly ?string $help = null, + /** @var list */ + public readonly array $aliases = [], ) {} } diff --git a/src/Console/src/Configurator/Attribute/Parser.php b/src/Console/src/Configurator/Attribute/Parser.php index 0999e68e7..a6edcae9a 100644 --- a/src/Console/src/Configurator/Attribute/Parser.php +++ b/src/Console/src/Configurator/Attribute/Parser.php @@ -46,6 +46,7 @@ public function parse(\ReflectionClass $reflection): CommandDefinition options: $this->parseOptions($reflection), description: $attribute->description, help: $attribute instanceof AsCommand ? $attribute->help : null, + aliases: $attribute instanceof AsCommand ? $attribute->aliases : [], ); } diff --git a/src/Console/src/Configurator/AttributeBasedConfigurator.php b/src/Console/src/Configurator/AttributeBasedConfigurator.php index 53ea75040..aa1bf51e3 100644 --- a/src/Console/src/Configurator/AttributeBasedConfigurator.php +++ b/src/Console/src/Configurator/AttributeBasedConfigurator.php @@ -28,6 +28,7 @@ public function configure(Command $command, \ReflectionClass $reflection): void $command->setName($result->name); $command->setDescription($result->description ?? (string) $reflection->getConstant('DESCRIPTION')); $command->setHelp((string) $result->help); + $command->setAliases($result->aliases); foreach ($result->options as $option) { $command->getDefinition()->addOption($option); diff --git a/src/Console/src/Configurator/CommandDefinition.php b/src/Console/src/Configurator/CommandDefinition.php index b3fa10c60..becfadd24 100644 --- a/src/Console/src/Configurator/CommandDefinition.php +++ b/src/Console/src/Configurator/CommandDefinition.php @@ -23,5 +23,7 @@ public function __construct( public readonly ?string $description = null, /** @var ?non-empty-string */ public readonly ?string $help = null, + /** @var non-empty-string[] */ + public readonly array $aliases = [], ) {} } diff --git a/src/Console/tests/AttributeTest.php b/src/Console/tests/AttributeTest.php index 776a85460..4dbbd0c84 100644 --- a/src/Console/tests/AttributeTest.php +++ b/src/Console/tests/AttributeTest.php @@ -6,6 +6,7 @@ use Spiral\Attributes\AttributeReader; use Spiral\Attributes\ReaderInterface; +use Spiral\Tests\Console\Fixtures\Attribute\WithAliasesCommand; use Spiral\Tests\Console\Fixtures\Attribute\WithDescriptionCommand; use Spiral\Tests\Console\Fixtures\Attribute\WithHelpCommand; use Spiral\Tests\Console\Fixtures\Attribute\WithNameCommand; @@ -40,6 +41,17 @@ public function testCommandWithHelp(): void self::assertSame('Some help message', $core->run(command: 'attribute-with-help')->getOutput()->fetch()); } + public function testCommandWithAliases(): void + { + $core = $this->getCore($this->getStaticLocator([ + WithAliasesCommand::class, + ])); + + self::assertSame('awa,alias-for-with-aliases', $core->run(command: 'attribute-with-aliases')->getOutput()->fetch()); + self::assertSame('awa,alias-for-with-aliases', $core->run(command: 'awa')->getOutput()->fetch()); + self::assertSame('awa,alias-for-with-aliases', $core->run(command: 'alias-for-with-aliases')->getOutput()->fetch()); + } + public function testCommandWithSymfonyAttribute(): void { $core = $this->getCore($this->getStaticLocator([ diff --git a/src/Console/tests/Fixtures/Attribute/WithAliasesCommand.php b/src/Console/tests/Fixtures/Attribute/WithAliasesCommand.php new file mode 100644 index 000000000..295c08a31 --- /dev/null +++ b/src/Console/tests/Fixtures/Attribute/WithAliasesCommand.php @@ -0,0 +1,19 @@ +write(\implode(',', $this->getAliases())); + + return self::SUCCESS; + } +} diff --git a/src/Exceptions/src/Renderer/ConsoleRenderer.php b/src/Exceptions/src/Renderer/ConsoleRenderer.php index 53c782c56..0036f1fc2 100644 --- a/src/Exceptions/src/Renderer/ConsoleRenderer.php +++ b/src/Exceptions/src/Renderer/ConsoleRenderer.php @@ -246,7 +246,7 @@ private function isColorsSupported(mixed $stream = STDOUT): bool try { if (\DIRECTORY_SEPARATOR === '\\') { - return (\function_exists('sapi_windows_vt100_support') && @\sapi_windows_vt100_support($stream)) + return (\function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support($stream)) || \getenv('ANSICON') !== false || \getenv('ConEmuANSI') === 'ON' || \getenv('TERM') === 'xterm'; diff --git a/src/Scaffolder/src/Command/CommandCommand.php b/src/Scaffolder/src/Command/CommandCommand.php index 228cd5c73..3a918c154 100644 --- a/src/Scaffolder/src/Command/CommandCommand.php +++ b/src/Scaffolder/src/Command/CommandCommand.php @@ -29,6 +29,9 @@ class CommandCommand extends AbstractCommand #[Option(description: 'Optional, specify a custom namespace')] private ?string $namespace = null; + #[Option(name: 'aliases', description: 'Command aliases')] + private array $aliases = []; + #[Option(name: 'argument', shortcut: 'a', description: 'Command arguments')] private array $arguments = []; @@ -40,6 +43,7 @@ public function perform(): int $declaration = $this->createDeclaration(CommandDeclaration::class, [ 'description' => $this->description, 'alias' => $this->alias ?? \strtolower((string) \preg_replace('/(?name)), + 'aliases' => $this->aliases, ]); foreach ($this->arguments as $argument) { diff --git a/src/Scaffolder/src/Declaration/CommandDeclaration.php b/src/Scaffolder/src/Declaration/CommandDeclaration.php index ab9acb0fe..1e09a4390 100644 --- a/src/Scaffolder/src/Declaration/CommandDeclaration.php +++ b/src/Scaffolder/src/Declaration/CommandDeclaration.php @@ -22,6 +22,7 @@ public function __construct( ?string $namespace = null, private readonly ?string $alias = null, private readonly ?string $description = null, + private readonly array $aliases = [], ) { parent::__construct($config, $name, $comment, $namespace); } @@ -73,6 +74,10 @@ public function declare(): void $commandDefinition['description'] = $this->description; } + if ($this->aliases !== []) { + $commandDefinition['aliases'] = $this->aliases; + } + $this->class->addAttribute(AsCommand::class, $commandDefinition); $this->class diff --git a/src/Scaffolder/tests/Command/CommandTest.php b/src/Scaffolder/tests/Command/CommandTest.php index 7d4564774..1c1447556 100644 --- a/src/Scaffolder/tests/Command/CommandTest.php +++ b/src/Scaffolder/tests/Command/CommandTest.php @@ -126,6 +126,26 @@ public function testScaffoldWithCustomNamespace(): void self::assertStringContainsString('App\Custom\Command', $content); } + public function testScaffoldWithAliases(): void + { + $this->className = $className = '\\Spiral\\Tests\\Scaffolder\\App\\Command\\AliasedCommand'; + + $this->console()->run('create:command', [ + 'name' => 'Aliased', + '--aliases' => ['al', 'aliased-cmd'], + ]); + + \clearstatcache(); + self::assertTrue(\class_exists($className)); + + $reflection = new \ReflectionClass($className); + + /** @var \Spiral\Console\Attribute\AsCommand $definition */ + $definition = $reflection->getAttributes()[0]->newInstance(); + + self::assertSame(['al', 'aliased-cmd'], $definition->aliases); + } + public function testShowInstructionAfterInstallation(): void { $this->className = $className = '\\Spiral\\Tests\\Scaffolder\\App\\Command\\ArgumentCommand';