【问题标题】:Merge two string of CLI options合并两个 CLI 选项字符串
【发布时间】:2021-04-25 04:24:32
【问题描述】:

如何合并两个包含常规 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 被第二个字符串中的columns 覆盖。

尝试 1 失败:转换为数组

如果我可以将这些字符串放入如下数组中,那么我可以使用array_merge_recursive()

array(
    '-d xdebug.mode' => 'off',
    '--columns'      => 115
)

array(
     '--columns' => 95,
     '--debug'
)

...但要做到这一点,我需要一个能够理解 CLI 选项的解析器。

我查看了以下内容,但似乎没有一个被设计为采用任意字符串并返回已解析的数组。

失败的尝试 2:连接

我尝试只是连接两个字符串而不是合并它们,这在技术上是可行的,但它会产生 UX 问题。我的程序向用户显示 args,并且 concat'd 字符串将包含重复项,这会让某些人感到困惑。程序在运行时也接受输入,并重新生成选项;随着时间的推移,附加到前面的字符串会滚雪球,更糟糕的是混淆/UX。

例如,在设置groups 几次后,它最终会变成

-d xdebug.mode=off --columns=95 --debug --groups=database --groups=file --groups=console

【问题讨论】:

    标签: php symfony command-line-arguments docopt


    【解决方案1】:

    您可以创建自己的函数来智能合并配置参数和 CLI 参数。使用单个正则表达式,我们可以提取 --- 等前置符号、命令名称和具有值的相等字符。

    使用正则表达式和循环合并参数

    请阅读内联 cmets

    <?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 上在线检查此解析工作。

    【讨论】:

    • 效果很好,谢谢!我发现了一些边缘案例,一旦修复它们,我将分享我的最终版本。
    猜你喜欢
    • 1970-01-01
    • 2017-09-11
    • 2019-06-29
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多