Created
September 14, 2010 19:55
-
-
Save njones/579660 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* return all of the commandline options as a nice list of things. | |
* This is modeled after the Python Optparse. (so it's easy!!) with some | |
* BASH getops flavor. | |
* | |
* @author Nika Jones | |
* | |
* @copyright 2010 Appcelerator Inc. | |
* | |
* Licensed under the terms of the Apache Public License | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
*/ | |
class OptParse | |
{ | |
/** | |
* A constant for parsing ALL values and subsituting the default value | |
*/ | |
const WITH_DEFAULTS = TRUE; | |
/** | |
* A bool of what state the parser is in. If it's a short option | |
* or not. | |
*/ | |
private $is_short_option = FALSE; | |
/** | |
* The letter that represents the short option | |
*/ | |
private $short_option_letter = ""; | |
/** | |
* An array that holds all of the options | |
* in a key=value | |
*/ | |
private $parse_map = array(); | |
/** | |
* Maps short options to long options (i.e. -f == file) | |
*/ | |
private $short_to_long_map = array(); | |
/** | |
* Maps short options to long options (i.e. -f == file) | |
*/ | |
private $help_map = array(); | |
/** | |
* An internal space seperated string of the arguments presented on the commandline | |
*/ | |
private $args; | |
/** | |
* An array of non-option arguments (this is mainly used for the auto-generated help) | |
*/ | |
private $argument_name = array(); | |
/** | |
* The name of the calling script | |
*/ | |
private $script_name; | |
/** | |
* | |
*/ | |
private $help_description = ""; | |
/** | |
* The constructor to create an object. | |
*/ | |
public function __construct($argvs = NULL, $script_name = ":_internal_:") | |
{ | |
// The argv from the calling script... | |
global $argv; | |
$_argv = $argv; | |
/* Checks to see if the args are passed in or not. | |
* If not then use the global args, remember to | |
* to pass in the scriptname (for the auto-gnerated help). | |
*/ | |
if(is_null($argvs)) | |
{ | |
$this->script_name = array_shift($_argv); | |
// Args that have spaces in them, should be quoted... so this will do that. | |
$this->args = $_argv; | |
} | |
else | |
{ | |
$this->script_name = $script_name; | |
$this->args = (array)$argvs; | |
} | |
// this is the inital set up so the -h can be help (this can be overriden) | |
$this->short_to_long_map["-h"] = "help"; | |
} | |
/** | |
* The static method way of calling the option parse class. | |
* This is the preferred way of starting things. | |
* | |
* @param string $argvs an array of arguments (i.e. -f FILENAME --with-path=/path) | |
would equal: array("-f", "FILENAME", "--with-path=/path") | |
* | |
* @returns object OptParse | |
*/ | |
public static function OptionParser($argvs = NULL, $script_name = ":_internal_:") | |
{ | |
return new optparse($argvs, $script_name); | |
} | |
/** | |
* How to add an option to the parser. So that it can be recognized | |
* and placed automatically into the help -h option (unless overridden) | |
* | |
* @param string $argument_name The long name of the option (i.e. --file) | |
* @param string $description A description which will be in the help section | |
* @param int $index A specific index, so we can have optional arguments for | |
* the same index position | |
* @returns null | |
*/ | |
public function add_description($description) | |
{ | |
$this->help_description = $description; | |
} | |
/** | |
* How to add an option to the parser. So that it can be recognized | |
* and placed automatically into the help -h option (unless overridden) | |
* | |
* @param string $argument_name The long name of the option (i.e. --file) | |
* @param string $description A description which will be in the help section | |
* @param int $index A specific index, so we can have optional arguments for | |
* the same index position | |
* @returns null | |
*/ | |
public function add_argument($argument_name, $description = NULL, $index = NULL) | |
{ | |
static $n = 1; | |
if(!isset($index)) | |
{ | |
$index = $n++; | |
} | |
else | |
{ | |
$n = $index >= $n ? $index + 1 : $n; // add one to the | |
} | |
$this->argument_name[] = array($argument_name, $index); | |
$this->help_map[$argument_name]["description"] = $description; | |
} | |
/** | |
* Adds an option to the parser | |
* | |
* @params string $long The long option (i.e. --file) | |
* @params string $short The short option to use (i.e. -f) | |
* @params array $options The actual options. | |
*/ | |
public function add_option($long, $short, $options = array()) | |
{ | |
static $internal_counter = 1; | |
if($long == "--help") // then remove the default mapping | |
{ | |
unset($this->short_to_long_map["-h"]); | |
} | |
// Check to make sure that there is atleast an option!! | |
if(empty($long) && empty($short)) | |
{ | |
throw new Exception("You must have atleast one option."); | |
} | |
// Account for a missiong long option | |
if(empty($long)) | |
{ | |
$long = "-" . $short . "__" . $internal_counter++ . "__"; | |
} | |
// Account for a missing short option | |
if(empty($short)) | |
{ | |
$short = "-" . $long[2] . "__" . $internal_counter++ . "__"; | |
} | |
// remove the --'s | |
$long = substr($long, 2); | |
// Note short will have the "-" prefix | |
$this->short_to_long_map[$short] = $long; | |
$this->long_to_short_map[$long] = $short; | |
if(!isset($this->parse_map[$long])) | |
{ | |
$this->parse_map[$long] = array(); | |
} | |
if(isset($options["metavar"])) | |
{ | |
$this->parse_map[$long]["metavar"] = $options["metavar"]; | |
} | |
if(isset($options["default"])) | |
{ | |
$this->parse_map[$long]["default"] = $options["default"]; | |
} | |
if(isset($options["description"])) | |
{ | |
$this->help_map[$long]["description"] = $options["description"]; | |
} | |
} | |
/** | |
* Return the name of the running script. (good for nested parsings) | |
*/ | |
public function name() | |
{ | |
return $this->script_name; | |
} | |
/** | |
* Parse the arguments out and place into the appropiate place in an array | |
* | |
* @params const $with_defaults Will fill in all default values, otherwise a non | |
* used value will not be set or NULL | |
*/ | |
public function parse_args($with_defaults = NULL) | |
{ | |
$arguments = array(); | |
$defaults = $options = array(); | |
if($with_defaults == self::WITH_DEFAULTS) | |
{ | |
foreach($this->parse_map as $long_default_key => $long_default) | |
{ | |
if(isset($this->parse_map[$long_default_key]["default"])) | |
{ | |
$defaults[$long_default_key] = $this->parse_map[$long_default_key]["default"]; | |
} | |
} | |
} | |
$length = count($this->args) - 1; | |
while(list($index, $args) = each($this->args)) | |
{ | |
if(preg_match("/^--(.*)/", $args, $match)) | |
{ | |
list($long, $value) = array_pad(explode("=", $match[1]), 2, NULL); | |
$options[$long] = $value; | |
} | |
elseif(preg_match("/^(-(.))(.*)/", $args, $match)) | |
{ | |
if(!empty($match[3])) | |
{ | |
// Make into individual flags | |
foreach(str_split($match[2] . $match[3]) as $flag) | |
{ | |
if(isset($this->short_to_long_map["-" . $flag])) | |
{ | |
$long = $this->short_to_long_map["-" . $flag]; | |
$value = isset($this->parse_map[$long]["default"]) ? $this->parse_map[$long]["default"] : TRUE; | |
$options[$long] = $value; | |
} | |
else | |
{ | |
if("-$flag" == $match[1]) | |
{ | |
throw new Exception("The short key -" . $flag . " is not an option.", 500); | |
} | |
$long = $this->short_to_long_map[$match[1]]; | |
$value = $match[3]; | |
$options[$long] = $value; | |
break; | |
} | |
} | |
} | |
else | |
{ | |
if(!isset($this->short_to_long_map[$match[1]])) | |
{ | |
throw new Exception("The short key " . $match[1] . " is not an option.", 500); | |
} | |
$long = $this->short_to_long_map[$match[1]]; | |
$current = current($this->args); | |
$value = $index == $length && $current == FALSE ? TRUE : $current; | |
$options[$long] = $value; | |
next($this->args); // skip the next one. | |
} | |
} | |
else | |
{ | |
$arguments[] = $args; | |
} | |
} | |
if(isset($options["help"])) { $options["help"] = $this->build_help(); } | |
return array(array_merge($defaults, $options), $arguments); | |
} | |
/** | |
* Automatically build the help screen, out of the values we have for the options and | |
* arugments added to the parser. | |
*/ | |
private function build_help() | |
{ | |
// build the help text if theres a need | |
$argument_names = ""; | |
foreach((array)$this->argument_name as $arg) | |
{ | |
$argument_list[$arg[1]][] = $arg[0]; | |
} | |
foreach($argument_list as $argument_group) | |
{ | |
$argument_grouped = implode("|", $argument_group); | |
$argument_line[] = count($argument_group) > 1 ? "[" . $argument_grouped . "]" : $argument_grouped; | |
} | |
$arguments = trim(implode(" ", $argument_line)); | |
$_padding = 25; | |
$scriptname = ltrim($this->script_name, './'); | |
// $help_text = "HELP FOR " . $scriptname . "\n\n"; | |
$help_text = "usage " . $scriptname . " " . $arguments . " [OPTIONS]\n\n"; | |
$help_text .= (empty($this->help_description) ? "" : "Description:\n" . $this->help_description . "\n\n"); | |
$help_text .= "Options:\n"; | |
$help_map = $this->help_map; | |
$help_map["--help"] = array("description"=>"This help screen."); | |
if(!isset($this->long_to_short_map["--help"])) { $this->long_to_short_map["--help"] = "-h"; } | |
ksort($help_map); | |
foreach($help_map as $help_long => $help_data) | |
{ | |
if($help_long[0] == "-") | |
{ | |
// Check the option to see if it's internal or not, so we know to display it or not. | |
$opt = array(); | |
$opt[] = preg_match("/__\d+__$/", $this->long_to_short_map[$help_long]) ? NULL : $this->long_to_short_map[$help_long]; | |
$opt[] = preg_match("/__\d+__$/", $help_long) ? NULL : $help_long; | |
$help_opts = implode(", ", array_filter($opt)); | |
$help_opts .= isset($this->parse_map[$help_long]["metavar"]) ? (isset($opt[1]) ? "=" : " ") . $this->parse_map[$help_long]["metavar"] : ""; | |
$help_line = str_pad($help_opts, $_padding); | |
$help_line .= " " . $help_data["description"]."\n"; | |
// If there's a defaut then display that on the next line. | |
if(isset($this->parse_map[$help_long]["default"])) | |
{ | |
$help_line .= str_pad("", $_padding) . " default : " . $this->parse_map[$help_long]["default"] . "\n\n"; | |
} | |
$help_text .= $help_line; | |
} | |
else | |
{ | |
if(!isset($has_space)) { $help_text .= "\nArguments:\n"; $has_space = TRUE; } | |
$help_text .= str_pad($help_long, $_padding) . $help_data["description"] . "\n"; | |
} | |
} | |
return $help_text; | |
} | |
/** | |
* Make sure that all options are properly quoted | |
* | |
* @param string $arg a single argument of a commandline command. | |
*/ | |
private function quote_it($arg) | |
{ | |
return str_replace('""', "", preg_replace("/^(--?\w+=?|\w+=)?(.*)/", "$1\"$2\"", $arg)); | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment