合并两串 CLI 选项
Merge two string of CLI options
如何合并两个包含常规 CLI 选项的字符串(即任何 getopt()
都可以正确解析。)?
第一个来自配置文件,第二个来自 Symfony 的 Console\Input\getArgument()
。我还可以从 $GLOBALS['argv']
得到第二个。我想把它们结合起来,这样我就可以用它们启动另一个程序了。
任一字符串都可以包含短选项、长选项,有值或无值。
例子
例如,config.yml
包含
phpunit_arguments: -d xdebug.mode=off --columns=115
...然后用户可以用php {filename} --columns=95 --debug
调用我的程序。我想合并这些字符串,所以我最终得到:
-d xdebug.mode=off --columns=95 --debug
第一个字符串的 columns
被第二个字符串覆盖。
尝试 1 失败:转换为数组
如果我可以像下面这样将这些字符串放入数组中,那么我就可以使用 array_merge_recursive()
:
array(
'-d xdebug.mode' => 'off',
'--columns' => 115
)
array(
'--columns' => 95,
'--debug'
)
...但要做到这一点,我需要一个理解 CLI 选项的解析器。
我查看了以下内容,但 none 似乎被设计为采用任意字符串和 return 已解析的数组。
- PHP的
getopt()
- Symfony 的
Console\Input
- PHP单位的
TextUI\CliArguments\Builder
- docopt.php
- Commando
- GetOptionKit
失败的尝试 2:连接
我试过只是连接两个字符串而不是合并它们,这在技术上是可行的,但它会产生用户体验问题。我的程序向用户显示参数,并且连接的字符串将包含重复项,这会让一些人感到困惑。该程序还在 运行 时接受输入,并重新生成选项;随着时间的推移,附加到先前的字符串会滚雪球,更糟的是 confusion/UX.
例如,设置groups
几次后,最终会变成
-d xdebug.mode=off --columns=95 --debug --groups=database --groups=file --groups=console
您可以创建自己的函数,它将智能 合并配置参数和 CLI 参数。使用单个正则表达式,我们可以提取 -
或 --
等前缀、命令名称和具有值的相等字符。
使用正则表达式和循环合并参数
请阅读内联评论
<?php
echo "PHP ".phpversion().PHP_EOL;
// $config = yaml_parse_file('config.yml');
// Load a JSON file instead of YAML, because repl.it
// does not have php_yaml extension enabled
$json = file_get_contents("config.json");
$config = json_decode($json, TRUE);
$phpunit_arguments = explode(' ', $config['phpunit_arguments']);
echo "phpunit_arguments:\n";
print_r($phpunit_arguments);
// Merge the params
$params = mergeConfigParameters($phpunit_arguments);
// Concatenate and print all params
echo "cli args:\n";
echo implode(' ', $params).PHP_EOL;
function mergeConfigParameters($config_params) {
$argv = $GLOBALS['argv'];
array_shift($argv); // Remove script file from CLI arguments
if (empty($argv)) {
// If we run the file without CLI arguments,
// apply some fake argv arguments for demontration
$argv = [
'-d',
'xdebug.mode=on',
'--columns=95',
'--debug',
'--groups=database',
'--groups=file',
'--groups=console'
];
}
echo "argv:\n";
print_r($argv);
// Merge all parameters, CLI arguments and from config
$all_params = array_merge($config_params, $argv);
echo "all_params:\n";
print_r($all_params);
// We'll save all the params here using assoc array
// to identify and handle/override duplicate commands
$params = [];
foreach ($all_params as $param) {
// This regex will match everything:
// -d
// xdebug.mode=off
// --columns=95
// and create 4 groups:
// 1: the pre char(s), - or --
// 2: the cmd, actual command
// 3: the eq char, =
// 4: the value
if (preg_match('/^(-[-]?)?([\w.]+)(=?)(.*)/', $param, $matches)) {
// Destructure matches
[, $pre, $cmd, $eq, $value] = $matches;
$param = [
'pre' => $pre,
'cmd' => $cmd,
'eq' => $eq,
'value' => $value
];
// If the command is set, merge it with the previous,
// else add it to $params array
if (isset($params[$cmd])) {
$params[$cmd] = array_merge($params[$cmd], $param);
} else {
$params[$cmd] = $param;
}
}
}
$merged = [];
// Loop throu all unique params and re-build the commands
foreach ($params as $param) {
[
'pre' => $pre,
'cmd' => $cmd,
'eq' => $eq,
'value' => $value
] = $param;
if (!empty($pre)) {
$cmd = $pre.$cmd;
}
if (!empty($eq)) {
$cmd .= $eq;
if (!empty($value)) {
$cmd .= $value;
}
}
$merged[] = $cmd;
}
echo "merged:\n";
print_r($merged);
// Finally we have all unique commands compiled again
return $merged;
}
结果
运行执行此命令:
php main.php -d xdebug.mode=on --columns=95 --debug --groups=database --groups=file --groups=console
用这个 config.yml
:
phpunit_arguments: -d xdebug.mode=off --columns=115 --number=1234
将输出:
PHP 7.2.24-0ubuntu0.18.04.7
phpunit_arguments:
Array
(
[0] => -d
[1] => xdebug.mode=off
[2] => --columns=115
[3] => --number=1234
)
argv:
Array
(
[0] => -d
[1] => xdebug.mode=on
[2] => --columns=95
[3] => --debug
[4] => --groups=database
[5] => --groups=file
[6] => --groups=console
)
all_params:
Array
(
[0] => -d
[1] => xdebug.mode=off
[2] => --columns=115
[3] => --number=1234
[4] => -d
[5] => xdebug.mode=on
[6] => --columns=95
[7] => --debug
[8] => --groups=database
[9] => --groups=file
[10] => --groups=console
)
merged:
Array
(
[0] => -d
[1] => xdebug.mode=on
[2] => --columns=95
[3] => --number=1234
[4] => --debug
[5] => --groups=console
)
cli args:
-d xdebug.mode=on --columns=95 --number=1234 --debug --groups=console
所以我们可以看到参数 -d xdebug.mode=off
和 --columns=115
已经合并并覆盖为 CLI 参数 -d xdebug.mode=on --columns=95
而且我们只有最后一个 --groups=console
设置。
运行这
您可以在线检查此解析工作repl.it。
如何合并两个包含常规 CLI 选项的字符串(即任何 getopt()
都可以正确解析。)?
第一个来自配置文件,第二个来自 Symfony 的 Console\Input\getArgument()
。我还可以从 $GLOBALS['argv']
得到第二个。我想把它们结合起来,这样我就可以用它们启动另一个程序了。
任一字符串都可以包含短选项、长选项,有值或无值。
例子
例如,config.yml
包含
phpunit_arguments: -d xdebug.mode=off --columns=115
...然后用户可以用php {filename} --columns=95 --debug
调用我的程序。我想合并这些字符串,所以我最终得到:
-d xdebug.mode=off --columns=95 --debug
第一个字符串的 columns
被第二个字符串覆盖。
尝试 1 失败:转换为数组
如果我可以像下面这样将这些字符串放入数组中,那么我就可以使用 array_merge_recursive()
:
array(
'-d xdebug.mode' => 'off',
'--columns' => 115
)
array(
'--columns' => 95,
'--debug'
)
...但要做到这一点,我需要一个理解 CLI 选项的解析器。
我查看了以下内容,但 none 似乎被设计为采用任意字符串和 return 已解析的数组。
- PHP的
getopt()
- Symfony 的
Console\Input
- PHP单位的
TextUI\CliArguments\Builder
- docopt.php
- Commando
- GetOptionKit
失败的尝试 2:连接
我试过只是连接两个字符串而不是合并它们,这在技术上是可行的,但它会产生用户体验问题。我的程序向用户显示参数,并且连接的字符串将包含重复项,这会让一些人感到困惑。该程序还在 运行 时接受输入,并重新生成选项;随着时间的推移,附加到先前的字符串会滚雪球,更糟的是 confusion/UX.
例如,设置groups
几次后,最终会变成
-d xdebug.mode=off --columns=95 --debug --groups=database --groups=file --groups=console
您可以创建自己的函数,它将智能 合并配置参数和 CLI 参数。使用单个正则表达式,我们可以提取 -
或 --
等前缀、命令名称和具有值的相等字符。
使用正则表达式和循环合并参数
请阅读内联评论
<?php
echo "PHP ".phpversion().PHP_EOL;
// $config = yaml_parse_file('config.yml');
// Load a JSON file instead of YAML, because repl.it
// does not have php_yaml extension enabled
$json = file_get_contents("config.json");
$config = json_decode($json, TRUE);
$phpunit_arguments = explode(' ', $config['phpunit_arguments']);
echo "phpunit_arguments:\n";
print_r($phpunit_arguments);
// Merge the params
$params = mergeConfigParameters($phpunit_arguments);
// Concatenate and print all params
echo "cli args:\n";
echo implode(' ', $params).PHP_EOL;
function mergeConfigParameters($config_params) {
$argv = $GLOBALS['argv'];
array_shift($argv); // Remove script file from CLI arguments
if (empty($argv)) {
// If we run the file without CLI arguments,
// apply some fake argv arguments for demontration
$argv = [
'-d',
'xdebug.mode=on',
'--columns=95',
'--debug',
'--groups=database',
'--groups=file',
'--groups=console'
];
}
echo "argv:\n";
print_r($argv);
// Merge all parameters, CLI arguments and from config
$all_params = array_merge($config_params, $argv);
echo "all_params:\n";
print_r($all_params);
// We'll save all the params here using assoc array
// to identify and handle/override duplicate commands
$params = [];
foreach ($all_params as $param) {
// This regex will match everything:
// -d
// xdebug.mode=off
// --columns=95
// and create 4 groups:
// 1: the pre char(s), - or --
// 2: the cmd, actual command
// 3: the eq char, =
// 4: the value
if (preg_match('/^(-[-]?)?([\w.]+)(=?)(.*)/', $param, $matches)) {
// Destructure matches
[, $pre, $cmd, $eq, $value] = $matches;
$param = [
'pre' => $pre,
'cmd' => $cmd,
'eq' => $eq,
'value' => $value
];
// If the command is set, merge it with the previous,
// else add it to $params array
if (isset($params[$cmd])) {
$params[$cmd] = array_merge($params[$cmd], $param);
} else {
$params[$cmd] = $param;
}
}
}
$merged = [];
// Loop throu all unique params and re-build the commands
foreach ($params as $param) {
[
'pre' => $pre,
'cmd' => $cmd,
'eq' => $eq,
'value' => $value
] = $param;
if (!empty($pre)) {
$cmd = $pre.$cmd;
}
if (!empty($eq)) {
$cmd .= $eq;
if (!empty($value)) {
$cmd .= $value;
}
}
$merged[] = $cmd;
}
echo "merged:\n";
print_r($merged);
// Finally we have all unique commands compiled again
return $merged;
}
结果
运行执行此命令:
php main.php -d xdebug.mode=on --columns=95 --debug --groups=database --groups=file --groups=console
用这个 config.yml
:
phpunit_arguments: -d xdebug.mode=off --columns=115 --number=1234
将输出:
PHP 7.2.24-0ubuntu0.18.04.7
phpunit_arguments:
Array
(
[0] => -d
[1] => xdebug.mode=off
[2] => --columns=115
[3] => --number=1234
)
argv:
Array
(
[0] => -d
[1] => xdebug.mode=on
[2] => --columns=95
[3] => --debug
[4] => --groups=database
[5] => --groups=file
[6] => --groups=console
)
all_params:
Array
(
[0] => -d
[1] => xdebug.mode=off
[2] => --columns=115
[3] => --number=1234
[4] => -d
[5] => xdebug.mode=on
[6] => --columns=95
[7] => --debug
[8] => --groups=database
[9] => --groups=file
[10] => --groups=console
)
merged:
Array
(
[0] => -d
[1] => xdebug.mode=on
[2] => --columns=95
[3] => --number=1234
[4] => --debug
[5] => --groups=console
)
cli args:
-d xdebug.mode=on --columns=95 --number=1234 --debug --groups=console
所以我们可以看到参数 -d xdebug.mode=off
和 --columns=115
已经合并并覆盖为 CLI 参数 -d xdebug.mode=on --columns=95
而且我们只有最后一个 --groups=console
设置。
运行这
您可以在线检查此解析工作repl.it。