Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/*** Command-line option parser - Console Getopt+ (Getopt Plus)** This package is (1) a PHP5 port/rewrite of Console_Getopt, (2) with added* functionalities, and (3) with a Web interface to run getopt-like shell* commands through a browser (not implemented yet).** (1) Console_getoptPlus:getopt() is a replacement for Console_getopt:getopt().* Same for getopt2() and readPHPArgv(). It returns PEAR_Exception instead of* PEAR_Error. Error messages are the same.** (2) GetoptPlus:getoptplus uses an array-based description of the command. It can* generates the command usage/help automaticly. It can return the parsed* options and parameters in an associative array. It can be set to accept* option shortcut names.** Fully tested with phpUnit. Code coverage test close to 100%.** Usage is fully documented in docs/examples files.** PHP version 5** 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: GetoptPlus.php 50 2008-01-11 09:01:57Z mcorne $* @link http://pear.php.net/package/Console_GetoptPlus*/require_once 'Console/GetoptPlus/Getopt.php';require_once 'Console/GetoptPlus/Help.php';/*** Parsing of a command line based on the command description in an array.* See more examples in docs/examples.** Code Example:* <code>* require_once 'Console/GetoptPlus.php';** try {* $config = array(* 'header' => array('The command xyz is used to...',* 'Note that the header and the usage are optional.'),* 'usage' => array('--foo', '--bar <arg> -c [arg]'),* 'options' => array(* array('long' => 'foo', 'type' => 'noarg', 'desc' => array(* 'An option without argument with only the long',* 'name defined.')),* array('long' => 'bar', 'type' => 'mandatory', 'short' => 'b',* 'desc' => array('arg',* 'A mandatory option with both the long and',* 'the short names defined.')),* array('short' => 'c', 'type' => 'optional',* 'desc' => array('arg',* 'An option with an optional argument with only',* 'the short name defined.'))),* 'parameters' => array('[param1] [param2]',* 'Some additional parameters.'),* 'footer' => array('Some additional information.',* 'Note that the footer is optional.'),* );** $options = Console_Getoptplus::getoptplus($config);* // some processing here...* print_r($options);* }* catch(Console_GetoptPlus_Exception $e) {* $error = array($e->getCode(), $e->getMessage());* print_r($error);* }* </code>** Run:* <pre>* #xyz --help* #xyz -h* #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* @todo create a Web interface to getopt-like shell command* @todo optionally split long usage lines to fit within 80 columns* @todo optionally change the start position of the options usage*/class Console_GetoptPlus extends Console_GetoptPlus_Getopt{/*** The long name to short option name cross-references** @var array* @access private*/private $long2short = array();/*** The short name to long option name cross-references** @var array* @access private*/private $short2long = array();/*** Verifies the option settings are valid** Adds the "help" option if missing.** @param array $config the command configuration, see the configuration* definition/example in the class Doc Block* @return array the options configurations, the updated 'options' subarray,* e.g. $config['options']* @access public*/public function checkOptionsConfig($config){$optionsConfig = empty($config['options'])? array() : $config['options'];$isHelp = false;$isH = false;foreach($optionsConfig as $idx => &$option) {// verifies there is at least a short or long option nameisset($option['long']) or isset($option['short']) orself::exception('missing', $idx);if (isset($option['long']) and isset($option['short'])) {// creates cross-references between long and short options names$this->short2long[$option['short']] = $option['long'];$this->long2short[$option['long']] = $option['short'];}if (isset($option['type'])) {// the option has a type, checks it is valid$type = $option['type'];in_array($type, array('noarg', 'mandatory', 'optional')) orself::exception('type', $type);} else {// defaults to no argument$option['type'] = 'noarg';}// determines if --help is provided$isHelp or isset($option['long']) and$option['long'] == 'help' and $isHelp = true;// determines if -h is used$isH or isset($option['short']) and$option['short'] == 'h' and $isH = true;}if (!$isHelp) {// no help option, adds the default --help, and -h if unused$help = array('long' => 'help', 'type' => 'noarg', 'desc' => 'This help.');$isH or $help['short'] = 'h';$optionsConfig[] = $help;if (isset($help['long']) and isset($help['short'])) {$this->short2long[$help['short']] = $help['long'];$this->long2short[$help['long']] = $help['short'];}}return $optionsConfig;}/*** Extracts the long or short option names and types** Validates the option name against the pattern. A short option name has* a single letter. A long option name has one of more alphanumerical* letters.** @param array $optionsConfig the options configurations, see the* configuration definition/example in the* class Doc Block, e.g. $config['options']* @param string $defType the option name type: "short" or "long"* @param string $pattern the validation pattern* @return array the option names list, e.g. array("a" => "noarg", ...)* @access public*/public function createOptionsDef($optionsConfig, $defType, $pattern){$optionsDef = array();foreach($optionsConfig as $option) {if (isset($option[$defType])) {// the option has a name$name = $option[$defType];// checks the option name syntax is valid and is not a duplicatepreg_match($pattern, $name) or self::exception('invalid', $option);isset($duplicates[$name]) and self::exception('duplicate', $name);$duplicates[$name] = true;$optionsDef[$name] = $option['type'];}}return $optionsDef;}/*** 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 configuration definition/example in the class Doc Block.** Example 1: returning an index array* <code>* array(* [0] => array(* [0] => array([0] => "--foo", [1] => null),* [1] => array([0] => "b", [1] => "car"),* [2] => array([0] => "c", [1] => null)),* [1] => array([0] => "param1", [1] => "param2")* );* </code>** Example 2: returning an associative array* <code>* array(* [0] => array("foo" => null, "bar" => "car", "c" => null),* [1] => array([0] => "param1", [1] => "param2")* );* </code>** @param array $config the command configuration, see the configuration* definition/example in the class Doc Block* @param string $convertName returns short option names if set to* "long2short", long ones if set to "short2long",* as in the command line by default* @param boolean $returnAssoc returns an associative array if true,* an index array if false* @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>* @param boolean $exitHelp if "--help" is one of the options:* <ul>* <li>true: displays the command usage and exits</li>* <li>false: returns the command usage as:* <ul>* <li>an index array, e.g.* array([0] => array([0] => array("h", "Usage:...")))</li>* <li>an associative, e.g.* array([0] => array("h" => "Usage:..."))</li>* </ul></li>* </ul>* @return array the parsed options, their arguments and parameters* @access public* @static*/public static function getoptplus($config = array(), $convertName = '',$returnAssoc = false, $ambiguity = '', $exitHelp = true){$getopt = new self;return $getopt->process($config, $convertName, $returnAssoc,$ambiguity, $exitHelp);}/*** Parses the long option names and types** Verifies the option names have one of more alphanumerical characters.** @param array $optionsConfig the options configurations, see the* configuration definition/example in the* class Doc Block, e.g. $config['options']* @return array the option names list, e.g. array("foo" => "noarg", ...)* @access public*/public function parseLongOptionsDef($optionsConfig){return $this->createOptionsDef($optionsConfig, 'long', '~^\w+$~');}/*** Parses the short option names and types** Verifies the option names have one alphanumerical character.** @param array $optionsConfig the options configurations, see the* configuration definition/example in the* class Doc Block, e.g. $config['options']* @return array the option names list, e.g. array("f" => "noarg", ...)* @access public*/public function parseShortOptionsDef($optionsConfig){return $this->createOptionsDef($optionsConfig, 'short', '~^\w$~');}/*** Parses the command line** See getoptplus() for a complete description.** @param array $config the command configuration* @param string $convertName returns short option names if set to* "long2short", long ones if set to "short2long",* as in the command line by default* @param boolean $returnAssoc returns an associative array if true,* an index array if false* @param string $ambiguity directive to handle option names ambiguity:* "loose", "strict", or "shortcuts"* @param boolean $exitHelp same as getoptplus()* @return array the parsed options, their arguments and parameters* @access public*/public function process($config = array(), $convertName = '',$returnAssoc = false, $ambiguity = '', $exitHelp = true){// extracts the command arguments, including the command name$args = self::readPHPArgv();$command = array_shift($args);// checks the options configurations, parses the command$config['options'] = $optionsConfig = $this->checkOptionsConfig($config);$options = parent::process($args, $optionsConfig, $optionsConfig, $ambiguity);// tidies the options$options[0] = $this->tidyOptions($options[0], $convertName, $returnAssoc);if (is_string($options[0])) {// a request for help, builds the command usage,$help = Console_GetoptPlus_Help::get($config, $command);// exits/displays the command usage or returns it$exitHelp and exit($help);$name = $options[0];$options[0] = $returnAssoc? array($name => $help) : array(array($name, $help));}return $options;}/*** Tidies the command arguments** See getoptplus() for a complete description of the returned options.** @param array $options the parsed options arguments, e.g.* <pre>* array(* [0] => array([0] => "--foo", [1] => null),* [1] => array([0] => "b", [1] => "car"),* [2] => array([0] => "c", [1] => null))* >/pre>* @param string $convertName returns short option names if set to* "long2short", long ones if set to "short2long",* as in the command line by default* @param boolean $returnAssoc returns an associative array if true,* an index array if false* @return array the tidied options arguments* @access public*/public function tidyOptions($options, $convertName = '', $returnAssoc = false){// verifies the conversion is validempty($convertName) or $convertName == 'long2short' or$convertName == 'short2long' or self::exception('convert', $convertName);$tidied = array();foreach($options as $option) {// extracs the option name and value, removes the long option prefixlist($name, $value) = $option;$isLong = substr($name, 0, 2) == '--' and $name = substr($name, 2);if ($isLong) {// a long optionif ($name == 'help') {// the help optionif ($convertName == 'long2short' and isset($this->long2short['help'])) {return $this->long2short['help'];} else {return '--help';}}// converts to a short option if requested and possibleif ($convertName == 'long2short' and isset($this->long2short[$name])) {$name = $this->long2short[$name];$isLong = false;}} else {// a short optionif ($convertName == 'short2long') {// converts to a long one if possibleif (isset($this->short2long[$name])) {$name = $this->short2long[$name];$isLong = true;}if ($isLong and $name == 'help') {// the help optionreturn '--help';}} else if (isset($this->short2long[$name]) and$this->short2long[$name] == 'help') {// the help optionreturn $name;}}if ($returnAssoc) {// converts the arguments to an associative array with// the argument name as the key, converts a NULL values to an// empty string to make it easier to use with isset()$tidied[$name] = is_null($value)? '' : $value;} else {// leaves the arguments as per per Console_Getopt::doGetopt()// format and prefixes long options with --$isLong and $name = '--' . $name;$tidied[] = array($name, $value);}}return $tidied;}}?>