argv = $argv?: $_SERVER['argv']; $this->progname = $this->argv[0]; $this->addRules($rules); } /** * Return a list of options that have been seen in the current argv. * * @return array */ public function getOptions() { $this->parse(); return $this->options; } /** * Return the state of the option seen on the command line of the * current application invocation. * * This function returns true, or the parameter value to the option, if any. * If the option was not given, this function returns false. * * @param string $flag * @return mixed */ public function getOption($flag) { $this->parse(); $flag = strtolower($flag); if (isset($this->ruleMap[$flag])) { $flag = $this->ruleMap[$flag]; if (isset($this->options[$flag])) { return $this->options[$flag]; } } return; } /** * Return a useful option reference, formatted for display in an * error message. * * Note that this usage information is provided in most Exceptions * generated by this class. * * @return string */ public function getUsageMessage() { $usage = "Usage: {$this->progname} [ options ]\n"; $maxLen = 20; $lines = array(); foreach ($this->rules as $rule) { if (isset($rule['isFreeformFlag'])) { continue; } $flags = array(); if (is_array($rule['alias'])) { foreach ($rule['alias'] as $flag) { $flags[] = (strlen($flag) == 1 ? '-' : '--') . $flag; } } $linepart['name'] = implode('|', $flags); if (isset($rule['param']) && $rule['param'] != 'none') { $linepart['name'] .= '=""'; switch ($rule['param']) { case 'optional': $linepart['name'] .= " (optional)"; break; case 'required': $linepart['name'] .= " (required)"; break; } } if (strlen($linepart['name']) > $maxLen) { $maxLen = strlen($linepart['name']); } $linepart['help'] = ''; if (isset($rule['help'])) { $linepart['help'] .= $rule['help']; } $lines[] = $linepart; } foreach ($lines as $linepart) { $usage .= sprintf( "%s %s\n", str_pad($linepart['name'], $maxLen), $linepart['help'] ); } return $usage; } /** * Parse command-line arguments and find both long and short * options. * * Also find option parameters, and remaining arguments after * all options have been parsed. * * @return self */ public function parse() { if ($this->parsed === true) { return $this; } if (in_array('--help', $this->argv)) { echo $this->getUsageMessage(); exit; } $this->options = array(); $long = []; $short = ''; foreach ($this->rules as $rule) { foreach ($rule['alias'] as $alias) { $prepared = $alias; if ($rule['param'] == 'optional') { $prepared .= '::'; } elseif ($rule['param'] == 'required') { $prepared .= ':'; } if (strlen($alias) == 1) { $short .= $prepared; } else { $long[] = $prepared; } } } $result = getopt($short, $long); foreach ($result as $key => $value) { $this->options[$this->ruleMap[$key]] = $value; } $this->parsed = true; return $this; } /** * Define legal options using the Zend-style format. * * @param array $rules * @throws InvalidArgumentException */ protected function addRules($rules) { foreach ($rules as $ruleCode => $helpMessage) { // this may have to translate the long parm type if there // are any complaints that =string will not work (even though that use // case is not documented) if (in_array(substr($ruleCode, -2, 1), array('-', '='))) { $flagList = substr($ruleCode, 0, -2); $delimiter = substr($ruleCode, -2, 1); } else { $flagList = $ruleCode; $delimiter = $paramType = null; } $flagList = strtolower($flagList); $flags = explode('|', $flagList); $rule = array(); $mainFlag = $flags[0]; foreach ($flags as $flag) { if (empty($flag)) { throw new InvalidArgumentException("Blank flag not allowed in rule \"$ruleCode\"."); } if (isset($this->ruleMap[$flag]) || (strlen($flag) != 1 && isset($this->rules[$flag]))) { throw new InvalidArgumentException("Option \"-$flag\" is being defined more than once."); } $this->ruleMap[$flag] = $mainFlag; $rule['alias'][] = $flag; } $rule['param'] = 'none'; if (isset($delimiter)) { $rule['param'] = $delimiter == self::PARAM_REQUIRED? 'required' : 'optional'; } $rule['help'] = $helpMessage; $this->rules[$mainFlag] = $rule; } } }