diff --git a/docs/command-definition.md b/docs/command-definition.md index 027e9a8..1cd900a 100644 --- a/docs/command-definition.md +++ b/docs/command-definition.md @@ -37,11 +37,12 @@ Options are always optional (duh). If an option is required, then it should be a ## Default values ```php -$app->command('greet [firstname] [lastname]', function () { +$app->command('greet [firstname] [lastname] [--age=]', function () { // ... })->defaults([ 'firstname' => 'John', 'lastname' => 'Doe', + 'age' => 25, ]); ``` diff --git a/src/Application.php b/src/Application.php index 570b3c8..cee8a4a 100644 --- a/src/Application.php +++ b/src/Application.php @@ -10,6 +10,7 @@ use Invoker\ParameterResolver\Container\ParameterNameContainerResolver; use Invoker\ParameterResolver\Container\TypeHintContainerResolver; use Invoker\ParameterResolver\ResolverChain; +use ReflectionFunction; use Silly\Command\Command; use Silly\Command\ExpressionParser; use Symfony\Component\Console\Application as SymfonyApplication; @@ -90,6 +91,10 @@ public function command($expression, $callable, array $aliases = []) $command = $this->createCommand($expression, $commandFunction); $command->setAliases($aliases); + if (is_callable($callable)) { + $command->defaults($this->defaultsViaReflection($callable)); + } + $this->add($command); return $command; @@ -195,4 +200,21 @@ private function createCommand($expression, callable $callable) return $command; } + + private function defaultsViaReflection(callable $callable) + { + $function = new ReflectionFunction($callable); + + $defaults = []; + + foreach ($function->getParameters() as $parameter) { + if (! $parameter->isDefaultValueAvailable()) { + continue; + } + + $defaults[$parameter->name] = $parameter->getDefaultValue(); + } + + return $defaults; + } } diff --git a/src/Command/Command.php b/src/Command/Command.php index ca55cf7..60ceb75 100644 --- a/src/Command/Command.php +++ b/src/Command/Command.php @@ -39,19 +39,28 @@ public function descriptions($description, array $argumentAndOptionDescriptions /** * Define default values for the arguments of the command. * - * @param array $argumentDefaults Default argument values. + * @param array $defaults Default argument values. * * @return $this * * @api */ - public function defaults(array $argumentDefaults = []) + public function defaults(array $defaults = []) { $definition = $this->getDefinition(); - foreach ($argumentDefaults as $name => $default) { - $argument = $definition->getArgument($name); - $argument->setDefault($default); + foreach ($defaults as $name => $default) { + if ($definition->hasArgument($name)) { + $input = $definition->getArgument($name); + } elseif ($definition->hasOption($name)) { + $input = $definition->getOption($name); + } else { + throw new \InvalidArgumentException( + "Unable to set default for [{$name}]. It does not exist as an argument or option." + ); + } + + $input->setDefault($default); } return $this; diff --git a/tests/Command/CommandTest.php b/tests/Command/CommandTest.php index 6599aa0..ec8de65 100644 --- a/tests/Command/CommandTest.php +++ b/tests/Command/CommandTest.php @@ -23,7 +23,7 @@ public function setUp() $this->application->setAutoExit(false); $this->application->setCatchExceptions(false); - $this->command = $this->application->command('greet [name] [--yell]', function () {}); + $this->command = $this->application->command('greet [name] [--yell] [--times=]', function () {}); } /** @@ -34,6 +34,7 @@ public function allows_to_define_descriptions() $this->command->descriptions('Greet someone', [ 'name' => 'Who?', '--yell' => 'Yell?', + '--times' => '# of times to greet?', ]); $definition = $this->command->getDefinition(); @@ -41,6 +42,7 @@ public function allows_to_define_descriptions() $this->assertEquals('Greet someone', $this->command->getDescription()); $this->assertEquals('Who?', $definition->getArgument('name')->getDescription()); $this->assertEquals('Yell?', $definition->getOption('yell')->getDescription()); + $this->assertEquals('# of times to greet?', $definition->getOption('times')->getDescription()); } /** @@ -50,10 +52,51 @@ public function allows_to_define_default_values() { $this->command->defaults([ 'name' => 'John', + 'times' => '1', ]); $definition = $this->command->getDefinition(); $this->assertEquals('John', $definition->getArgument('name')->getDefault()); + $this->assertEquals('1', $definition->getOption('times')->getDefault()); + } + + /** + * @test + */ + public function allows_default_values_to_be_inferred_from_closure_parameters() + { + $command = $this->application->command('greet [name] [--yell] [--times=]', function ($times = 15) { + // + }); + + $definition = $command->getDefinition(); + + $this->assertEquals(15, $definition->getOption("times")->getDefault()); + } + + /** + * @test + */ + public function setting_defaults_falls_back_to_options_when_no_argument_exists() + { + $this->command->defaults([ + 'times' => '5', + ]); + + $definition = $this->command->getDefinition(); + + $this->assertEquals(5, $definition->getOption("times")->getDefault()); + } + + /** + * @test + * @expectedException \InvalidArgumentException + */ + public function setting_unknown_defaults_throws_an_exception() + { + $this->command->defaults([ + 'doesnotexist' => '0', + ]); } }