diff --git a/src/Argument.php b/src/Argument.php index 910cb1a..48a22f3 100644 --- a/src/Argument.php +++ b/src/Argument.php @@ -10,27 +10,8 @@ * * @link https://github.com/adhocore/cli */ -class Argument +class Argument extends Parameter { - use InflectsString; - - protected $name; - - protected $rawArg; - - protected $default; - - protected $required = false; - - protected $variadic = false; - - public function __construct(string $arg) - { - $this->rawArg = $arg; - - $this->parse($arg); - } - protected function parse(string $arg) { $this->required = $arg[0] === '<'; @@ -44,26 +25,6 @@ protected function parse(string $arg) } } - public function name(): string - { - return $this->name; - } - - public function attributeName(): string - { - return $this->toCamelCase($this->name); - } - - public function required(): bool - { - return $this->required; - } - - public function variadic(): bool - { - return $this->variadic; - } - public function default() { if (!$this->variadic) { diff --git a/src/ArgvParser.php b/src/ArgvParser.php index 8a87b5c..f6c2837 100644 --- a/src/ArgvParser.php +++ b/src/ArgvParser.php @@ -77,8 +77,12 @@ public function arguments(string $definitions): self throw new \InvalidArgumentException('Only last argument can be variadic'); } - $this->_arguments[$argument->name()] = $argument; - $this->_values[$argument->attributeName()] = $argument->default(); + $name = $argument->attributeName(); + + $this->ifAlreadyRegistered($name, $argument); + + $this->_arguments[$name] = $argument; + $this->_values[$name] = $argument->default(); } return $this; @@ -97,19 +101,26 @@ public function arguments(string $definitions): self public function option(string $cmd, string $desc = '', callable $filter = null, $default = null): self { $option = new Option($cmd, $desc, $default, $filter); + $name = $option->attributeName(); - if (isset($this->_options[$option->long()])) { - throw new \InvalidArgumentException( - \sprintf('The option "%s" is already registered', $option->long()) - ); - } + $this->ifAlreadyRegistered($name, $option); - $this->_values[$option->attributeName()] = $option->default(); - $this->_options[$option->long()] = $option; + $this->_options[$name] = $option; + $this->_values[$name] = $option->default(); return $this; } + protected function ifAlreadyRegistered(string $name, Parameter $param) + { + if (\array_key_exists($name, $this->_values)) { + throw new \InvalidArgumentException(\sprintf( + 'The parameter "%s" is already registered', + $param instanceof Option ? $param->long() : $param->name() + )); + } + } + /** * Sets event handler for last option. * @@ -176,7 +187,12 @@ public function values(bool $withDefaults = true): array */ public function __get(string $key) { - return isset($this->_values[$key]) ? $this->_values[$key] : null; + return $this->_values[$key] ?? null; + } + + public function args(): array + { + return \array_diff_key($this->_values, $this->_options); } protected function showHelp() diff --git a/src/Option.php b/src/Option.php index 590bf3f..e6a9877 100644 --- a/src/Option.php +++ b/src/Option.php @@ -10,58 +10,37 @@ * * @link https://github.com/adhocore/cli */ -class Option +class Option extends Parameter { - use InflectsString; - protected $short; protected $long; - protected $desc; - - protected $rawCmd; - - protected $default; - - protected $required = true; - - protected $optional = false; - - protected $variadic = false; - protected $filter; - public function __construct(string $cmd, string $desc = null, $default = null, callable $filter = null) + public function __construct(string $raw, string $desc = null, $default = null, callable $filter = null) { - $this->rawCmd = $cmd; - $this->desc = $desc; - $this->default = $default; - $this->filter = $filter; - $this->required = \strpos($cmd, '<') !== false; - $this->optional = \strpos($cmd, '[') !== false; - - if ($this->variadic = \strpos($cmd, '...') !== false) { - $this->default = (array) $this->default; - } + $this->filter = $filter; - $this->parse($cmd); + parent::__construct($raw, $desc, $default); } - protected function parse(string $cmd) + protected function parse(string $raw) { - if (\strpos($cmd, '-with-') !== false) { + if (\strpos($raw, '-with-') !== false) { $this->default = false; - } elseif (\strpos($cmd, '-no-') !== false) { + } elseif (\strpos($raw, '-no-') !== false) { $this->default = true; } - $parts = \preg_split('/[\s,\|]+/', $cmd); + $parts = \preg_split('/[\s,\|]+/', $raw); $this->short = $this->long = $parts[0]; if (isset($parts[1])) { $this->long = $parts[1]; } + + $this->name = \str_replace(['--', 'no-', 'with-'], '', $this->long); } public function long(): string @@ -74,41 +53,11 @@ public function short(): string return $this->short; } - public function name(): string - { - return \str_replace(['--', 'no-', 'with-'], '', $this->long); - } - - public function attributeName(): string - { - return $this->toCamelCase($this->name()); - } - public function is($arg): bool { return $this->short === $arg || $this->long === $arg; } - public function required(): bool - { - return $this->required; - } - - public function optional(): bool - { - return $this->optional; - } - - public function variadic(): bool - { - return $this->variadic; - } - - public function default() - { - return $this->default; - } - public function bool(): bool { return \preg_match('/\-no|\-with/', $this->long) > 0; diff --git a/src/Parameter.php b/src/Parameter.php new file mode 100644 index 0000000..e57406f --- /dev/null +++ b/src/Parameter.php @@ -0,0 +1,79 @@ + + * @license MIT + * + * @link https://github.com/adhocore/cli + */ +abstract class Parameter +{ + use InflectsString; + + protected $name; + + protected $raw; + + protected $desc; + + protected $default; + + protected $required = false; + + protected $optional = false; + + protected $variadic = false; + + public function __construct(string $raw, string $desc = null, $default = null) + { + $this->raw = $raw; + $this->desc = $desc; + $this->default = $default; + $this->required = \strpos($raw, '<') !== false; + $this->optional = \strpos($raw, '[') !== false; + $this->variadic = \strpos($raw, '...') !== false; + + $this->parse($raw); + } + + abstract protected function parse(string $raw); + + public function raw(): string + { + return $this->raw; + } + + public function name(): string + { + return $this->name; + } + + public function attributeName(): string + { + return $this->toCamelCase($this->name); + } + + public function required(): bool + { + return $this->required; + } + + public function optional(): bool + { + return $this->optional; + } + + public function variadic(): bool + { + return $this->variadic; + } + + public function default() + { + return $this->default; + } +} diff --git a/src/Parser.php b/src/Parser.php index 7d2e459..bda48da 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -43,10 +43,12 @@ public function parse(array $argv): self $argv = $this->normalize($argv); $count = \count($argv); + $literal = false; for ($i = 0; $i < $count; $i++) { - list($arg, $nextArg) = [$argv[$i], isset($argv[$i + 1]) ? $argv[$i + 1] : null]; + list($arg, $nextArg) = [$argv[$i], $argv[$i + 1] ?? null]; - if ($arg[0] !== '-' || !empty($literal) || ($literal = $arg === '--')) { + $literal = $literal ?: $arg === '--'; + if ($arg[0] !== '-' || $literal) { $this->parseArgs($arg); } else { $i += (int) $this->parseOptions($arg, $nextArg); @@ -86,7 +88,7 @@ protected function splitShort(string $arg): array protected function parseArgs(string $arg) { - if ($arg == '--') { + if ($arg === '--') { return; } @@ -129,7 +131,7 @@ protected function parseOptions(string $arg, string $nextArg = null) return $isValue; } - $this->emit($option->long()); + $this->emit($option->attributeName()); $this->setValue($option, $value); return $isValue; @@ -137,10 +139,6 @@ protected function parseOptions(string $arg, string $nextArg = null) protected function optionFor(string $arg) { - if (isset($this->_options[$arg])) { - return $this->_options[$arg]; - } - foreach ($this->_options as $option) { if ($option->is($arg)) { return $option; diff --git a/tests/ArgvParserTest.php b/tests/ArgvParserTest.php index 5a16027..2346cad 100644 --- a/tests/ArgvParserTest.php +++ b/tests/ArgvParserTest.php @@ -87,7 +87,7 @@ public function test_arguments_with_options() public function test_options_repeat() { $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The option "--apple" is already registered'); + $this->expectExceptionMessage('The parameter "--apple" is already registered'); $p = $this->newParser()->option('-a --apple', 'Apple')->option('-a --apple', 'Apple'); } @@ -203,6 +203,14 @@ public function test_no_value() $this->assertNull($p->xyz); } + public function test_args() + { + $p = $this->newParser()->arguments(' [b]')->option('-x --xyz') + ->parse(['php', 'A', '-x', 'X', 'B', 'C', 'D']); + + $this->assertSame(['a' => 'A', 'b' => 'B', 'C', 'D'], $p->args()); + } + protected function newParser(string $version = '0.0.1', string $desc = null, bool $allowUnknown = false) { $p = new ArgvParser('ArgvParserTest', $desc, $allowUnknown); diff --git a/tests/OptionTest.php b/tests/OptionTest.php index e2756cd..5d5896e 100644 --- a/tests/OptionTest.php +++ b/tests/OptionTest.php @@ -20,6 +20,8 @@ public function test_new($cmd, $expect) $more += ['bool' => $o->bool()]; } + $this->assertEquals($cmd, $o->raw()); + $this->assertEquals($expect, [ 'long' => $o->long(), 'short' => $o->short(),