From a71d7e5823c24343b82404ef9d34730043eba228 Mon Sep 17 00:00:00 2001 From: Tobias Hettler Date: Sun, 20 Feb 2022 19:50:10 +0100 Subject: [PATCH 1/6] First Working Draft --- .../Console/Attributes/Argument.php | 25 + .../Console/Attributes/CommandAttribute.php | 15 + src/Illuminate/Console/Attributes/Option.php | 37 ++ src/Illuminate/Console/Command.php | 79 ++- .../Console/Concerns/HasAttributeSyntax.php | 144 ++++++ .../Reflections/ArgumentReflection.php | 13 + .../Console/Reflections/CommandReflection.php | 106 ++++ .../Console/Reflections/InputReflection.php | 90 ++++ .../Console/Reflections/OptionReflection.php | 42 ++ .../Contracts/Console/ConsoleInput.php | 10 + tests/Console/CommandAttributesTest.php | 457 ++++++++++++++++++ tests/Console/Enums.php | 24 + 12 files changed, 1026 insertions(+), 16 deletions(-) create mode 100644 src/Illuminate/Console/Attributes/Argument.php create mode 100644 src/Illuminate/Console/Attributes/CommandAttribute.php create mode 100644 src/Illuminate/Console/Attributes/Option.php create mode 100644 src/Illuminate/Console/Concerns/HasAttributeSyntax.php create mode 100644 src/Illuminate/Console/Reflections/ArgumentReflection.php create mode 100644 src/Illuminate/Console/Reflections/CommandReflection.php create mode 100644 src/Illuminate/Console/Reflections/InputReflection.php create mode 100644 src/Illuminate/Console/Reflections/OptionReflection.php create mode 100644 src/Illuminate/Contracts/Console/ConsoleInput.php create mode 100644 tests/Console/CommandAttributesTest.php create mode 100644 tests/Console/Enums.php diff --git a/src/Illuminate/Console/Attributes/Argument.php b/src/Illuminate/Console/Attributes/Argument.php new file mode 100644 index 000000000000..e690d7e1ed21 --- /dev/null +++ b/src/Illuminate/Console/Attributes/Argument.php @@ -0,0 +1,25 @@ +description; + } + + public function getAlias(): ?string + { + return $this->as; + } +} diff --git a/src/Illuminate/Console/Attributes/CommandAttribute.php b/src/Illuminate/Console/Attributes/CommandAttribute.php new file mode 100644 index 000000000000..837e380732bf --- /dev/null +++ b/src/Illuminate/Console/Attributes/CommandAttribute.php @@ -0,0 +1,15 @@ +description; + } + + public function getAlias(): ?string + { + return $this->as; + } + + public function getShortcut(): ?string + { + return $this->shortcut; + } + + public function isNegatable(): bool + { + return $this->negatable; + } +} diff --git a/src/Illuminate/Console/Command.php b/src/Illuminate/Console/Command.php index 6d9ae8c89381..44eee94014a7 100755 --- a/src/Illuminate/Console/Command.php +++ b/src/Illuminate/Console/Command.php @@ -2,6 +2,8 @@ namespace Illuminate\Console; +use Illuminate\Console\Reflections\ArgumentReflection; +use Illuminate\Console\Reflections\CommandReflection; use Illuminate\Support\Traits\Macroable; use Symfony\Component\Console\Command\Command as SymfonyCommand; use Symfony\Component\Console\Input\InputInterface; @@ -12,6 +14,7 @@ class Command extends SymfonyCommand use Concerns\CallsCommands, Concerns\HasParameters, Concerns\InteractsWithIO, + Concerns\HasAttributeSyntax, Macroable; /** @@ -56,6 +59,13 @@ class Command extends SymfonyCommand */ protected $hidden = false; + /** + * The Reflection of the Command. + * + * @var CommandReflection + */ + protected CommandReflection $reflection; + /** * Create a new console command instance. * @@ -63,27 +73,34 @@ class Command extends SymfonyCommand */ public function __construct() { - // We will go ahead and set the name, description, and parameters on console - // commands just to make things a little easier on the developer. This is - // so they don't have to all be manually specified in the constructors. + $this->reflection = new CommandReflection($this); + $this->intiCommandData(); + $this->configureCommand(); + } + + /** + * Configure the console command using a fluent or attribute definition. + * + * We will go ahead and set the name, description, and parameters on console + * commands just to make things a little easier on the developer. This is + * so they don't have to all be manually specified in the constructors. + * + * @return void + */ + protected function configureCommand(): void + { if (isset($this->signature)) { $this->configureUsingFluentDefinition(); - } else { - parent::__construct($this->name); + return; } - // Once we have constructed the command, we'll set the description and other - // related properties of the command. If a signature wasn't used to build - // the command we'll set the arguments and the options on this command. - $this->setDescription((string) $this->description); - - $this->setHelp((string) $this->help); - - $this->setHidden($this->isHidden()); - - if (! isset($this->signature)) { - $this->specifyParameters(); + if ($this->reflection->usesInputAttributes()) { + $this->configureUsingAttributeDefinition(); + return; } + + parent::__construct($this->name); + $this->specifyParameters(); } /** @@ -131,6 +148,9 @@ public function run(InputInterface $input, OutputInterface $output): int */ protected function execute(InputInterface $input, OutputInterface $output) { + $this->hydrateArguments(); + $this->hydrateOptions(); + $method = method_exists($this, 'handle') ? 'handle' : '__invoke'; return (int) $this->laravel->call([$this, $method]); @@ -161,6 +181,33 @@ protected function resolveCommand($command) return $command; } + /** + * Once we have constructed the command, we'll set the description and other + * related properties of the command ether by the command attribute or by command properties + * them self. + * + * @return void + */ + protected function intiCommandData(): void + { + if ($this->reflection->usesCommandAttribute()) { + parent::__construct($this->name = $this->reflection->getName()); + $this->setDescription($this->reflection->getDescription()); + $this->setHelp($this->reflection->getHelp()); + $this->setHidden($this->reflection->isHidden()); + + return; + } + + if(!isset($this->signature)) { + parent::__construct($this->name); + } + + $this->setDescription((string) $this->description); + $this->setHelp((string) $this->help); + $this->setHidden($this->isHidden()); + } + /** * {@inheritdoc} * diff --git a/src/Illuminate/Console/Concerns/HasAttributeSyntax.php b/src/Illuminate/Console/Concerns/HasAttributeSyntax.php new file mode 100644 index 000000000000..e033863babe0 --- /dev/null +++ b/src/Illuminate/Console/Concerns/HasAttributeSyntax.php @@ -0,0 +1,144 @@ +configureArgumentsUsingAttributeDefinition(); + $this->configureOptionsUsingAttributeDefinition(); + } + + protected function configureArgumentsUsingAttributeDefinition(): void + { + $this->reflection + ->getArguments() + ->each(function (ArgumentReflection $argumentReflection) { + $this->getDefinition() + ->addArgument( + $this->propertyToArgument($argumentReflection) + ); + }); + } + + protected function configureOptionsUsingAttributeDefinition(): void + { + $this->reflection + ->getOptions() + ->each(function (OptionReflection $optionReflection) { + $this->getDefinition() + ->addOption($this->propertyToOption($optionReflection)); + }); + } + + protected function hydrateArguments(): void + { + $this->reflection + ->getArguments() + ->each(function (ArgumentReflection $argumentReflection) { + $this->{$argumentReflection->getName()} = $argumentReflection->castTo($this->argument($argumentReflection->getAlias() ?? $argumentReflection->getName())); + }); + } + + protected function hydrateOptions(): void + { + $this->reflection + ->getOptions() + ->each(function (OptionReflection $optionReflection) { + $consoleName = $optionReflection->getAlias() ?? $optionReflection->getName(); + + if (!$optionReflection->hasRequiredValue()) { + $this->{$optionReflection->getName()} = $optionReflection->castTo($this->option($consoleName)); + return; + } + + if ($this->option($consoleName) === null) { + return; + } + + $this->{$optionReflection->getName()} = $optionReflection->castTo($this->option($consoleName)); + + }); + } + + + protected function propertyToArgument(ArgumentReflection $argument): InputArgument + { + return match (true) { + $argument->isArray() && !$argument->isOptional() => $this->makeInputArgument($argument, + InputArgument::IS_ARRAY | InputArgument::REQUIRED), + + $argument->isArray() => $this->makeInputArgument($argument, InputArgument::IS_ARRAY, + $argument->getDefaultValue()), + + $argument->isOptional() || $argument->getDefaultValue() => $this->makeInputArgument($argument, + InputArgument::OPTIONAL, $argument->getDefaultValue()), + + default => $this->makeInputArgument($argument, InputArgument::REQUIRED), + }; + } + + protected function propertyToOption(OptionReflection $option): InputOption + { + return match (true) { + $option->hasValue() && $option->isArray() => $this->makeInputOption( + $option, + InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + $option->getDefaultValue() + ), + + $option->hasValue() && !$option->isOptional() => $this->makeInputOption($option, + InputOption::VALUE_REQUIRED), + + $option->hasValue() => $this->makeInputOption($option, InputOption::VALUE_OPTIONAL, + $option->getDefaultValue()), + + $option->isNegatable() => $this->makeInputOption( + $option, + InputOption::VALUE_NEGATABLE, + $option->getDefaultValue() !== null ? $option->getDefaultValue() : false + ), + + default => $this->makeInputOption($option, InputOption::VALUE_NONE), + }; + } + + protected function makeInputArgument( + ArgumentReflection $argument, + int $mode, + string|bool|int|float|array|null $default = null + ): InputArgument { + return new InputArgument( + $argument->getAlias() ?? $argument->getName(), + $mode, + $argument->getDescription(), + $default + ); + } + + protected function makeInputOption( + OptionReflection $option, + int $mode, + string|bool|int|float|array|null $default = null + ): InputOption { + return new InputOption( + $option->getAlias() ?? $option->getName(), + $option->getShortcut(), + $mode, + $option->getDescription(), + $default + ); + } +} diff --git a/src/Illuminate/Console/Reflections/ArgumentReflection.php b/src/Illuminate/Console/Reflections/ArgumentReflection.php new file mode 100644 index 000000000000..2f9792f4c7ba --- /dev/null +++ b/src/Illuminate/Console/Reflections/ArgumentReflection.php @@ -0,0 +1,13 @@ +getAttributes(Argument::class)); + } +} diff --git a/src/Illuminate/Console/Reflections/CommandReflection.php b/src/Illuminate/Console/Reflections/CommandReflection.php new file mode 100644 index 000000000000..4ad6a2be1538 --- /dev/null +++ b/src/Illuminate/Console/Reflections/CommandReflection.php @@ -0,0 +1,106 @@ +reflection = new \ReflectionClass($this->command); + $this->attribute = $this->initCommandAttribute(); + } + + public function usesAttributeSyntax(): bool + { + return $this->usesCommandAttribute() || $this->usesInputAttributes(); + } + + public function usesCommandAttribute(): bool + { + return $this->attribute !== null; + } + + public function usesInputAttributes(): bool + { + return $this->getArguments()->isNotEmpty() || $this->getOptions()->isNotEmpty(); + } + + public function getArguments(): Collection + { + return collect($this->reflection->getProperties()) + ->filter(fn(\ReflectionProperty $property) => ArgumentReflection::isArgument($property)) + ->map(fn(\ReflectionProperty $property) => new ArgumentReflection( + $property, + $property->getAttributes(Argument::class)[0]->newInstance() + ) + ); + } + + public function getOptions(): Collection + { + return collect($this->reflection->getProperties()) + ->filter(fn(\ReflectionProperty $property) => OptionReflection::isOption($property)) + ->map(fn(\ReflectionProperty $property) => new OptionReflection( + $property, + $property->getAttributes(Option::class)[0]->newInstance() + ) + ); + } + + public function getName(): ?string + { + if (!$this->usesCommandAttribute()) { + return $this->command->getName(); + } + + return $this->attribute->name; + } + + public function getDescription(): string + { + if (!$this->usesCommandAttribute()) { + return $this->command->getDescription(); + } + + return $this->attribute->description; + } + + public function getHelp(): string + { + if (!$this->usesCommandAttribute()) { + return $this->command->getHelp(); + } + + return $this->attribute->help; + } + + public function isHidden(): bool + { + if (!$this->usesCommandAttribute()) { + return $this->command->isHidden(); + } + + return $this->attribute->hidden; + } + + protected function initCommandAttribute(): ?CommandAttribute + { + $attributes = $this->reflection->getAttributes(CommandAttribute::class); + + if (empty($attributes)) { + return null; + } + + return $attributes[0]->newInstance(); + } +} diff --git a/src/Illuminate/Console/Reflections/InputReflection.php b/src/Illuminate/Console/Reflections/InputReflection.php new file mode 100644 index 000000000000..8db785cd771c --- /dev/null +++ b/src/Illuminate/Console/Reflections/InputReflection.php @@ -0,0 +1,90 @@ +property->getName(); + } + + public function getAlias(): ?string + { + return $this->consoleInput->getAlias(); + } + + public function getDescription(): string + { + return $this->consoleInput->getDescription(); + } + + public function getDefaultValue(): string|bool|int|float|array|null + { + return $this->property->hasDefaultValue() + ? $this->castFrom($this->property->getDefaultValue()) + : null; + } + + public function isOptional(): bool + { + return $this->property->hasDefaultValue() || $this->property->getType()?->allowsNull(); + } + + public function isArray(): bool + { + if (($type = $this->property->getType()) instanceof \ReflectionNamedType) { + return $type->getName() === 'array'; + } + + return false; + } + + public function castFrom(mixed $value): int|float|array|string|bool|null + { + return match (gettype($value)) { + 'integer', 'NULL', 'boolean', 'double', 'string', 'array' => $value, + 'object' => enum_exists($value::class) ? $this->castEnum($value) : $value, + default => $value, + }; + + } + + public function castEnum(object $value): int|float|array|string|bool|null + { + return (new \ReflectionEnum($value))->isBacked() + ? $value->value + : $value->name; + } + + public function castTo(int|array|float|string|bool|null $value): mixed + { + if (!($type = $this->property->getType())) { + return $value; + } + + if (! $type instanceof \ReflectionNamedType) { + return $value; + } + + if (!enum_exists($type->getName())){ + return $value; + } + + $enum = new \ReflectionEnum($type->getName()); + + return $enum->isBacked() + ? ($type->getName())::from((string) $value) + : $enum->getCase((string) $value)->getValue(); + } +} diff --git a/src/Illuminate/Console/Reflections/OptionReflection.php b/src/Illuminate/Console/Reflections/OptionReflection.php new file mode 100644 index 000000000000..512c703d93d8 --- /dev/null +++ b/src/Illuminate/Console/Reflections/OptionReflection.php @@ -0,0 +1,42 @@ +getAttributes(Option::class)); + } + + public function isNegatable(): bool + { + return $this->consoleInput->isNegatable(); + } + + public function hasRequiredValue(): bool + { + return $this->hasValue() && !$this->isOptional(); + } + + public function getShortcut(): ?string + { + return $this->consoleInput->getShortcut(); + } + + public function hasValue(): bool + { + if (($type = $this->property->getType()) instanceof \ReflectionNamedType) { + return $type->getName() !== 'bool'; + } + + return false; + } +} diff --git a/src/Illuminate/Contracts/Console/ConsoleInput.php b/src/Illuminate/Contracts/Console/ConsoleInput.php new file mode 100644 index 000000000000..97e2c9d07b97 --- /dev/null +++ b/src/Illuminate/Contracts/Console/ConsoleInput.php @@ -0,0 +1,10 @@ += 80100) { + include 'Enums.php'; +} + +class CommandAttributesTest extends TestCase +{ + public function testAttributeWillBeUsed() + { + $this->makeCommand(meta: 'name: "test:basic", description: "Basic Command description!", help: "Some Help.", hidden: true') + (function (Command $command) { + $this->assertSame('test:basic', $command->getName()); + $this->assertSame('Basic Command description!', $command->getDescription()); + $this->assertSame('Some Help.', $command->getHelp()); + $this->assertTrue($command->isHidden()); + }); + } + + public function testArgumentsWillBeRegisteredWithAttributeSyntax() + { + $command = new class extends Command { + protected $name = 'test'; + + #[Argument] + public string $requiredArgument; + + #[Argument] + public ?string $optionalArgument; + + #[Argument] + public string $defaultArgument = 'default_value'; + + public function handle() + { + + } + }; + + $definition = $command->getDefinition(); + + $command = $this->callCommand($command, [ + 'requiredArgument' => 'Argument_Required', + 'optionalArgument' => 'Argument_Optional', + 'defaultArgument' => 'Argument_Default', + ]); + + + $this->assertTrue($definition->getArgument('requiredArgument')->isRequired()); + $this->assertSame('Argument_Required', $command->requiredArgument); + + $this->assertFalse($definition->getArgument('optionalArgument')->isRequired()); + $this->assertSame('Argument_Optional', $command->optionalArgument); + + $this->assertSame('default_value', $definition->getArgument('defaultArgument')->getDefault()); + $this->assertSame('Argument_Default', $command->defaultArgument); + } + + public function testArrayArgumentsWillBeRegisteredWithAttributeSyntax() + { + $commandRequired = new class extends Command { + protected $name = 'test'; + + #[Argument] + public array $arrayArgument; + + public function handle() + { + + } + }; + + $commandOptional = new class extends Command { + protected $name = 'test'; + + #[Argument] + public ?array $optionalArrayArgument; + + public function handle() + { + + } + }; + + $commandDefault = new class extends Command { + protected $name = 'test'; + + #[Argument] + public array $defaultArrayArgument = ['Value A', 'Value B']; + + public function handle() + { + + } + }; + + + $commandRequired = $this->callCommand($commandRequired, [ + 'arrayArgument' => ['Array_Required'], + ]); + + $definition = $commandRequired->getDefinition(); + + $this->assertTrue($definition->getArgument('arrayArgument')->isArray()); + $this->assertTrue($definition->getArgument('arrayArgument')->isRequired()); + $this->assertSame(['Array_Required'], $commandRequired->arrayArgument); + + + $commandOptional = $this->callCommand($commandOptional, [ + 'optionalArrayArgument' => ['Array_Optional'], + ]); + + $definition = $commandOptional->getDefinition(); + + $this->assertTrue($definition->getArgument('optionalArrayArgument')->isArray()); + $this->assertFalse($definition->getArgument('optionalArrayArgument')->isRequired()); + $this->assertSame(['Array_Optional'], $commandOptional->optionalArrayArgument); + + + $commandDefault = $this->callCommand($commandDefault, [ + 'defaultArrayArgument' => ['Array_Default'], + ]); + + $definition = $commandDefault->getDefinition(); + + $this->assertTrue($definition->getArgument('defaultArrayArgument')->isArray()); + $this->assertFalse($definition->getArgument('defaultArrayArgument')->isRequired()); + $this->assertSame(['Value A', 'Value B'], $definition->getArgument('defaultArrayArgument')->getDefault()); + $this->assertSame(['Array_Default'], $commandDefault->defaultArrayArgument); + + $commandDefault = $this->callCommand($commandDefault, []); + $this->assertSame(['Value A', 'Value B'], $commandDefault->defaultArrayArgument); + + + } + + public function testOptionsWillBeRegisteredWithAttributeSyntax() + { + $command = new class extends Command { + protected $name = 'test'; + + #[Option] + public bool $option; + + #[Option] + public string $optionWithValue; + + #[Option] + public ?string $optionWithNullableValue; + + #[Option] + public string $optionWithDefaultValue = 'default'; + + public function handle(){} + }; + + $definition = $command->getDefinition(); + + $command = $this->callCommand($command, [ + '--option' => true, + '--optionWithValue' => 'Value A', + ]); + + $this->assertFalse($definition->getOption('option')->isValueOptional()); + $this->assertTrue($command->option); + + $this->assertTrue($definition->getOption('optionWithValue')->isValueRequired()); + $this->assertSame('Value A', $command->optionWithValue); + + $this->assertTrue($definition->getOption('optionWithNullableValue')->isValueOptional()); + $this->assertNull($command->optionWithNullableValue); + + $command = $this->callCommand($command, [ + '--optionWithNullableValue' => 'Value B', + ]); + $this->assertSame('Value B', $command->optionWithNullableValue); + + $this->assertSame('default', $definition->getOption('optionWithDefaultValue')->getDefault()); + + $command = $this->callCommand($command, [ + '--optionWithDefaultValue' => 'Value C', + ]); + $this->assertSame('Value C', $command->optionWithDefaultValue); + } + + public function testArrayOptionsWillBeRegisteredWithAttributeSyntax() + { + $command = new class extends Command { + protected $name = 'test'; + + #[Option] + public array $optionArray; + + #[Option] + public array $optionDefaultArray = ['default1', 'default2']; + + public function handle(){} + }; + + $definition = $command->getDefinition(); + + $command = $this->callCommand($command, []); + $this->assertSame([], $command->optionArray); + + $command = $this->callCommand($command, [ + '--optionArray' => ['Value A', 'Value B'], + ]); + + $this->assertTrue($definition->getOption('optionArray')->isArray()); + $this->assertSame(['Value A', 'Value B'],$command->optionArray); + + $this->assertTrue($definition->getOption('optionArray')->isArray()); + $this->assertTrue($definition->getOption('optionArray')->isValueOptional()); + $this->assertSame(['Value A', 'Value B'],$command->optionArray); + + $command = $this->callCommand($command, [ + '--optionDefaultArray' => ['Value C', 'Value D'], + ]); + + $this->assertSame(['Value C', 'Value D'], $command->optionDefaultArray); + + } + + public function testInputMetaDataWillBeRegisteredWithAttributeSyntax() + { + $command = new class extends Command { + protected $name = 'test'; + + #[Argument( + as: 'argumentAlias', + description: 'Argument Description', + )] + public string $argument = ''; + + #[Option( + as: 'optionAlias', + description: 'Option Description' + )] + public bool $option; + + public function handle(){} + }; + + $definition = $command->getDefinition(); + + $this->assertSame('Argument Description', $definition->getArgument('argumentAlias')->getDescription()); + $this->assertSame('Option Description', $definition->getOption('optionAlias')->getDescription()); + + $command = $this->callCommand($command, [ + 'argumentAlias' => 'Value', + '--optionAlias' => true, + ]); + + $this->assertSame('Value', $command->argument); + $this->assertSame(true, $command->option); + } + + public function testOptionShortcutWillBeRegisteredWithAttributeSyntax() + { + $command = new class extends Command { + protected $name = 'test'; + + #[Option( + shortcut: 'O' + )] + public string $option; + + public function handle(){} + }; + + $definition = $command->getDefinition(); + + $this->assertSame('O', $definition->getOption('option')->getShortcut()); + + $command = $this->callCommand($command, [ + '-O' => 'short', + ]); + + $this->assertSame('short', $command->option); + } + + public function testOptionNegatableWillBeRegisteredWithAttributeSyntax() + { + $command = new class extends Command { + protected $name = 'test'; + + #[Option( + negatable: true + )] + public bool $option; + + public function handle(){} + }; + + $definition = $command->getDefinition(); + + $this->assertTrue($definition->getOption('option')->isNegatable()); + + $command = $this->callCommand($command, [ + '--option' => true, + ]); + + $this->assertTrue($command->option); + + $command = $this->callCommand($command, [ + '--no-option' => true, + ]); + + $this->assertFalse($command->option); + } + + /** + * @requires PHP >= 8.1 + */ + public function testArgumentEnumsWillBeCasted() + { + if (PHP_VERSION_ID <= 80100) { + $this->markTestSkipped('Enum Casting test skipped caused by PHP version.'); + return; + } + + $command = new class extends Command { + protected $name = 'test'; + + #[Argument] + public Enum $enumArgument; + + #[Argument] + public StringEnum $enumStringArgument; + + #[Argument] + public IntEnum $enumIntArgument; + + #[Argument] + public StringEnum $enumDefaultArgument = StringEnum::B; + + public function handle(){} + }; + + $command = $this->callCommand($command, [ + 'enumArgument' => 'B', + 'enumStringArgument' => 'String B', + 'enumIntArgument' => 2, + ]); + + $this->assertSame(Enum::B, $command->enumArgument); + + $this->assertSame(StringEnum::B, $command->enumStringArgument); + + $this->assertSame(IntEnum::B, $command->enumIntArgument); + + $this->assertSame(StringEnum::B, $command->enumDefaultArgument); + } + + /** + * @requires PHP >= 8.1 + */ + public function testOptionEnumsWillBeCasted() + { + if (PHP_VERSION_ID <= 80100) { + $this->markTestSkipped('Enum Casting test skipped caused by PHP version.'); + return; + } + + $command = new class extends Command { + protected $name = 'test'; + + #[Option] + public Enum $enumOption; + + #[Option] + public StringEnum $enumStringOption; + + #[Option] + public IntEnum $enumIntOption; + + #[Option] + public StringEnum $enumDefaultOption = StringEnum::B; + + public function handle(){} + }; + + $command = $this->callCommand($command, [ + '--enumOption' => 'B', + '--enumStringOption' => 'String B', + '--enumIntOption' => 2, + ]); + + $this->assertSame(Enum::B, $command->enumOption); + + $this->assertSame(StringEnum::B, $command->enumStringOption); + + $this->assertSame(IntEnum::B, $command->enumIntOption); + + $this->assertSame(StringEnum::B, $command->enumDefaultOption); + } + + protected function makeCommand(string $properties = '', ?string $meta = null) + { + return function (callable $testScenario) use ($properties, $meta) { + $name = 'Test'.Str::random(); + $meta ??= "name: '{$name}'"; + $filePath = __DIR__."/Temp/{$name}.php"; + $namespace = '\\Illuminate\\Tests\\Console\\Temp\\'.$name; + + try { + $file = new Filesystem(); + $file->put( + $filePath, + <<delete($filePath); + } + }; + } + + protected function callCommand(Command $command, array $input): Command + { + $application = app(); + $command->setLaravel($application); + + $input = new ArrayInput($input); + $output = new NullOutput(); + + $command->run($input, $output); + return $command; + } +} diff --git a/tests/Console/Enums.php b/tests/Console/Enums.php new file mode 100644 index 000000000000..ccce707f4ef0 --- /dev/null +++ b/tests/Console/Enums.php @@ -0,0 +1,24 @@ + Date: Sun, 20 Feb 2022 21:53:31 +0100 Subject: [PATCH 2/6] Remove magic class creation form tests --- tests/Console/CommandAttributesTest.php | 54 ++++----------------- tests/Console/fixtures/AttributeCommand.php | 19 ++++++++ 2 files changed, 28 insertions(+), 45 deletions(-) create mode 100644 tests/Console/fixtures/AttributeCommand.php diff --git a/tests/Console/CommandAttributesTest.php b/tests/Console/CommandAttributesTest.php index 6b365bf77eba..53d22ccbfd22 100644 --- a/tests/Console/CommandAttributesTest.php +++ b/tests/Console/CommandAttributesTest.php @@ -6,6 +6,7 @@ use Illuminate\Console\Attributes\Option; use Illuminate\Console\Command; use Illuminate\Filesystem\Filesystem; +use Illuminate\Tests\Console\fixtures\AttributeCommand; use PHPUnit\Framework\TestCase; use Illuminate\Support\Str; use Symfony\Component\Console\Input\ArrayInput; @@ -19,13 +20,13 @@ class CommandAttributesTest extends TestCase { public function testAttributeWillBeUsed() { - $this->makeCommand(meta: 'name: "test:basic", description: "Basic Command description!", help: "Some Help.", hidden: true') - (function (Command $command) { - $this->assertSame('test:basic', $command->getName()); - $this->assertSame('Basic Command description!', $command->getDescription()); - $this->assertSame('Some Help.', $command->getHelp()); - $this->assertTrue($command->isHidden()); - }); + $command = new AttributeCommand(); + $command= $this->callCommand($command); + + $this->assertSame('test:basic', $command->getName()); + $this->assertSame('Basic Command description!', $command->getDescription()); + $this->assertSame('Some Help.', $command->getHelp()); + $this->assertTrue($command->isHidden()); } public function testArgumentsWillBeRegisteredWithAttributeSyntax() @@ -406,44 +407,7 @@ public function handle(){} $this->assertSame(StringEnum::B, $command->enumDefaultOption); } - protected function makeCommand(string $properties = '', ?string $meta = null) - { - return function (callable $testScenario) use ($properties, $meta) { - $name = 'Test'.Str::random(); - $meta ??= "name: '{$name}'"; - $filePath = __DIR__."/Temp/{$name}.php"; - $namespace = '\\Illuminate\\Tests\\Console\\Temp\\'.$name; - - try { - $file = new Filesystem(); - $file->put( - $filePath, - <<delete($filePath); - } - }; - } - - protected function callCommand(Command $command, array $input): Command + protected function callCommand(Command $command, array $input = []): Command { $application = app(); $command->setLaravel($application); diff --git a/tests/Console/fixtures/AttributeCommand.php b/tests/Console/fixtures/AttributeCommand.php new file mode 100644 index 000000000000..9c2767568090 --- /dev/null +++ b/tests/Console/fixtures/AttributeCommand.php @@ -0,0 +1,19 @@ + Date: Sun, 20 Feb 2022 22:11:55 +0100 Subject: [PATCH 3/6] Fix PHP 8 issue --- src/Illuminate/Console/Reflections/InputReflection.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Console/Reflections/InputReflection.php b/src/Illuminate/Console/Reflections/InputReflection.php index 8db785cd771c..504161c28c42 100644 --- a/src/Illuminate/Console/Reflections/InputReflection.php +++ b/src/Illuminate/Console/Reflections/InputReflection.php @@ -54,7 +54,7 @@ public function castFrom(mixed $value): int|float|array|string|bool|null { return match (gettype($value)) { 'integer', 'NULL', 'boolean', 'double', 'string', 'array' => $value, - 'object' => enum_exists($value::class) ? $this->castEnum($value) : $value, + 'object' => function_exists('enum_exists') && enum_exists($value::class) ? $this->castEnum($value) : $value, default => $value, }; @@ -77,6 +77,10 @@ public function castTo(int|array|float|string|bool|null $value): mixed return $value; } + if (!function_exists('enum_exists')){ + return $value; + } + if (!enum_exists($type->getName())){ return $value; } From d56261643ebacb76d2a17c04083f308f867f294e Mon Sep 17 00:00:00 2001 From: Tobias Hettler Date: Mon, 21 Feb 2022 12:40:24 +0100 Subject: [PATCH 4/6] Rename CommandAttribute to ArtisanCommand; Add aliases to command names; Some Refactoring --- ...{CommandAttribute.php => ArtisanCommand.php} | 3 ++- src/Illuminate/Console/Command.php | 7 +------ .../Console/Concerns/HasAttributeSyntax.php | 11 ++++++++++- .../Console/Reflections/CommandReflection.php | 17 +++++++++++++---- tests/Console/CommandAttributesTest.php | 1 + tests/Console/fixtures/AttributeCommand.php | 5 +++-- 6 files changed, 30 insertions(+), 14 deletions(-) rename src/Illuminate/Console/Attributes/{CommandAttribute.php => ArtisanCommand.php} (83%) diff --git a/src/Illuminate/Console/Attributes/CommandAttribute.php b/src/Illuminate/Console/Attributes/ArtisanCommand.php similarity index 83% rename from src/Illuminate/Console/Attributes/CommandAttribute.php rename to src/Illuminate/Console/Attributes/ArtisanCommand.php index 837e380732bf..158e27b3f5ee 100644 --- a/src/Illuminate/Console/Attributes/CommandAttribute.php +++ b/src/Illuminate/Console/Attributes/ArtisanCommand.php @@ -3,12 +3,13 @@ namespace Illuminate\Console\Attributes; #[\Attribute(\Attribute::TARGET_CLASS)] -class CommandAttribute +class ArtisanCommand { public function __construct( public string $name, public string $description = '', public string $help = '', + public array $aliases = [], public bool $hidden = false, ) { } diff --git a/src/Illuminate/Console/Command.php b/src/Illuminate/Console/Command.php index 44eee94014a7..da46a6db5147 100755 --- a/src/Illuminate/Console/Command.php +++ b/src/Illuminate/Console/Command.php @@ -2,7 +2,6 @@ namespace Illuminate\Console; -use Illuminate\Console\Reflections\ArgumentReflection; use Illuminate\Console\Reflections\CommandReflection; use Illuminate\Support\Traits\Macroable; use Symfony\Component\Console\Command\Command as SymfonyCommand; @@ -191,11 +190,7 @@ protected function resolveCommand($command) protected function intiCommandData(): void { if ($this->reflection->usesCommandAttribute()) { - parent::__construct($this->name = $this->reflection->getName()); - $this->setDescription($this->reflection->getDescription()); - $this->setHelp($this->reflection->getHelp()); - $this->setHidden($this->reflection->isHidden()); - + $this->initCommandDataFromAttribute(); return; } diff --git a/src/Illuminate/Console/Concerns/HasAttributeSyntax.php b/src/Illuminate/Console/Concerns/HasAttributeSyntax.php index e033863babe0..9b8904ba69d5 100644 --- a/src/Illuminate/Console/Concerns/HasAttributeSyntax.php +++ b/src/Illuminate/Console/Concerns/HasAttributeSyntax.php @@ -15,12 +15,21 @@ trait HasAttributeSyntax * * @return void */ - protected function configureUsingAttributeDefinition() + protected function configureUsingAttributeDefinition(): void { $this->configureArgumentsUsingAttributeDefinition(); $this->configureOptionsUsingAttributeDefinition(); } + protected function initCommandDataFromAttribute(): void + { + parent::__construct($this->name = $this->reflection->getName()); + $this->setDescription($this->reflection->getDescription()); + $this->setHelp($this->reflection->getHelp()); + $this->setHidden($this->reflection->isHidden()); + $this->setAliases($this->reflection->getAliases()); + } + protected function configureArgumentsUsingAttributeDefinition(): void { $this->reflection diff --git a/src/Illuminate/Console/Reflections/CommandReflection.php b/src/Illuminate/Console/Reflections/CommandReflection.php index 4ad6a2be1538..27288e519f64 100644 --- a/src/Illuminate/Console/Reflections/CommandReflection.php +++ b/src/Illuminate/Console/Reflections/CommandReflection.php @@ -3,7 +3,7 @@ namespace Illuminate\Console\Reflections; use Illuminate\Console\Attributes\Argument; -use Illuminate\Console\Attributes\CommandAttribute; +use Illuminate\Console\Attributes\ArtisanCommand; use Illuminate\Console\Attributes\Option; use Illuminate\Console\Command; use Illuminate\Support\Collection; @@ -11,7 +11,7 @@ class CommandReflection { public \ReflectionClass $reflection; - public ?CommandAttribute $attribute; + public ?ArtisanCommand $attribute; public function __construct( public Command $command @@ -93,9 +93,18 @@ public function isHidden(): bool return $this->attribute->hidden; } - protected function initCommandAttribute(): ?CommandAttribute + public function getAliases(): array { - $attributes = $this->reflection->getAttributes(CommandAttribute::class); + if (!$this->usesCommandAttribute()) { + return []; + } + + return $this->attribute->aliases; + } + + protected function initCommandAttribute(): ?ArtisanCommand + { + $attributes = $this->reflection->getAttributes(ArtisanCommand::class); if (empty($attributes)) { return null; diff --git a/tests/Console/CommandAttributesTest.php b/tests/Console/CommandAttributesTest.php index 53d22ccbfd22..27df3a454c70 100644 --- a/tests/Console/CommandAttributesTest.php +++ b/tests/Console/CommandAttributesTest.php @@ -27,6 +27,7 @@ public function testAttributeWillBeUsed() $this->assertSame('Basic Command description!', $command->getDescription()); $this->assertSame('Some Help.', $command->getHelp()); $this->assertTrue($command->isHidden()); + $this->assertSame(['alias:basic'], $command->getAliases()); } public function testArgumentsWillBeRegisteredWithAttributeSyntax() diff --git a/tests/Console/fixtures/AttributeCommand.php b/tests/Console/fixtures/AttributeCommand.php index 9c2767568090..bce1825e1436 100644 --- a/tests/Console/fixtures/AttributeCommand.php +++ b/tests/Console/fixtures/AttributeCommand.php @@ -2,13 +2,14 @@ namespace Illuminate\Tests\Console\fixtures; -use Illuminate\Console\Attributes\CommandAttribute; +use Illuminate\Console\Attributes\ArtisanCommand; use Illuminate\Console\Command; -#[CommandAttribute( +#[ArtisanCommand( name: "test:basic", description: "Basic Command description!", help: "Some Help.", + aliases: ['alias:basic'], hidden: true )] class AttributeCommand extends Command From 354aacad1a1b20fbce09b427da05bc06a7794696 Mon Sep 17 00:00:00 2001 From: Tobias Hettler Date: Mon, 21 Feb 2022 13:22:56 +0100 Subject: [PATCH 5/6] Fix StyleCi --- src/Illuminate/Console/Command.php | 5 +- .../Console/Concerns/HasAttributeSyntax.php | 9 +-- .../Console/Reflections/CommandReflection.php | 18 ++--- .../Console/Reflections/InputReflection.php | 9 +-- .../Console/Reflections/OptionReflection.php | 4 +- .../Contracts/Console/ConsoleInput.php | 2 +- tests/Console/CommandAttributesTest.php | 75 +++++++++++-------- 7 files changed, 65 insertions(+), 57 deletions(-) diff --git a/src/Illuminate/Console/Command.php b/src/Illuminate/Console/Command.php index da46a6db5147..3afc79072d68 100755 --- a/src/Illuminate/Console/Command.php +++ b/src/Illuminate/Console/Command.php @@ -90,11 +90,13 @@ protected function configureCommand(): void { if (isset($this->signature)) { $this->configureUsingFluentDefinition(); + return; } if ($this->reflection->usesInputAttributes()) { $this->configureUsingAttributeDefinition(); + return; } @@ -191,10 +193,11 @@ protected function intiCommandData(): void { if ($this->reflection->usesCommandAttribute()) { $this->initCommandDataFromAttribute(); + return; } - if(!isset($this->signature)) { + if ( !isset($this->signature)) { parent::__construct($this->name); } diff --git a/src/Illuminate/Console/Concerns/HasAttributeSyntax.php b/src/Illuminate/Console/Concerns/HasAttributeSyntax.php index 9b8904ba69d5..959fb0b2fd66 100644 --- a/src/Illuminate/Console/Concerns/HasAttributeSyntax.php +++ b/src/Illuminate/Console/Concerns/HasAttributeSyntax.php @@ -9,7 +9,6 @@ trait HasAttributeSyntax { - /** * Configure the console command using an Attribute definition. * @@ -68,7 +67,7 @@ protected function hydrateOptions(): void ->each(function (OptionReflection $optionReflection) { $consoleName = $optionReflection->getAlias() ?? $optionReflection->getName(); - if (!$optionReflection->hasRequiredValue()) { + if (! $optionReflection->hasRequiredValue()) { $this->{$optionReflection->getName()} = $optionReflection->castTo($this->option($consoleName)); return; } @@ -78,15 +77,13 @@ protected function hydrateOptions(): void } $this->{$optionReflection->getName()} = $optionReflection->castTo($this->option($consoleName)); - }); } - protected function propertyToArgument(ArgumentReflection $argument): InputArgument { return match (true) { - $argument->isArray() && !$argument->isOptional() => $this->makeInputArgument($argument, + $argument->isArray() && ! $argument->isOptional() => $this->makeInputArgument($argument, InputArgument::IS_ARRAY | InputArgument::REQUIRED), $argument->isArray() => $this->makeInputArgument($argument, InputArgument::IS_ARRAY, @@ -108,7 +105,7 @@ protected function propertyToOption(OptionReflection $option): InputOption $option->getDefaultValue() ), - $option->hasValue() && !$option->isOptional() => $this->makeInputOption($option, + $option->hasValue() && ! $option->isOptional() => $this->makeInputOption($option, InputOption::VALUE_REQUIRED), $option->hasValue() => $this->makeInputOption($option, InputOption::VALUE_OPTIONAL, diff --git a/src/Illuminate/Console/Reflections/CommandReflection.php b/src/Illuminate/Console/Reflections/CommandReflection.php index 27288e519f64..af0f3bc8a91a 100644 --- a/src/Illuminate/Console/Reflections/CommandReflection.php +++ b/src/Illuminate/Console/Reflections/CommandReflection.php @@ -38,8 +38,8 @@ public function usesInputAttributes(): bool public function getArguments(): Collection { return collect($this->reflection->getProperties()) - ->filter(fn(\ReflectionProperty $property) => ArgumentReflection::isArgument($property)) - ->map(fn(\ReflectionProperty $property) => new ArgumentReflection( + ->filter(fn (\ReflectionProperty $property) => ArgumentReflection::isArgument($property)) + ->map(fn (\ReflectionProperty $property) => new ArgumentReflection( $property, $property->getAttributes(Argument::class)[0]->newInstance() ) @@ -49,8 +49,8 @@ public function getArguments(): Collection public function getOptions(): Collection { return collect($this->reflection->getProperties()) - ->filter(fn(\ReflectionProperty $property) => OptionReflection::isOption($property)) - ->map(fn(\ReflectionProperty $property) => new OptionReflection( + ->filter(fn (\ReflectionProperty $property) => OptionReflection::isOption($property)) + ->map(fn (\ReflectionProperty $property) => new OptionReflection( $property, $property->getAttributes(Option::class)[0]->newInstance() ) @@ -59,7 +59,7 @@ public function getOptions(): Collection public function getName(): ?string { - if (!$this->usesCommandAttribute()) { + if (! $this->usesCommandAttribute()) { return $this->command->getName(); } @@ -68,7 +68,7 @@ public function getName(): ?string public function getDescription(): string { - if (!$this->usesCommandAttribute()) { + if (! $this->usesCommandAttribute()) { return $this->command->getDescription(); } @@ -77,7 +77,7 @@ public function getDescription(): string public function getHelp(): string { - if (!$this->usesCommandAttribute()) { + if (! $this->usesCommandAttribute()) { return $this->command->getHelp(); } @@ -86,7 +86,7 @@ public function getHelp(): string public function isHidden(): bool { - if (!$this->usesCommandAttribute()) { + if (! $this->usesCommandAttribute()) { return $this->command->isHidden(); } @@ -95,7 +95,7 @@ public function isHidden(): bool public function getAliases(): array { - if (!$this->usesCommandAttribute()) { + if (! $this->usesCommandAttribute()) { return []; } diff --git a/src/Illuminate/Console/Reflections/InputReflection.php b/src/Illuminate/Console/Reflections/InputReflection.php index 504161c28c42..39a1f36c6ad7 100644 --- a/src/Illuminate/Console/Reflections/InputReflection.php +++ b/src/Illuminate/Console/Reflections/InputReflection.php @@ -54,10 +54,9 @@ public function castFrom(mixed $value): int|float|array|string|bool|null { return match (gettype($value)) { 'integer', 'NULL', 'boolean', 'double', 'string', 'array' => $value, - 'object' => function_exists('enum_exists') && enum_exists($value::class) ? $this->castEnum($value) : $value, + 'object' => function_exists('enum_exists') && enum_exists($value::class) ? $this->castEnum($value) : $value, default => $value, }; - } public function castEnum(object $value): int|float|array|string|bool|null @@ -69,7 +68,7 @@ public function castEnum(object $value): int|float|array|string|bool|null public function castTo(int|array|float|string|bool|null $value): mixed { - if (!($type = $this->property->getType())) { + if (! ($type = $this->property->getType())) { return $value; } @@ -77,11 +76,11 @@ public function castTo(int|array|float|string|bool|null $value): mixed return $value; } - if (!function_exists('enum_exists')){ + if (! function_exists('enum_exists')){ return $value; } - if (!enum_exists($type->getName())){ + if (! enum_exists($type->getName())){ return $value; } diff --git a/src/Illuminate/Console/Reflections/OptionReflection.php b/src/Illuminate/Console/Reflections/OptionReflection.php index 512c703d93d8..8c81b8e5f960 100644 --- a/src/Illuminate/Console/Reflections/OptionReflection.php +++ b/src/Illuminate/Console/Reflections/OptionReflection.php @@ -13,7 +13,7 @@ public function __construct(\ReflectionProperty $property, Option $consoleInput) public static function isOption(\ReflectionProperty $property): bool { - return !empty($property->getAttributes(Option::class)); + return ! empty($property->getAttributes(Option::class)); } public function isNegatable(): bool @@ -23,7 +23,7 @@ public function isNegatable(): bool public function hasRequiredValue(): bool { - return $this->hasValue() && !$this->isOptional(); + return $this->hasValue() && ! $this->isOptional(); } public function getShortcut(): ?string diff --git a/src/Illuminate/Contracts/Console/ConsoleInput.php b/src/Illuminate/Contracts/Console/ConsoleInput.php index 97e2c9d07b97..cdaa1cf0a5f6 100644 --- a/src/Illuminate/Contracts/Console/ConsoleInput.php +++ b/src/Illuminate/Contracts/Console/ConsoleInput.php @@ -6,5 +6,5 @@ interface ConsoleInput { public function getDescription(): string; - public function getAlias(): ? string; + public function getAlias(): ?string; } diff --git a/tests/Console/CommandAttributesTest.php b/tests/Console/CommandAttributesTest.php index 27df3a454c70..967e241a5a8a 100644 --- a/tests/Console/CommandAttributesTest.php +++ b/tests/Console/CommandAttributesTest.php @@ -5,10 +5,8 @@ use Illuminate\Console\Attributes\Argument; use Illuminate\Console\Attributes\Option; use Illuminate\Console\Command; -use Illuminate\Filesystem\Filesystem; use Illuminate\Tests\Console\fixtures\AttributeCommand; use PHPUnit\Framework\TestCase; -use Illuminate\Support\Str; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\NullOutput; @@ -20,8 +18,8 @@ class CommandAttributesTest extends TestCase { public function testAttributeWillBeUsed() { - $command = new AttributeCommand(); - $command= $this->callCommand($command); + $command = new AttributeCommand(); + $command = $this->callCommand($command); $this->assertSame('test:basic', $command->getName()); $this->assertSame('Basic Command description!', $command->getDescription()); @@ -32,7 +30,8 @@ public function testAttributeWillBeUsed() public function testArgumentsWillBeRegisteredWithAttributeSyntax() { - $command = new class extends Command { + $command = new class extends Command + { protected $name = 'test'; #[Argument] @@ -46,7 +45,6 @@ public function testArgumentsWillBeRegisteredWithAttributeSyntax() public function handle() { - } }; @@ -58,7 +56,6 @@ public function handle() 'defaultArgument' => 'Argument_Default', ]); - $this->assertTrue($definition->getArgument('requiredArgument')->isRequired()); $this->assertSame('Argument_Required', $command->requiredArgument); @@ -71,7 +68,8 @@ public function handle() public function testArrayArgumentsWillBeRegisteredWithAttributeSyntax() { - $commandRequired = new class extends Command { + $commandRequired = new class extends Command + { protected $name = 'test'; #[Argument] @@ -79,7 +77,6 @@ public function testArrayArgumentsWillBeRegisteredWithAttributeSyntax() public function handle() { - } }; @@ -91,11 +88,11 @@ public function handle() public function handle() { - } }; - $commandDefault = new class extends Command { + $commandDefault = new class extends Command + { protected $name = 'test'; #[Argument] @@ -103,11 +100,9 @@ public function handle() public function handle() { - } }; - $commandRequired = $this->callCommand($commandRequired, [ 'arrayArgument' => ['Array_Required'], ]); @@ -118,7 +113,6 @@ public function handle() $this->assertTrue($definition->getArgument('arrayArgument')->isRequired()); $this->assertSame(['Array_Required'], $commandRequired->arrayArgument); - $commandOptional = $this->callCommand($commandOptional, [ 'optionalArrayArgument' => ['Array_Optional'], ]); @@ -129,7 +123,6 @@ public function handle() $this->assertFalse($definition->getArgument('optionalArrayArgument')->isRequired()); $this->assertSame(['Array_Optional'], $commandOptional->optionalArrayArgument); - $commandDefault = $this->callCommand($commandDefault, [ 'defaultArrayArgument' => ['Array_Default'], ]); @@ -143,13 +136,12 @@ public function handle() $commandDefault = $this->callCommand($commandDefault, []); $this->assertSame(['Value A', 'Value B'], $commandDefault->defaultArrayArgument); - - } public function testOptionsWillBeRegisteredWithAttributeSyntax() { - $command = new class extends Command { + $command = new class extends Command + { protected $name = 'test'; #[Option] @@ -164,7 +156,9 @@ public function testOptionsWillBeRegisteredWithAttributeSyntax() #[Option] public string $optionWithDefaultValue = 'default'; - public function handle(){} + public function handle() + { + } }; $definition = $command->getDefinition(); @@ -198,7 +192,8 @@ public function handle(){} public function testArrayOptionsWillBeRegisteredWithAttributeSyntax() { - $command = new class extends Command { + $command = new class extends Command + { protected $name = 'test'; #[Option] @@ -207,7 +202,9 @@ public function testArrayOptionsWillBeRegisteredWithAttributeSyntax() #[Option] public array $optionDefaultArray = ['default1', 'default2']; - public function handle(){} + public function handle() + { + } }; $definition = $command->getDefinition(); @@ -220,23 +217,23 @@ public function handle(){} ]); $this->assertTrue($definition->getOption('optionArray')->isArray()); - $this->assertSame(['Value A', 'Value B'],$command->optionArray); + $this->assertSame(['Value A', 'Value B'], $command->optionArray); $this->assertTrue($definition->getOption('optionArray')->isArray()); $this->assertTrue($definition->getOption('optionArray')->isValueOptional()); - $this->assertSame(['Value A', 'Value B'],$command->optionArray); + $this->assertSame(['Value A', 'Value B'], $command->optionArray); $command = $this->callCommand($command, [ '--optionDefaultArray' => ['Value C', 'Value D'], ]); $this->assertSame(['Value C', 'Value D'], $command->optionDefaultArray); - } public function testInputMetaDataWillBeRegisteredWithAttributeSyntax() { - $command = new class extends Command { + $command = new class extends Command + { protected $name = 'test'; #[Argument( @@ -251,7 +248,9 @@ public function testInputMetaDataWillBeRegisteredWithAttributeSyntax() )] public bool $option; - public function handle(){} + public function handle() + { + } }; $definition = $command->getDefinition(); @@ -270,7 +269,8 @@ public function handle(){} public function testOptionShortcutWillBeRegisteredWithAttributeSyntax() { - $command = new class extends Command { + $command = new class extends Command + { protected $name = 'test'; #[Option( @@ -294,7 +294,8 @@ public function handle(){} public function testOptionNegatableWillBeRegisteredWithAttributeSyntax() { - $command = new class extends Command { + $command = new class extends Command + { protected $name = 'test'; #[Option( @@ -302,7 +303,9 @@ public function testOptionNegatableWillBeRegisteredWithAttributeSyntax() )] public bool $option; - public function handle(){} + public function handle() + { + } }; $definition = $command->getDefinition(); @@ -332,7 +335,8 @@ public function testArgumentEnumsWillBeCasted() return; } - $command = new class extends Command { + $command = new class extends Command + { protected $name = 'test'; #[Argument] @@ -347,7 +351,9 @@ public function testArgumentEnumsWillBeCasted() #[Argument] public StringEnum $enumDefaultArgument = StringEnum::B; - public function handle(){} + public function handle() + { + } }; $command = $this->callCommand($command, [ @@ -375,7 +381,8 @@ public function testOptionEnumsWillBeCasted() return; } - $command = new class extends Command { + $command = new class extends Command + { protected $name = 'test'; #[Option] @@ -390,7 +397,9 @@ public function testOptionEnumsWillBeCasted() #[Option] public StringEnum $enumDefaultOption = StringEnum::B; - public function handle(){} + public function handle() + { + } }; $command = $this->callCommand($command, [ From 2767241bbc9352b01792210e7d4e268108255acc Mon Sep 17 00:00:00 2001 From: Tobias Hettler Date: Mon, 21 Feb 2022 13:27:40 +0100 Subject: [PATCH 6/6] Fix StyleCi --- src/Illuminate/Console/Command.php | 2 +- src/Illuminate/Console/Concerns/HasAttributeSyntax.php | 1 + src/Illuminate/Console/Reflections/InputReflection.php | 6 ++---- tests/Console/CommandAttributesTest.php | 10 ++++++++-- tests/Console/fixtures/AttributeCommand.php | 10 +++++----- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Illuminate/Console/Command.php b/src/Illuminate/Console/Command.php index 3afc79072d68..f13567253c7d 100755 --- a/src/Illuminate/Console/Command.php +++ b/src/Illuminate/Console/Command.php @@ -197,7 +197,7 @@ protected function intiCommandData(): void return; } - if ( !isset($this->signature)) { + if (! isset($this->signature)) { parent::__construct($this->name); } diff --git a/src/Illuminate/Console/Concerns/HasAttributeSyntax.php b/src/Illuminate/Console/Concerns/HasAttributeSyntax.php index 959fb0b2fd66..6cf380e04c9d 100644 --- a/src/Illuminate/Console/Concerns/HasAttributeSyntax.php +++ b/src/Illuminate/Console/Concerns/HasAttributeSyntax.php @@ -69,6 +69,7 @@ protected function hydrateOptions(): void if (! $optionReflection->hasRequiredValue()) { $this->{$optionReflection->getName()} = $optionReflection->castTo($this->option($consoleName)); + return; } diff --git a/src/Illuminate/Console/Reflections/InputReflection.php b/src/Illuminate/Console/Reflections/InputReflection.php index 39a1f36c6ad7..b6954461b761 100644 --- a/src/Illuminate/Console/Reflections/InputReflection.php +++ b/src/Illuminate/Console/Reflections/InputReflection.php @@ -3,8 +3,6 @@ namespace Illuminate\Console\Reflections; use Illuminate\Contracts\Console\ConsoleInput; -use Illuminate\Tests\Console\StringEnum; -use Illuminate\Validation\Rules\Enum; abstract class InputReflection { @@ -76,11 +74,11 @@ public function castTo(int|array|float|string|bool|null $value): mixed return $value; } - if (! function_exists('enum_exists')){ + if (! function_exists('enum_exists')) { return $value; } - if (! enum_exists($type->getName())){ + if (! enum_exists($type->getName())) { return $value; } diff --git a/tests/Console/CommandAttributesTest.php b/tests/Console/CommandAttributesTest.php index 967e241a5a8a..64af94abfcd6 100644 --- a/tests/Console/CommandAttributesTest.php +++ b/tests/Console/CommandAttributesTest.php @@ -80,7 +80,8 @@ public function handle() } }; - $commandOptional = new class extends Command { + $commandOptional = new class extends Command + { protected $name = 'test'; #[Argument] @@ -278,7 +279,9 @@ public function testOptionShortcutWillBeRegisteredWithAttributeSyntax() )] public string $option; - public function handle(){} + public function handle() + { + } }; $definition = $command->getDefinition(); @@ -332,6 +335,7 @@ public function testArgumentEnumsWillBeCasted() { if (PHP_VERSION_ID <= 80100) { $this->markTestSkipped('Enum Casting test skipped caused by PHP version.'); + return; } @@ -378,6 +382,7 @@ public function testOptionEnumsWillBeCasted() { if (PHP_VERSION_ID <= 80100) { $this->markTestSkipped('Enum Casting test skipped caused by PHP version.'); + return; } @@ -426,6 +431,7 @@ protected function callCommand(Command $command, array $input = []): Command $output = new NullOutput(); $command->run($input, $output); + return $command; } } diff --git a/tests/Console/fixtures/AttributeCommand.php b/tests/Console/fixtures/AttributeCommand.php index bce1825e1436..397c60e27392 100644 --- a/tests/Console/fixtures/AttributeCommand.php +++ b/tests/Console/fixtures/AttributeCommand.php @@ -6,15 +6,15 @@ use Illuminate\Console\Command; #[ArtisanCommand( - name: "test:basic", - description: "Basic Command description!", - help: "Some Help.", + name: 'test:basic', + description: 'Basic Command description!', + help: 'Some Help.', aliases: ['alias:basic'], hidden: true )] class AttributeCommand extends Command { - public function handle(){ - + public function handle() + { } }