Exemplo n.º 1
0
 /**
  * Parse an array of arguments.
  *
  * If the first item in the array is in the form of a command (no preceeding - or --),
  * 'command' is filled with its value.
  *
  * @param array $argv An array of arguments passed in a form compatible with the global `$argv` variable.
  * @return Args Returns the raw parsed arguments.
  * @throws \Exception Throws an exception when {@see $argv} isn't an array.
  */
 protected function parseRaw($argv = null)
 {
     if ($argv === null) {
         $argv = $GLOBALS['argv'];
     }
     if (!is_array($argv)) {
         throw new \Exception(__METHOD__ . " expects an array", 400);
     }
     $path = array_shift($argv);
     $hasCommand = $this->hasCommand();
     $parsed = new Args();
     $parsed->setMeta('path', $path);
     $parsed->setMeta('filename', basename($path));
     if ($argc = count($argv)) {
         // Get possible command.
         if (substr($argv[0], 0, 1) != '-') {
             $arg0 = array_shift($argv);
             if ($hasCommand) {
                 $parsed->setCommand($arg0);
             } else {
                 $parsed->addArg($arg0);
             }
         }
         // Get the data types for all of the commands.
         $schema = $this->getSchema($parsed->getCommand());
         $types = [];
         foreach ($schema as $sname => $srow) {
             if ($sname === Cli::META) {
                 continue;
             }
             $type = Cli::val('type', $srow, 'string');
             $types[$sname] = $type;
             if (isset($srow['short'])) {
                 $types[$srow['short']] = $type;
             }
         }
         // Parse opts.
         for ($i = 0; $i < count($argv); $i++) {
             $str = $argv[$i];
             if ($str === '--') {
                 // --
                 $i++;
                 break;
             } elseif (strlen($str) > 2 && substr($str, 0, 2) == '--') {
                 // --foo
                 $str = substr($str, 2);
                 $parts = explode('=', $str);
                 $key = $parts[0];
                 // Does not have an =, so choose the next arg as its value
                 if (count($parts) == 1 && isset($argv[$i + 1]) && preg_match('/^--?.+/', $argv[$i + 1]) == 0) {
                     $v = $argv[$i + 1];
                     $i++;
                 } elseif (count($parts) == 2) {
                     // Has a =, so pick the second piece
                     $v = $parts[1];
                 } else {
                     $v = true;
                 }
                 $parsed->setOpt($key, $v);
             } elseif (strlen($str) == 2 && $str[0] == '-') {
                 // -a
                 $key = $str[1];
                 $type = Cli::val($key, $types, 'boolean');
                 $v = null;
                 if (isset($argv[$i + 1])) {
                     // Try and be smart about the next arg.
                     $nextArg = $argv[$i + 1];
                     if ($type === 'boolean') {
                         if (in_array($nextArg, ['0', '1', 'true', 'false', 'on', 'off', 'yes', 'no'])) {
                             // The next arg looks like a boolean to me.
                             $v = $nextArg;
                             $i++;
                         } else {
                             $v = true;
                         }
                     } elseif (!preg_match('/^--?.+/', $argv[$i + 1])) {
                         // The next arg is not an opt.
                         $v = $nextArg;
                         $i++;
                     } else {
                         // The next arg is another opt.
                         $v = null;
                     }
                 }
                 if ($v === null) {
                     $v = Cli::val($type, ['boolean' => true, 'integer' => 1, 'string' => '']);
                 }
                 $parsed->setOpt($key, $v);
             } elseif (strlen($str) > 1 && $str[0] == '-') {
                 // -abcdef
                 for ($j = 1; $j < strlen($str); $j++) {
                     $opt = $str[$j];
                     $remaining = substr($str, $j + 1);
                     $type = Cli::val($opt, $types, 'boolean');
                     if ($type === 'boolean') {
                         if (preg_match('`^([01])`', $remaining, $matches)) {
                             // Treat the 0 or 1 as a true or false.
                             $parsed->setOpt($opt, $matches[1]);
                             $j += strlen($matches[1]);
                         } else {
                             // Treat the option as a flag.
                             $parsed->setOpt($opt, true);
                         }
                     } elseif ($type === 'string') {
                         // Treat the option as a set with no = sign.
                         $parsed->setOpt($opt, $remaining);
                         break;
                     } elseif ($type === 'integer') {
                         if (preg_match('`^(\\d+)`', $remaining, $matches)) {
                             // Treat the option as a set with no = sign.
                             $parsed->setOpt($opt, $matches[1]);
                             $j += strlen($matches[1]);
                         } else {
                             // Treat the option as either multiple flags.
                             $optval = $parsed->getOpt($opt, 0);
                             $parsed->setOpt($opt, $optval + 1);
                         }
                     } else {
                         // This should not happen unless we've put a bug in our code.
                         throw new \Exception("Invalid type {$type} for {$opt}.", 500);
                     }
                 }
             } else {
                 // End of opts
                 break;
             }
         }
         // Grab the remaining args.
         for (; $i < count($argv); $i++) {
             $parsed->addArg($argv[$i]);
         }
     }
     return $parsed;
 }