Skip to content

Commit 0995024

Browse files
committed
feat: add support to parse the -- separator for commands
1 parent 3483334 commit 0995024

File tree

3 files changed

+87
-21
lines changed

3 files changed

+87
-21
lines changed

system/CLI/CLI.php

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -848,35 +848,41 @@ public static function wrap(?string $string = null, int $max = 0, int $padLeft =
848848
*/
849849
protected static function parseCommandLine()
850850
{
851-
$args = $_SERVER['argv'] ?? [];
852-
array_shift($args); // scrap invoking program
853-
$optionValue = false;
854-
855-
foreach ($args as $i => $arg) {
856-
// If there's no "-" at the beginning, then
857-
// this is probably an argument or an option value
858-
if (mb_strpos($arg, '-') !== 0) {
859-
if ($optionValue) {
860-
// We have already included this in the previous
861-
// iteration, so reset this flag
862-
$optionValue = false;
863-
} else {
864-
// Yup, it's a segment
865-
static::$segments[] = $arg;
851+
/** @var list<string> $tokens */
852+
$tokens = $_SERVER['argv'] ?? [];
853+
array_shift($tokens); // scrap application name
854+
855+
$parseOptions = true;
856+
$optionValue = false;
857+
858+
foreach ($tokens as $index => $token) {
859+
if ($token === '--' && $parseOptions) {
860+
$parseOptions = false;
861+
862+
continue;
863+
}
864+
865+
if (str_starts_with($token, '-') && $parseOptions) {
866+
$value = null;
867+
868+
if (isset($tokens[$index + 1]) && ! str_starts_with($tokens[$index + 1], '-')) {
869+
$value = $tokens[$index + 1];
870+
871+
$optionValue = true;
866872
}
867873

874+
static::$options[ltrim($token, '-')] = $value;
875+
868876
continue;
869877
}
870878

871-
$arg = ltrim($arg, '-');
872-
$value = null;
879+
if (! str_starts_with($token, '-') && $optionValue) {
880+
$optionValue = false;
873881

874-
if (isset($args[$i + 1]) && mb_strpos($args[$i + 1], '-') !== 0) {
875-
$value = $args[$i + 1];
876-
$optionValue = true;
882+
continue;
877883
}
878884

879-
static::$options[$arg] = $value;
885+
static::$segments[] = $token;
880886
}
881887
}
882888

tests/system/CLI/CLITest.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,63 @@ public function testParseCommandMultipleOptions(): void
563563
$this->assertSame(['b', 'c', 'd'], CLI::getSegments());
564564
}
565565

566+
/**
567+
* @param list<string> $args
568+
* @param array<string, string|null> $options
569+
* @param list<string> $segments
570+
*/
571+
#[DataProvider('provideParseCommandSupportsDoubleHyphen')]
572+
public function testParseCommandSupportsDoubleHyphen(array $args, array $options, array $segments): void
573+
{
574+
service('superglobals')->setServer('argv', ['spark', ...$args]);
575+
CLI::init();
576+
577+
$this->assertSame($options, CLI::getOptions());
578+
$this->assertSame($segments, CLI::getSegments());
579+
}
580+
581+
/**
582+
* @return iterable<string, array{list<string>, array<string, string|null>, list<string>}>
583+
*/
584+
public static function provideParseCommandSupportsDoubleHyphen(): iterable
585+
{
586+
yield 'options before double hyphen' => [
587+
['b', 'c', '--key', 'value', '--', 'd'],
588+
['key' => 'value'],
589+
['b', 'c', 'd'],
590+
];
591+
592+
yield 'options after double hyphen' => [
593+
['b', 'c', '--', '--key', 'value', 'd'],
594+
[],
595+
['b', 'c', '--key', 'value', 'd'],
596+
];
597+
598+
yield 'options before and after double hyphen' => [
599+
['b', 'c', '--key', 'value', '--', '--p2', 'value 2', 'd'],
600+
['key' => 'value'],
601+
['b', 'c', '--p2', 'value 2', 'd'],
602+
];
603+
604+
yield 'double hyphen only' => [
605+
['b', 'c', '--', 'd'],
606+
[],
607+
['b', 'c', 'd'],
608+
];
609+
610+
yield 'options before segments with double hyphen' => [
611+
['--key', 'value', '--foo', '--', 'b', 'c', 'd'],
612+
['key' => 'value', 'foo' => null],
613+
['b', 'c', 'd'],
614+
];
615+
616+
yield 'options before segments with double hyphen and no options' => [
617+
['--', 'b', 'c', 'd'],
618+
[],
619+
['b', 'c', 'd'],
620+
];
621+
}
622+
566623
public function testWindow(): void
567624
{
568625
$height = new ReflectionProperty(CLI::class, 'height');

user_guide_src/source/changelogs/v4.8.0.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ Enhancements
143143
Commands
144144
========
145145

146+
- CLI now supports the ``--`` separator to tell the parser to treat subsequent arguments as literal values, allowing you to use reserved characters without needing to escape them.
147+
For example, ``spark my:command -- --option value`` will pass ``--option`` and ``value`` as literal arguments to the command.
148+
146149
Testing
147150
=======
148151

0 commit comments

Comments
 (0)