private function parseInternal()

in src/parser/argument/PhutilArgumentParser.php [124:283]


  private function parseInternal(
    array $specs,
    $correct_spelling,
    $initial_only) {

    $specs = PhutilArgumentSpecification::newSpecsFromList($specs);
    $this->mergeSpecs($specs);

    $specs_by_name  = mpull($specs, null, 'getName');
    $specs_by_short = mpull($specs, null, 'getShortAlias');
    unset($specs_by_short[null]);

    $argv = $this->argv;
    $len = count($argv);
    $is_initial = true;
    for ($ii = 0; $ii < $len; $ii++) {
      $arg = $argv[$ii];
      $map = null;
      $options = null;
      if (!is_string($arg)) {
        // Non-string argument; pass it through as-is.
      } else if ($arg == '--') {
        // This indicates "end of flags".
        break;
      } else if ($arg == '-') {
        // This is a normal argument (e.g., stdin).
        continue;
      } else if (!strncmp('--', $arg, 2)) {
        $pre = '--';
        $arg = substr($arg, 2);
        $map = $specs_by_name;
        $options = array_keys($specs_by_name);
      } else if (!strncmp('-', $arg, 1) && strlen($arg) > 1) {
        $pre = '-';
        $arg = substr($arg, 1);
        $map = $specs_by_short;
      } else {
        $is_initial = false;
      }

      if ($map) {
        $val = null;
        $parts = explode('=', $arg, 2);
        if (count($parts) == 2) {
          list($arg, $val) = $parts;
        }

        // Try to correct flag spelling for full flags, to allow users to make
        // minor mistakes.
        if ($correct_spelling && $options && !isset($map[$arg])) {
          $corrections = PhutilArgumentSpellingCorrector::newFlagCorrector()
            ->correctSpelling($arg, $options);

          if (count($corrections) == 1) {
            $corrected = head($corrections);

            $this->logMessage(
              tsprintf(
                "%s\n",
                pht(
                  '(Assuming "%s" is the British spelling of "%s".)',
                  $pre.$arg,
                  $pre.$corrected)));

            $arg = $corrected;
          }
        }

        if (isset($map[$arg])) {
          if ($initial_only && !$is_initial) {
            throw new PhutilArgumentUsageException(
              pht(
                'Argument "%s" appears after the first non-flag argument. '.
                'This special argument must appear before other arguments.',
                "{$pre}{$arg}"));
          }

          $spec = $map[$arg];
          unset($argv[$ii]);

          $param_name = $spec->getParamName();
          if ($val !== null) {
            if ($param_name === null) {
              throw new PhutilArgumentUsageException(
                pht(
                  "Argument '%s' does not take a parameter.",
                  "{$pre}{$arg}"));
            }
          } else {
            if ($param_name !== null) {
              if ($ii + 1 < $len) {
                $val = $argv[$ii + 1];
                unset($argv[$ii + 1]);
                $ii++;
              } else {
                throw new PhutilArgumentUsageException(
                  pht(
                    "Argument '%s' requires a parameter.",
                    "{$pre}{$arg}"));
              }
            } else {
              $val = true;
            }
          }

          if (!$spec->getRepeatable()) {
            if (array_key_exists($spec->getName(), $this->results)) {
              throw new PhutilArgumentUsageException(
                pht(
                  "Argument '%s' was provided twice.",
                  "{$pre}{$arg}"));
            }
          }

          $conflicts = $spec->getConflicts();
          foreach ($conflicts as $conflict => $reason) {
            if (array_key_exists($conflict, $this->results)) {

              if (!is_string($reason) || !strlen($reason)) {
                $reason = '.';
              } else {
                $reason = ': '.$reason.'.';
              }

              throw new PhutilArgumentUsageException(
                pht(
                  "Argument '%s' conflicts with argument '%s'%s",
                  "{$pre}{$arg}",
                  "--{$conflict}",
                  $reason));
            }
          }

          if ($spec->getRepeatable()) {
            if ($spec->getParamName() === null) {
              if (empty($this->results[$spec->getName()])) {
                $this->results[$spec->getName()] = 0;
              }
              $this->results[$spec->getName()]++;
            } else {
              $this->results[$spec->getName()][] = $val;
            }
          } else {
            $this->results[$spec->getName()] = $val;
          }
        }
      }
    }

    foreach ($specs as $spec) {
      if ($spec->getWildcard()) {
        $this->results[$spec->getName()] = $this->filterWildcardArgv($argv);
        $argv = array();
        break;
      }
    }

    $this->argv = array_values($argv);
    return $this;
  }