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