1cbeaa4a0SAndreas Gohr<?php 2cbeaa4a0SAndreas Gohr 3cbeaa4a0SAndreas Gohrnamespace splitbrain\phpcli; 4cbeaa4a0SAndreas Gohr 5cbeaa4a0SAndreas Gohr/** 6cbeaa4a0SAndreas Gohr * Class Options 7cbeaa4a0SAndreas Gohr * 8cbeaa4a0SAndreas Gohr * Parses command line options passed to the CLI script. Allows CLI scripts to easily register all accepted options and 9cbeaa4a0SAndreas Gohr * commands and even generates a help text from this setup. 10cbeaa4a0SAndreas Gohr * 11cbeaa4a0SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 12cbeaa4a0SAndreas Gohr * @license MIT 13cbeaa4a0SAndreas Gohr */ 14cbeaa4a0SAndreas Gohrclass Options 15cbeaa4a0SAndreas Gohr{ 16cbeaa4a0SAndreas Gohr /** @var array keeps the list of options to parse */ 17cbeaa4a0SAndreas Gohr protected $setup; 18cbeaa4a0SAndreas Gohr 19cbeaa4a0SAndreas Gohr /** @var array store parsed options */ 20cbeaa4a0SAndreas Gohr protected $options = array(); 21cbeaa4a0SAndreas Gohr 22cbeaa4a0SAndreas Gohr /** @var string current parsed command if any */ 23cbeaa4a0SAndreas Gohr protected $command = ''; 24cbeaa4a0SAndreas Gohr 25cbeaa4a0SAndreas Gohr /** @var array passed non-option arguments */ 26cbeaa4a0SAndreas Gohr protected $args = array(); 27cbeaa4a0SAndreas Gohr 28cbeaa4a0SAndreas Gohr /** @var string the executed script */ 29cbeaa4a0SAndreas Gohr protected $bin; 30cbeaa4a0SAndreas Gohr 31cbeaa4a0SAndreas Gohr /** @var Colors for colored help output */ 32cbeaa4a0SAndreas Gohr protected $colors; 33cbeaa4a0SAndreas Gohr 34*6cb05674SAndreas Gohr /** @var string newline used for spacing help texts */ 35*6cb05674SAndreas Gohr protected $newline = "\n"; 36*6cb05674SAndreas Gohr 37cbeaa4a0SAndreas Gohr /** 38cbeaa4a0SAndreas Gohr * Constructor 39cbeaa4a0SAndreas Gohr * 40cbeaa4a0SAndreas Gohr * @param Colors $colors optional configured color object 41cbeaa4a0SAndreas Gohr * @throws Exception when arguments can't be read 42cbeaa4a0SAndreas Gohr */ 43cbeaa4a0SAndreas Gohr public function __construct(Colors $colors = null) 44cbeaa4a0SAndreas Gohr { 45cbeaa4a0SAndreas Gohr if (!is_null($colors)) { 46cbeaa4a0SAndreas Gohr $this->colors = $colors; 47cbeaa4a0SAndreas Gohr } else { 48cbeaa4a0SAndreas Gohr $this->colors = new Colors(); 49cbeaa4a0SAndreas Gohr } 50cbeaa4a0SAndreas Gohr 51cbeaa4a0SAndreas Gohr $this->setup = array( 52cbeaa4a0SAndreas Gohr '' => array( 53cbeaa4a0SAndreas Gohr 'opts' => array(), 54cbeaa4a0SAndreas Gohr 'args' => array(), 55*6cb05674SAndreas Gohr 'help' => '', 56*6cb05674SAndreas Gohr 'commandhelp' => 'This tool accepts a command as first parameter as outlined below:' 57cbeaa4a0SAndreas Gohr ) 58cbeaa4a0SAndreas Gohr ); // default command 59cbeaa4a0SAndreas Gohr 60cbeaa4a0SAndreas Gohr $this->args = $this->readPHPArgv(); 61cbeaa4a0SAndreas Gohr $this->bin = basename(array_shift($this->args)); 62cbeaa4a0SAndreas Gohr 63cbeaa4a0SAndreas Gohr $this->options = array(); 64cbeaa4a0SAndreas Gohr } 65ab0a8902SAndreas Gohr 66ab0a8902SAndreas Gohr /** 67ab0a8902SAndreas Gohr * Gets the bin value 68ab0a8902SAndreas Gohr */ 69ab0a8902SAndreas Gohr public function getBin() 70ab0a8902SAndreas Gohr { 71ab0a8902SAndreas Gohr return $this->bin; 72ab0a8902SAndreas Gohr } 73cbeaa4a0SAndreas Gohr 74cbeaa4a0SAndreas Gohr /** 75cbeaa4a0SAndreas Gohr * Sets the help text for the tool itself 76cbeaa4a0SAndreas Gohr * 77cbeaa4a0SAndreas Gohr * @param string $help 78cbeaa4a0SAndreas Gohr */ 79cbeaa4a0SAndreas Gohr public function setHelp($help) 80cbeaa4a0SAndreas Gohr { 81cbeaa4a0SAndreas Gohr $this->setup['']['help'] = $help; 82cbeaa4a0SAndreas Gohr } 83cbeaa4a0SAndreas Gohr 84cbeaa4a0SAndreas Gohr /** 85*6cb05674SAndreas Gohr * Sets the help text for the tools commands itself 86*6cb05674SAndreas Gohr * 87*6cb05674SAndreas Gohr * @param string $help 88*6cb05674SAndreas Gohr */ 89*6cb05674SAndreas Gohr public function setCommandHelp($help) 90*6cb05674SAndreas Gohr { 91*6cb05674SAndreas Gohr $this->setup['']['commandhelp'] = $help; 92*6cb05674SAndreas Gohr } 93*6cb05674SAndreas Gohr 94*6cb05674SAndreas Gohr /** 95*6cb05674SAndreas Gohr * Use a more compact help screen with less new lines 96*6cb05674SAndreas Gohr * 97*6cb05674SAndreas Gohr * @param bool $set 98*6cb05674SAndreas Gohr */ 99*6cb05674SAndreas Gohr public function useCompactHelp($set = true) 100*6cb05674SAndreas Gohr { 101*6cb05674SAndreas Gohr $this->newline = $set ? '' : "\n"; 102*6cb05674SAndreas Gohr } 103*6cb05674SAndreas Gohr 104*6cb05674SAndreas Gohr /** 105cbeaa4a0SAndreas Gohr * Register the names of arguments for help generation and number checking 106cbeaa4a0SAndreas Gohr * 107cbeaa4a0SAndreas Gohr * This has to be called in the order arguments are expected 108cbeaa4a0SAndreas Gohr * 109cbeaa4a0SAndreas Gohr * @param string $arg argument name (just for help) 110cbeaa4a0SAndreas Gohr * @param string $help help text 111cbeaa4a0SAndreas Gohr * @param bool $required is this a required argument 112cbeaa4a0SAndreas Gohr * @param string $command if theses apply to a sub command only 113cbeaa4a0SAndreas Gohr * @throws Exception 114cbeaa4a0SAndreas Gohr */ 115cbeaa4a0SAndreas Gohr public function registerArgument($arg, $help, $required = true, $command = '') 116cbeaa4a0SAndreas Gohr { 117cbeaa4a0SAndreas Gohr if (!isset($this->setup[$command])) { 118cbeaa4a0SAndreas Gohr throw new Exception("Command $command not registered"); 119cbeaa4a0SAndreas Gohr } 120cbeaa4a0SAndreas Gohr 121cbeaa4a0SAndreas Gohr $this->setup[$command]['args'][] = array( 122cbeaa4a0SAndreas Gohr 'name' => $arg, 123cbeaa4a0SAndreas Gohr 'help' => $help, 124cbeaa4a0SAndreas Gohr 'required' => $required 125cbeaa4a0SAndreas Gohr ); 126cbeaa4a0SAndreas Gohr } 127cbeaa4a0SAndreas Gohr 128cbeaa4a0SAndreas Gohr /** 129cbeaa4a0SAndreas Gohr * This registers a sub command 130cbeaa4a0SAndreas Gohr * 131cbeaa4a0SAndreas Gohr * Sub commands have their own options and use their own function (not main()). 132cbeaa4a0SAndreas Gohr * 133cbeaa4a0SAndreas Gohr * @param string $command 134cbeaa4a0SAndreas Gohr * @param string $help 135cbeaa4a0SAndreas Gohr * @throws Exception 136cbeaa4a0SAndreas Gohr */ 137cbeaa4a0SAndreas Gohr public function registerCommand($command, $help) 138cbeaa4a0SAndreas Gohr { 139cbeaa4a0SAndreas Gohr if (isset($this->setup[$command])) { 140cbeaa4a0SAndreas Gohr throw new Exception("Command $command already registered"); 141cbeaa4a0SAndreas Gohr } 142cbeaa4a0SAndreas Gohr 143cbeaa4a0SAndreas Gohr $this->setup[$command] = array( 144cbeaa4a0SAndreas Gohr 'opts' => array(), 145cbeaa4a0SAndreas Gohr 'args' => array(), 146cbeaa4a0SAndreas Gohr 'help' => $help 147cbeaa4a0SAndreas Gohr ); 148cbeaa4a0SAndreas Gohr 149cbeaa4a0SAndreas Gohr } 150cbeaa4a0SAndreas Gohr 151cbeaa4a0SAndreas Gohr /** 152cbeaa4a0SAndreas Gohr * Register an option for option parsing and help generation 153cbeaa4a0SAndreas Gohr * 154cbeaa4a0SAndreas Gohr * @param string $long multi character option (specified with --) 155cbeaa4a0SAndreas Gohr * @param string $help help text for this option 156cbeaa4a0SAndreas Gohr * @param string|null $short one character option (specified with -) 157cbeaa4a0SAndreas Gohr * @param bool|string $needsarg does this option require an argument? give it a name here 158cbeaa4a0SAndreas Gohr * @param string $command what command does this option apply to 159cbeaa4a0SAndreas Gohr * @throws Exception 160cbeaa4a0SAndreas Gohr */ 161cbeaa4a0SAndreas Gohr public function registerOption($long, $help, $short = null, $needsarg = false, $command = '') 162cbeaa4a0SAndreas Gohr { 163cbeaa4a0SAndreas Gohr if (!isset($this->setup[$command])) { 164cbeaa4a0SAndreas Gohr throw new Exception("Command $command not registered"); 165cbeaa4a0SAndreas Gohr } 166cbeaa4a0SAndreas Gohr 167cbeaa4a0SAndreas Gohr $this->setup[$command]['opts'][$long] = array( 168cbeaa4a0SAndreas Gohr 'needsarg' => $needsarg, 169cbeaa4a0SAndreas Gohr 'help' => $help, 170cbeaa4a0SAndreas Gohr 'short' => $short 171cbeaa4a0SAndreas Gohr ); 172cbeaa4a0SAndreas Gohr 173cbeaa4a0SAndreas Gohr if ($short) { 174cbeaa4a0SAndreas Gohr if (strlen($short) > 1) { 175cbeaa4a0SAndreas Gohr throw new Exception("Short options should be exactly one ASCII character"); 176cbeaa4a0SAndreas Gohr } 177cbeaa4a0SAndreas Gohr 178cbeaa4a0SAndreas Gohr $this->setup[$command]['short'][$short] = $long; 179cbeaa4a0SAndreas Gohr } 180cbeaa4a0SAndreas Gohr } 181cbeaa4a0SAndreas Gohr 182cbeaa4a0SAndreas Gohr /** 183cbeaa4a0SAndreas Gohr * Checks the actual number of arguments against the required number 184cbeaa4a0SAndreas Gohr * 185cbeaa4a0SAndreas Gohr * Throws an exception if arguments are missing. 186cbeaa4a0SAndreas Gohr * 187cbeaa4a0SAndreas Gohr * This is run from CLI automatically and usually does not need to be called directly 188cbeaa4a0SAndreas Gohr * 189cbeaa4a0SAndreas Gohr * @throws Exception 190cbeaa4a0SAndreas Gohr */ 191cbeaa4a0SAndreas Gohr public function checkArguments() 192cbeaa4a0SAndreas Gohr { 193cbeaa4a0SAndreas Gohr $argc = count($this->args); 194cbeaa4a0SAndreas Gohr 195cbeaa4a0SAndreas Gohr $req = 0; 196cbeaa4a0SAndreas Gohr foreach ($this->setup[$this->command]['args'] as $arg) { 197cbeaa4a0SAndreas Gohr if (!$arg['required']) { 198cbeaa4a0SAndreas Gohr break; 199cbeaa4a0SAndreas Gohr } // last required arguments seen 200cbeaa4a0SAndreas Gohr $req++; 201cbeaa4a0SAndreas Gohr } 202cbeaa4a0SAndreas Gohr 203cbeaa4a0SAndreas Gohr if ($req > $argc) { 204cbeaa4a0SAndreas Gohr throw new Exception("Not enough arguments", Exception::E_OPT_ARG_REQUIRED); 205cbeaa4a0SAndreas Gohr } 206cbeaa4a0SAndreas Gohr } 207cbeaa4a0SAndreas Gohr 208cbeaa4a0SAndreas Gohr /** 209cbeaa4a0SAndreas Gohr * Parses the given arguments for known options and command 210cbeaa4a0SAndreas Gohr * 211cbeaa4a0SAndreas Gohr * The given $args array should NOT contain the executed file as first item anymore! The $args 212cbeaa4a0SAndreas Gohr * array is stripped from any options and possible command. All found otions can be accessed via the 213cbeaa4a0SAndreas Gohr * getOpt() function 214cbeaa4a0SAndreas Gohr * 215cbeaa4a0SAndreas Gohr * Note that command options will overwrite any global options with the same name 216cbeaa4a0SAndreas Gohr * 217cbeaa4a0SAndreas Gohr * This is run from CLI automatically and usually does not need to be called directly 218cbeaa4a0SAndreas Gohr * 219cbeaa4a0SAndreas Gohr * @throws Exception 220cbeaa4a0SAndreas Gohr */ 221cbeaa4a0SAndreas Gohr public function parseOptions() 222cbeaa4a0SAndreas Gohr { 223cbeaa4a0SAndreas Gohr $non_opts = array(); 224cbeaa4a0SAndreas Gohr 225cbeaa4a0SAndreas Gohr $argc = count($this->args); 226cbeaa4a0SAndreas Gohr for ($i = 0; $i < $argc; $i++) { 227cbeaa4a0SAndreas Gohr $arg = $this->args[$i]; 228cbeaa4a0SAndreas Gohr 229cbeaa4a0SAndreas Gohr // The special element '--' means explicit end of options. Treat the rest of the arguments as non-options 230cbeaa4a0SAndreas Gohr // and end the loop. 231cbeaa4a0SAndreas Gohr if ($arg == '--') { 232cbeaa4a0SAndreas Gohr $non_opts = array_merge($non_opts, array_slice($this->args, $i + 1)); 233cbeaa4a0SAndreas Gohr break; 234cbeaa4a0SAndreas Gohr } 235cbeaa4a0SAndreas Gohr 236cbeaa4a0SAndreas Gohr // '-' is stdin - a normal argument 237cbeaa4a0SAndreas Gohr if ($arg == '-') { 238cbeaa4a0SAndreas Gohr $non_opts = array_merge($non_opts, array_slice($this->args, $i)); 239cbeaa4a0SAndreas Gohr break; 240cbeaa4a0SAndreas Gohr } 241cbeaa4a0SAndreas Gohr 242cbeaa4a0SAndreas Gohr // first non-option 24364604f4eSAndreas Gohr if ($arg[0] != '-') { 244cbeaa4a0SAndreas Gohr $non_opts = array_merge($non_opts, array_slice($this->args, $i)); 245cbeaa4a0SAndreas Gohr break; 246cbeaa4a0SAndreas Gohr } 247cbeaa4a0SAndreas Gohr 248cbeaa4a0SAndreas Gohr // long option 24964604f4eSAndreas Gohr if (strlen($arg) > 1 && $arg[1] === '-') { 250cbeaa4a0SAndreas Gohr $arg = explode('=', substr($arg, 2), 2); 251cbeaa4a0SAndreas Gohr $opt = array_shift($arg); 252cbeaa4a0SAndreas Gohr $val = array_shift($arg); 253cbeaa4a0SAndreas Gohr 254cbeaa4a0SAndreas Gohr if (!isset($this->setup[$this->command]['opts'][$opt])) { 255cbeaa4a0SAndreas Gohr throw new Exception("No such option '$opt'", Exception::E_UNKNOWN_OPT); 256cbeaa4a0SAndreas Gohr } 257cbeaa4a0SAndreas Gohr 258cbeaa4a0SAndreas Gohr // argument required? 259cbeaa4a0SAndreas Gohr if ($this->setup[$this->command]['opts'][$opt]['needsarg']) { 260cbeaa4a0SAndreas Gohr if (is_null($val) && $i + 1 < $argc && !preg_match('/^--?[\w]/', $this->args[$i + 1])) { 261cbeaa4a0SAndreas Gohr $val = $this->args[++$i]; 262cbeaa4a0SAndreas Gohr } 263cbeaa4a0SAndreas Gohr if (is_null($val)) { 264cbeaa4a0SAndreas Gohr throw new Exception("Option $opt requires an argument", 265cbeaa4a0SAndreas Gohr Exception::E_OPT_ARG_REQUIRED); 266cbeaa4a0SAndreas Gohr } 267cbeaa4a0SAndreas Gohr $this->options[$opt] = $val; 268cbeaa4a0SAndreas Gohr } else { 269cbeaa4a0SAndreas Gohr $this->options[$opt] = true; 270cbeaa4a0SAndreas Gohr } 271cbeaa4a0SAndreas Gohr 272cbeaa4a0SAndreas Gohr continue; 273cbeaa4a0SAndreas Gohr } 274cbeaa4a0SAndreas Gohr 275cbeaa4a0SAndreas Gohr // short option 276cbeaa4a0SAndreas Gohr $opt = substr($arg, 1); 277cbeaa4a0SAndreas Gohr if (!isset($this->setup[$this->command]['short'][$opt])) { 278cbeaa4a0SAndreas Gohr throw new Exception("No such option $arg", Exception::E_UNKNOWN_OPT); 279cbeaa4a0SAndreas Gohr } else { 280cbeaa4a0SAndreas Gohr $opt = $this->setup[$this->command]['short'][$opt]; // store it under long name 281cbeaa4a0SAndreas Gohr } 282cbeaa4a0SAndreas Gohr 283cbeaa4a0SAndreas Gohr // argument required? 284cbeaa4a0SAndreas Gohr if ($this->setup[$this->command]['opts'][$opt]['needsarg']) { 285cbeaa4a0SAndreas Gohr $val = null; 286cbeaa4a0SAndreas Gohr if ($i + 1 < $argc && !preg_match('/^--?[\w]/', $this->args[$i + 1])) { 287cbeaa4a0SAndreas Gohr $val = $this->args[++$i]; 288cbeaa4a0SAndreas Gohr } 289cbeaa4a0SAndreas Gohr if (is_null($val)) { 290cbeaa4a0SAndreas Gohr throw new Exception("Option $arg requires an argument", 291cbeaa4a0SAndreas Gohr Exception::E_OPT_ARG_REQUIRED); 292cbeaa4a0SAndreas Gohr } 293cbeaa4a0SAndreas Gohr $this->options[$opt] = $val; 294cbeaa4a0SAndreas Gohr } else { 295cbeaa4a0SAndreas Gohr $this->options[$opt] = true; 296cbeaa4a0SAndreas Gohr } 297cbeaa4a0SAndreas Gohr } 298cbeaa4a0SAndreas Gohr 299cbeaa4a0SAndreas Gohr // parsing is now done, update args array 300cbeaa4a0SAndreas Gohr $this->args = $non_opts; 301cbeaa4a0SAndreas Gohr 302cbeaa4a0SAndreas Gohr // if not done yet, check if first argument is a command and reexecute argument parsing if it is 303cbeaa4a0SAndreas Gohr if (!$this->command && $this->args && isset($this->setup[$this->args[0]])) { 304cbeaa4a0SAndreas Gohr // it is a command! 305cbeaa4a0SAndreas Gohr $this->command = array_shift($this->args); 306cbeaa4a0SAndreas Gohr $this->parseOptions(); // second pass 307cbeaa4a0SAndreas Gohr } 308cbeaa4a0SAndreas Gohr } 309cbeaa4a0SAndreas Gohr 310cbeaa4a0SAndreas Gohr /** 311cbeaa4a0SAndreas Gohr * Get the value of the given option 312cbeaa4a0SAndreas Gohr * 313cbeaa4a0SAndreas Gohr * Please note that all options are accessed by their long option names regardless of how they were 314cbeaa4a0SAndreas Gohr * specified on commandline. 315cbeaa4a0SAndreas Gohr * 316cbeaa4a0SAndreas Gohr * Can only be used after parseOptions() has been run 317cbeaa4a0SAndreas Gohr * 318cbeaa4a0SAndreas Gohr * @param mixed $option 319cbeaa4a0SAndreas Gohr * @param bool|string $default what to return if the option was not set 32043d3f077SAndreas Gohr * @return bool|string|string[] 321cbeaa4a0SAndreas Gohr */ 322cbeaa4a0SAndreas Gohr public function getOpt($option = null, $default = false) 323cbeaa4a0SAndreas Gohr { 324cbeaa4a0SAndreas Gohr if ($option === null) { 325cbeaa4a0SAndreas Gohr return $this->options; 326cbeaa4a0SAndreas Gohr } 327cbeaa4a0SAndreas Gohr 328cbeaa4a0SAndreas Gohr if (isset($this->options[$option])) { 329cbeaa4a0SAndreas Gohr return $this->options[$option]; 330cbeaa4a0SAndreas Gohr } 331cbeaa4a0SAndreas Gohr return $default; 332cbeaa4a0SAndreas Gohr } 333cbeaa4a0SAndreas Gohr 334cbeaa4a0SAndreas Gohr /** 335cbeaa4a0SAndreas Gohr * Return the found command if any 336cbeaa4a0SAndreas Gohr * 337cbeaa4a0SAndreas Gohr * @return string 338cbeaa4a0SAndreas Gohr */ 339cbeaa4a0SAndreas Gohr public function getCmd() 340cbeaa4a0SAndreas Gohr { 341cbeaa4a0SAndreas Gohr return $this->command; 342cbeaa4a0SAndreas Gohr } 343cbeaa4a0SAndreas Gohr 344cbeaa4a0SAndreas Gohr /** 345cbeaa4a0SAndreas Gohr * Get all the arguments passed to the script 346cbeaa4a0SAndreas Gohr * 347cbeaa4a0SAndreas Gohr * This will not contain any recognized options or the script name itself 348cbeaa4a0SAndreas Gohr * 349cbeaa4a0SAndreas Gohr * @return array 350cbeaa4a0SAndreas Gohr */ 351cbeaa4a0SAndreas Gohr public function getArgs() 352cbeaa4a0SAndreas Gohr { 353cbeaa4a0SAndreas Gohr return $this->args; 354cbeaa4a0SAndreas Gohr } 355cbeaa4a0SAndreas Gohr 356cbeaa4a0SAndreas Gohr /** 357cbeaa4a0SAndreas Gohr * Builds a help screen from the available options. You may want to call it from -h or on error 358cbeaa4a0SAndreas Gohr * 359cbeaa4a0SAndreas Gohr * @return string 36043d3f077SAndreas Gohr * 36143d3f077SAndreas Gohr * @throws Exception 362cbeaa4a0SAndreas Gohr */ 363cbeaa4a0SAndreas Gohr public function help() 364cbeaa4a0SAndreas Gohr { 365cbeaa4a0SAndreas Gohr $tf = new TableFormatter($this->colors); 366cbeaa4a0SAndreas Gohr $text = ''; 367cbeaa4a0SAndreas Gohr 368cbeaa4a0SAndreas Gohr $hascommands = (count($this->setup) > 1); 369*6cb05674SAndreas Gohr $commandhelp = $this->setup['']["commandhelp"]; 370*6cb05674SAndreas Gohr 371cbeaa4a0SAndreas Gohr foreach ($this->setup as $command => $config) { 372cbeaa4a0SAndreas Gohr $hasopts = (bool)$this->setup[$command]['opts']; 373cbeaa4a0SAndreas Gohr $hasargs = (bool)$this->setup[$command]['args']; 374cbeaa4a0SAndreas Gohr 375cbeaa4a0SAndreas Gohr // usage or command syntax line 376cbeaa4a0SAndreas Gohr if (!$command) { 377cbeaa4a0SAndreas Gohr $text .= $this->colors->wrap('USAGE:', Colors::C_BROWN); 378cbeaa4a0SAndreas Gohr $text .= "\n"; 379cbeaa4a0SAndreas Gohr $text .= ' ' . $this->bin; 380cbeaa4a0SAndreas Gohr $mv = 2; 381cbeaa4a0SAndreas Gohr } else { 382*6cb05674SAndreas Gohr $text .= $this->newline; 383cbeaa4a0SAndreas Gohr $text .= $this->colors->wrap(' ' . $command, Colors::C_PURPLE); 384cbeaa4a0SAndreas Gohr $mv = 4; 385cbeaa4a0SAndreas Gohr } 386cbeaa4a0SAndreas Gohr 387cbeaa4a0SAndreas Gohr if ($hasopts) { 388cbeaa4a0SAndreas Gohr $text .= ' ' . $this->colors->wrap('<OPTIONS>', Colors::C_GREEN); 389cbeaa4a0SAndreas Gohr } 390cbeaa4a0SAndreas Gohr 391cbeaa4a0SAndreas Gohr if (!$command && $hascommands) { 392cbeaa4a0SAndreas Gohr $text .= ' ' . $this->colors->wrap('<COMMAND> ...', Colors::C_PURPLE); 393cbeaa4a0SAndreas Gohr } 394cbeaa4a0SAndreas Gohr 395cbeaa4a0SAndreas Gohr foreach ($this->setup[$command]['args'] as $arg) { 396cbeaa4a0SAndreas Gohr $out = $this->colors->wrap('<' . $arg['name'] . '>', Colors::C_CYAN); 397cbeaa4a0SAndreas Gohr 398cbeaa4a0SAndreas Gohr if (!$arg['required']) { 399cbeaa4a0SAndreas Gohr $out = '[' . $out . ']'; 400cbeaa4a0SAndreas Gohr } 401cbeaa4a0SAndreas Gohr $text .= ' ' . $out; 402cbeaa4a0SAndreas Gohr } 403*6cb05674SAndreas Gohr $text .= $this->newline; 404cbeaa4a0SAndreas Gohr 405cbeaa4a0SAndreas Gohr // usage or command intro 406cbeaa4a0SAndreas Gohr if ($this->setup[$command]['help']) { 407cbeaa4a0SAndreas Gohr $text .= "\n"; 408cbeaa4a0SAndreas Gohr $text .= $tf->format( 409cbeaa4a0SAndreas Gohr array($mv, '*'), 410*6cb05674SAndreas Gohr array('', $this->setup[$command]['help'] . $this->newline) 411cbeaa4a0SAndreas Gohr ); 412cbeaa4a0SAndreas Gohr } 413cbeaa4a0SAndreas Gohr 414cbeaa4a0SAndreas Gohr // option description 415cbeaa4a0SAndreas Gohr if ($hasopts) { 416cbeaa4a0SAndreas Gohr if (!$command) { 417cbeaa4a0SAndreas Gohr $text .= "\n"; 418cbeaa4a0SAndreas Gohr $text .= $this->colors->wrap('OPTIONS:', Colors::C_BROWN); 419cbeaa4a0SAndreas Gohr } 420cbeaa4a0SAndreas Gohr $text .= "\n"; 421cbeaa4a0SAndreas Gohr foreach ($this->setup[$command]['opts'] as $long => $opt) { 422cbeaa4a0SAndreas Gohr 423cbeaa4a0SAndreas Gohr $name = ''; 424cbeaa4a0SAndreas Gohr if ($opt['short']) { 425cbeaa4a0SAndreas Gohr $name .= '-' . $opt['short']; 426cbeaa4a0SAndreas Gohr if ($opt['needsarg']) { 427cbeaa4a0SAndreas Gohr $name .= ' <' . $opt['needsarg'] . '>'; 428cbeaa4a0SAndreas Gohr } 429cbeaa4a0SAndreas Gohr $name .= ', '; 430cbeaa4a0SAndreas Gohr } 431cbeaa4a0SAndreas Gohr $name .= "--$long"; 432cbeaa4a0SAndreas Gohr if ($opt['needsarg']) { 433cbeaa4a0SAndreas Gohr $name .= ' <' . $opt['needsarg'] . '>'; 434cbeaa4a0SAndreas Gohr } 435cbeaa4a0SAndreas Gohr 436cbeaa4a0SAndreas Gohr $text .= $tf->format( 437cbeaa4a0SAndreas Gohr array($mv, '30%', '*'), 438cbeaa4a0SAndreas Gohr array('', $name, $opt['help']), 439cbeaa4a0SAndreas Gohr array('', 'green', '') 440cbeaa4a0SAndreas Gohr ); 441*6cb05674SAndreas Gohr $text .= $this->newline; 442cbeaa4a0SAndreas Gohr } 443cbeaa4a0SAndreas Gohr } 444cbeaa4a0SAndreas Gohr 445cbeaa4a0SAndreas Gohr // argument description 446cbeaa4a0SAndreas Gohr if ($hasargs) { 447cbeaa4a0SAndreas Gohr if (!$command) { 448cbeaa4a0SAndreas Gohr $text .= "\n"; 449cbeaa4a0SAndreas Gohr $text .= $this->colors->wrap('ARGUMENTS:', Colors::C_BROWN); 450cbeaa4a0SAndreas Gohr } 451*6cb05674SAndreas Gohr $text .= $this->newline; 452cbeaa4a0SAndreas Gohr foreach ($this->setup[$command]['args'] as $arg) { 453cbeaa4a0SAndreas Gohr $name = '<' . $arg['name'] . '>'; 454cbeaa4a0SAndreas Gohr 455cbeaa4a0SAndreas Gohr $text .= $tf->format( 456cbeaa4a0SAndreas Gohr array($mv, '30%', '*'), 457cbeaa4a0SAndreas Gohr array('', $name, $arg['help']), 458cbeaa4a0SAndreas Gohr array('', 'cyan', '') 459cbeaa4a0SAndreas Gohr ); 460cbeaa4a0SAndreas Gohr } 461cbeaa4a0SAndreas Gohr } 462cbeaa4a0SAndreas Gohr 463cbeaa4a0SAndreas Gohr // head line and intro for following command documentation 464cbeaa4a0SAndreas Gohr if (!$command && $hascommands) { 465cbeaa4a0SAndreas Gohr $text .= "\n"; 466cbeaa4a0SAndreas Gohr $text .= $this->colors->wrap('COMMANDS:', Colors::C_BROWN); 467cbeaa4a0SAndreas Gohr $text .= "\n"; 468cbeaa4a0SAndreas Gohr $text .= $tf->format( 469cbeaa4a0SAndreas Gohr array($mv, '*'), 470*6cb05674SAndreas Gohr array('', $commandhelp) 471cbeaa4a0SAndreas Gohr ); 472*6cb05674SAndreas Gohr $text .= $this->newline; 473cbeaa4a0SAndreas Gohr } 474cbeaa4a0SAndreas Gohr } 475cbeaa4a0SAndreas Gohr 476cbeaa4a0SAndreas Gohr return $text; 477cbeaa4a0SAndreas Gohr } 478cbeaa4a0SAndreas Gohr 479cbeaa4a0SAndreas Gohr /** 480cbeaa4a0SAndreas Gohr * Safely read the $argv PHP array across different PHP configurations. 481cbeaa4a0SAndreas Gohr * Will take care on register_globals and register_argc_argv ini directives 482cbeaa4a0SAndreas Gohr * 483cbeaa4a0SAndreas Gohr * @throws Exception 484cbeaa4a0SAndreas Gohr * @return array the $argv PHP array or PEAR error if not registered 485cbeaa4a0SAndreas Gohr */ 486cbeaa4a0SAndreas Gohr private function readPHPArgv() 487cbeaa4a0SAndreas Gohr { 488cbeaa4a0SAndreas Gohr global $argv; 489cbeaa4a0SAndreas Gohr if (!is_array($argv)) { 490cbeaa4a0SAndreas Gohr if (!@is_array($_SERVER['argv'])) { 491cbeaa4a0SAndreas Gohr if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) { 492cbeaa4a0SAndreas Gohr throw new Exception( 493cbeaa4a0SAndreas Gohr "Could not read cmd args (register_argc_argv=Off?)", 494cbeaa4a0SAndreas Gohr Exception::E_ARG_READ 495cbeaa4a0SAndreas Gohr ); 496cbeaa4a0SAndreas Gohr } 497cbeaa4a0SAndreas Gohr return $GLOBALS['HTTP_SERVER_VARS']['argv']; 498cbeaa4a0SAndreas Gohr } 499cbeaa4a0SAndreas Gohr return $_SERVER['argv']; 500cbeaa4a0SAndreas Gohr } 501cbeaa4a0SAndreas Gohr return $argv; 502cbeaa4a0SAndreas Gohr } 503cbeaa4a0SAndreas Gohr} 504cbeaa4a0SAndreas Gohr 505