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