Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/*** Console Getopt** All rights reserved.* Redistribution and use in source and binary forms, with or without modification,* are permitted provided that the following conditions are met:* + Redistributions of source code must retain the above copyright notice,* this list of conditions and the following disclaimer.* + Redistributions in binary form must reproduce the above copyright notice,* this list of conditions and the following disclaimer in the documentation and/or* other materials provided with the distribution.* + The names of its contributors may not be used to endorse or promote* products derived from this software without specific prior written permission.* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.** @category Console* @package Console_GetoptPlus* @author Michel Corne <mcorne@yahoo.com>* @copyright 2008 Michel Corne* @license http://www.opensource.org/licenses/bsd-license.php The BSD License* @version SVN: $Id: Getopt.php 48 2008-01-10 15:32:56Z mcorne $* @link http://pear.php.net/package/Console_GetoptPlus*/require_once 'Console/GetoptPlus/Exception.php';/*** Parsing of a command line.** See more examples in docs/examples.** Code Example 1:* <code>* require_once 'Console/GetoptPlus.php';** try {* $shortOptions = "b:c::";* $longOptions = array("foo", "bar=");* $options = Console_Getoptplus::getopt($config, $shortOptions, $longOptions);* // some processing here...* print_r($options);* }* catch(Console_GetoptPlus_Exception $e) {* $error = array($e->getCode(), $e->getMessage());* print_r($error);* }* </code>** Code Example 2:* <code>* require_once 'Console/GetoptPlus/Getopt.php';** try {* $shortOptions = "b:c::";* $longOptions = array("foo", "bar=");* $options = Console_GetoptPlus_Getopt::getopt($config, $shortOptions, $longOptions);* // some processing here...* print_r($options);* }* catch(Console_GetoptPlus_Exception $e) {* $error = array($e->getCode(), $e->getMessage());* print_r($error);* }* </code>** Run:* <pre>* #xyz --foo -b car -c* #xyz --foo -b car -c param1* #xyz --foo -b car -cbus param1* #xyz --foo -b car -c=bus param1* #xyz --bar car param1 param2* #xyz --bar car -- param1 param2* #xyz --bar=car param1 param2* </pre>** @category Console* @package Console_GetoptPlus* @author Michel Corne <mcorne@yahoo.com>* @copyright 2008 Michel Corne* @license http://www.opensource.org/licenses/bsd-license.php The BSD License* @version Release:* @package _version@* @link http://pear.php.net/package/Console_GetoptPlus* @see Console_Getopt*/class Console_GetoptPlus_Getopt{/*** The list of ambigous option names** @var array* @access private*/private $ambigous;/*** The command arguments** @var array* @access private*/private $args;/*** The long option names** @var array* @access private*/private $longOptionsDef;/*** The parsed options** @var array* @access private*/private $options;/*** The option shortcut names** @var array* @access private*/private $shortcuts;/*** The short option names and their definition** @var array* @access private*/private $shortOptionsDef;/*** The option types** @var array* @access private*/private $type = array(// /false => 'noarg','=' => 'mandatory', ':' => 'mandatory','==' => 'optional', '::' => 'optional',);/*** Creates the option shorcut names** @param array $longOptionsDef the long option names* @param string $ambiguity directive to handle option names ambiguity* @return array the option shorcuts and the ambigous options* @access public*/public function createShorcuts($longOptionsDef, $ambiguity){$shortcuts = array();$ambigous = array();if ($ambiguity == 'shortcuts') {foreach(array_keys($longOptionsDef) as $name) {// splits the option name in characters to build the name// substring combinations, e.g. foo => f, fo, foo$subName = '';foreach(str_split($name) as $char) {$subName .= $char;if (isset($ambigous[$subName])) {// adds the shortcut to the list of ambigous shortcuts$ambigous[$subName][] = $name;} else if (isset($shortcuts[$subName])) {// there is already a shortcut, adds the previous one// and the current one in the list of ambigous shortcuts$ambigous[$subName] = array($shortcuts[$subName], $name);unset($shortcuts[$subName]);} else {// creates the shorcut entry$shortcuts[$subName] = $name;}}}// checks if some options are ambigous, e.g. --foo --foobar$names = array_intersect_key($longOptionsDef, $ambigous) andself::exception('ambigous', key($names));}return array($shortcuts, $ambigous);}/*** Parses the command line** See getopt() for a complete description.** @param numeric $version the getopt version: 1 or 2* @param array $args the arguments* @param string $shortOptions the short options definition, e.g. "ab:c::"* @param array $longOptions the long options definition* @param string $ambiguity directive to handle option names ambiguity* @return array the parsed options, their arguments and parameters* @access public* @static*/public static function doGetopt($version = null, $args = array(),$shortOptions = '', $longOptions = array(), $ambiguity = ''){$getopt = new self;return $getopt->process($args, $shortOptions, $longOptions,$ambiguity, $version);}/*** Wraps the exception call** @return void* @access private* @throws Console_GetoptPlus_Exception Exception* @static*/private static function exception(){$error = func_get_args();throw new Console_GetoptPlus_Exception($error);}/*** Parses the command line** See the definition/example in the class Doc Block.** Example: returning an index array* <code>* array(* [0] => array("foo" => null, "bar" => "car", "c" => null),* [1] => array([0] => "param1", [1] => "param2")* );* </code>** @param array $args the arguments* @param string $shortOptions the short options definition, e.g. "ab:c::"* <ul>* <li>":" : the option requires an argument</li>* <li>"::" : the option accepts an optional argument</li>* <li>otherwise the option accepts no argument</li>* </ul>* @param array $longOptions the long options definition,* e.g. array("art", "bar=", "car==)* <ul>* <li>"=" : the option requires an argument</li>* <li>"==" : the option accepts an optional argument</li>* <li>otherwise the option accepts no argument</li>* </ul>* @param string $ambiguity directive to handle option names ambiguity,* e.g. "--foo" and "--foobar":* <ul>* <li>"loose": allowed if "--foo" does not* accept an argument, this is the default* behaviour</li>* <li>"strict": no ambiguity allowed</li>* <li>"shortcuts": implies "strict", the use of* partial option names is allowed,* e.g. "--f" or "--fo" instead of "--foo"</li>* </ul>* @return array the parsed options, their arguments and parameters* @access public* @static*/public static function getopt($args = array(), $shortOptions = '',$longOptions = array(), $ambiguity = ''){return self::doGetopt(1, $args, $shortOptions, $longOptions, $ambiguity);}/*** Parses the command line** See getopt() for a complete description.** @param array $args the arguments* @param string $shortOptions the short options definition, e.g. "ab:c::"* @param array $longOptions the long options definition* @param string $ambiguity directive to handle option names ambiguity* @return array the parsed options, their arguments and parameters* @access public* @static*/public static function getopt2($args = array(), $shortOptions = '',$longOptions = array(), $ambiguity = ''){return self::doGetopt(2, $args, $shortOptions, $longOptions, $ambiguity);}/*** Checks if the argument is an option** @param string $argument the argument, e.g. "-f" or "--foo"* @return boolean true if an option, false otherwise* @access public*/public function isOption($argument){return (bool)preg_match('~^(-\w|--\w+)$~', $argument);}/*** Parses a long option** @param string $argument the option and argument (excluding the "--" prefix),* e.g. "file=foo.php", "file foo.php", "bar"* @return void* @access public*/public function parseLongOption($argument){$option = explode('=', $argument, 2);$name = current($option);$arg = next($option) or $arg = null;// verifies the option is validisset($this->ambigous[$name]) and self::exception('ambigous', $name);isset($this->shortcuts[$name]) and $name = $this->shortcuts[$name] orisset($this->longOptionsDef[$name]) or self::exception('unrecognized', $name);if ($this->longOptionsDef[$name] == 'mandatory') {// the option requires an argument, e.g. --file=foo.php// tries the next argument if necessary, e.g. --file foo.phpis_null($arg) and list(, $arg) = each($this->args);is_null($arg) and self::exception('mandatory', $name);// verifies the argument is not an option itself$this->isOption($arg) and self::exception('mandatory', $name);} else if ($this->longOptionsDef[$name] == 'noarg' and !is_null($arg)) {// the option may not take an optional argumentself::exception('noargument', $name);}// capture the option and its argument$this->options[] = array('--' . $name, $arg);}/*** Parses the long option names and types** @param array $options the long options, e.g. array("foo", "bar=")* @return array the options name and type,* e.g. array("foo"=>"noarg", "bar"=>"mandatory")* @access public*/public function parseLongOptionsDef($options){// converts to an array if there is only one optionsettype($options, 'array');$longOptionsDef = array();foreach($options as $option) {if ($option = trim($option)) {// extracts the option name and type:// optional argument (==), mandatory (=), or none (null)// verifies the option syntax is correctpreg_match("~^(\w+)(==|=)?$~", $option, $match) orself::exception('invalid', $option);$name = next($match);$type = next($match);// verifies the option is not a duplicateisset($longOptionsDef[$name]) and self::exception('duplicate', $name);// captures the option name and type$longOptionsDef[$name] = $this->type[$type];}}return $longOptionsDef;}/*** Parses a short option** @param string $argument the option and argument (excluding the "-" prefix),* e.g. "zfoo.php", "z foo.php", "z".* @return void* @access public*/public function parseShortOption($argument){for ($i = 0; $i < strlen($argument); $i++) {$name = $argument{$i};$arg = null;// verifies the option is validisset($this->shortOptionsDef[$name]) or self::exception('unrecognized', $name);if ($this->shortOptionsDef[$name] == 'optional') {// the option may take an optional argument, e.g. -zfoo.php or -zif (($arg = substr($argument, $i + 1)) !== false) {// the remainder of the string is the option argument$this->options[] = array($name, $arg);return;}} else if ($this->shortOptionsDef[$name] == 'mandatory') {// the option requires an argument, -zfoo.php or -z foo.phpif (($arg = substr($argument, $i + 1)) === false) {// nothing left to use as the option argument// the next argument is expected to be the option argument// verifies there is one and it is not an option itselflist(, $arg) = each($this->args);(is_null($arg) or $this->isOption($arg)) andself::exception('mandatory', $name);}$this->options[] = array($name, $arg);return;}// else: the option is not expecting an argument, e.g. -h// TODO: verify that if followed by a non option which is interpreted// as the end of options, there is indeed no option after until// possibly -- or -// capture the option and its argument$this->options[] = array($name, $arg);}}/*** Parses the short option names and types** @param string $options the short options, e.g. array("ab:c::)* @return array the options name and type,* e.g. array("a"=>"noarg", "b"=>"mandatory", "c"=>"optional")* @access public*/public function parseShortOptionsDef($options){// expecting a string for a the short options definitionis_array($options) and self::exception('string');// trims and extracts the options name and type// optional argument (::), mandatory (:), or none (null)$options = trim($options);preg_match_all("~(\w)(::|:)?~", $options, $matches, PREG_SET_ORDER);$check = '';$shortOptionsDef = array();foreach($matches as $match) {$check .= current($match);$name = next($match);$type = next($match);// verifies the option is not a duplicateisset($shortOptionsDef[$name]) and self::exception('duplicate', $name);// captures the option name and type$shortOptionsDef[$name] = $this->type[$type];}// checks there is no syntax error the short options definition$check == $options or self::exception('syntax', $name);return $shortOptionsDef;}/*** Parses the command line** See getopt() for a complete description.** @param array $args the arguments* @param string $shortOptions the short options definition, e.g. "ab:c::"* @param array $longOptions the long options definition, e.g. array("foo", "bar=")* @param string $ambiguity directive to handle option names ambiguity* @param numeric $version the getopt version: 1 or 2* @return array the parsed options, their arguments and parameters* @access public*/public function process($args = array(), $shortOptions, $longOptions,$ambiguity = '', $version = 2){settype($args, 'array');in_array($ambiguity, array('loose', 'strict', 'shortcuts')) or$ambiguity = 'loose';if ($version < 2) {// preserve backwards compatibility with callers// that relied on erroneous POSIX fix// note: ported from Console/Getoptisset($args[0]) and substr($args[0], 0, 1) != '-' and array_shift($args);settype($args, 'array');}$this->args = $args;// parses the options definitions, create shorcuts or check ambiguities$this->shortOptionsDef = $this->parseShortOptionsDef($shortOptions);$this->longOptionsDef = $this->parseLongOptionsDef($longOptions);list($this->shortcuts, $this->ambigous) = $this->createShorcuts($this->longOptionsDef, $ambiguity);$this->verifyNoAmbiguity($this->longOptionsDef, $ambiguity);$this->options = array();$parameters = array();while (list($i, $arg) = each($this->args)) {if ($arg == '--') {// end of options// the remaining arguments are parameters excluding this one$parameters = array_slice($this->args, $i + 1);break;} else if ($arg == '-') {// the stdin flag// the remaining arguments are parameters including this one$parameters = array_slice($this->args, $i);break;} else if (substr($arg, 0, 2) == '--') {// a long option, e.g. --fooif ($this->longOptionsDef) {$this->parseLongOption(substr($arg, 2));} else {// not expecting long options, the remaining arguments are// parameters including this one stripped off of --$parameters = array_slice($this->args, $i);$parameters[0] = substr($parameters[0], 2);break;}} else if ($arg{0} == '-') {// a short option, e.g. -h$this->parseShortOption(substr($arg, 1));} else {// the first non option// the remaining arguments are parameters including this one$parameters = array_slice($this->args, $i);break;}}return array($this->options, $parameters);}/*** Reads the command arguments** @return array the arguments* @access public* @static*/public static function readPHPArgv(){global $argv;is_array($args = $argv) oris_array($args = $_SERVER['argv']) oris_array($args = $GLOBALS['HTTP_SERVER_VARS']['argv']) orself::exception('noargs');return $args;}/*** Verifies there is no ambiguity with option names** @param array $longOptionsDef the long options names and their types* @param string $ambiguity directive to handle option names ambiguity,* See getopt() for a complete description* @return boolean no ambiguity if true, false otherwise* @access public*/public function verifyNoAmbiguity($longOptionsDef, $ambiguity){settype($longOptionsDef, 'array');foreach($longOptionsDef as $name => $type) {foreach($longOptionsDef as $name2 => $type2) {if ($name != $name2) {if ($ambiguity == 'loose' and $type == 'noarg') {// according to Getopt.php, CVS v 1.4 2007/06/12,// _parseLongOption(), line #236, the possible// ambiguity of a long option name with another one is// ignored if this option does not expect an argument!continue;}// checks options are not ambigous, e.g. --foo --foobarstrpos($name2, $name) === false or self::exception('ambigous', $name);}// else: there is no ambiguity between an option and itself!}}return true;}}?>