Console_CommandLine
[ class tree: Console_CommandLine ] [ index: Console_CommandLine ] [ all elements ]

Source for file CommandLine.php

Documentation is available at CommandLine.php

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * This file is part of the PEAR Console_CommandLine package.
  7.  *
  8.  * A full featured package for managing command-line options and arguments
  9.  * hightly inspired from python optparse module, it allows the developper to
  10.  * easily build complex command line interfaces.
  11.  *
  12.  * PHP version 5
  13.  *
  14.  * LICENSE: This source file is subject to the MIT license that is available
  15.  * through the world-wide-web at the following URI:
  16.  * http://opensource.org/licenses/mit-license.php
  17.  *
  18.  * @category  Console
  19.  * @package   Console_CommandLine
  20.  * @author    David JEAN LOUIS <izimobil@gmail.com>
  21.  * @copyright 2007 David JEAN LOUIS
  22.  * @license   http://opensource.org/licenses/mit-license.php MIT License
  23.  * @version   CVS: $Id: CommandLine.php,v 1.5 2009/06/19 10:22:48 izi Exp $
  24.  * @link      http://pear.php.net/package/Console_CommandLine
  25.  * @since     Class available since release 0.1.0
  26.  * @filesource
  27.  */
  28.  
  29. /**
  30.  * Required unconditionally
  31.  */
  32. require_once 'Console/CommandLine/Exception.php';
  33. require_once 'Console/CommandLine/Outputter/Default.php';
  34. require_once 'Console/CommandLine/Renderer/Default.php';
  35. require_once 'Console/CommandLine/MessageProvider/Default.php';
  36.  
  37. /**
  38.  * Main class for parsing command line options and arguments.
  39.  * 
  40.  * There are three ways to create parsers with this class:
  41.  * <code>
  42.  * // direct usage
  43.  * $parser = new Console_CommandLine();
  44.  *
  45.  * // with an xml definition file
  46.  * $parser = Console_CommandLine::fromXmlFile('path/to/file.xml');
  47.  *
  48.  * // with an xml definition string
  49.  * $validXmlString = '..your xml string...';
  50.  * $parser = Console_CommandLine::fromXmlString($validXmlString);
  51.  * </code>
  52.  *
  53.  * @category  Console
  54.  * @package   Console_CommandLine
  55.  * @author    David JEAN LOUIS <izimobil@gmail.com>
  56.  * @copyright 2007 David JEAN LOUIS
  57.  * @license   http://opensource.org/licenses/mit-license.php MIT License
  58.  * @version   Release: 1.1.0
  59.  * @link      http://pear.php.net/package/Console_CommandLine
  60.  * @since     File available since release 0.1.0
  61.  * @example   docs/examples/ex1.php
  62.  * @example   docs/examples/ex2.php
  63.  */
  64. {
  65.     // Public properties {{{
  66.  
  67.     /**
  68.      * Error messages.
  69.      *
  70.      * @var array $errors Error messages
  71.      * @todo move this to Console_CommandLine_MessageProvider
  72.      */
  73.     public static $errors = array(
  74.         'option_bad_name'                    => 'option name must be a valid php variable name (got: {$name})',
  75.         'argument_bad_name'                  => 'argument name must be a valid php variable name (got: {$name})',
  76.         'option_long_and_short_name_missing' => 'you must provide at least an option short name or long name for option "{$name}"',
  77.         'option_bad_short_name'              => 'option "{$name}" short name must be a dash followed by a letter (got: "{$short_name}")',
  78.         'option_bad_long_name'               => 'option "{$name}" long name must be 2 dashes followed by a word (got: "{$long_name}")',
  79.         'option_unregistered_action'         => 'unregistered action "{$action}" for option "{$name}".',
  80.         'option_bad_action'                  => 'invalid action for option "{$name}".',
  81.         'option_invalid_callback'            => 'you must provide a valid callback for option "{$name}"',
  82.         'action_class_does_not_exists'       => 'action "{$name}" class "{$class}" not found, make sure that your class is available before calling Console_CommandLine::registerAction()',
  83.         'invalid_xml_file'                   => 'XML definition file "{$file}" does not exists or is not readable',
  84.         'invalid_rng_file'                   => 'RNG file "{$file}" does not exists or is not readable'
  85.     );
  86.  
  87.     /**
  88.      * The name of the program, if not given it defaults to argv[0].
  89.      *
  90.      * @var string $name Name of your program
  91.      */
  92.     public $name;
  93.  
  94.     /**
  95.      * A description text that will be displayed in the help message.
  96.      *
  97.      * @var string $description Description of your program
  98.      */
  99.     public $description = '';
  100.  
  101.     /**
  102.      * A string that represents the version of the program, if this property is
  103.      * not empty and property add_version_option is not set to false, the
  104.      * command line parser will add a --version option, that will display the
  105.      * property content.
  106.      *
  107.      * @var    string $version 
  108.      * @access public
  109.      */
  110.     public $version = '';
  111.  
  112.     /**
  113.      * Boolean that determine if the command line parser should add the help
  114.      * (-h, --help) option automatically.
  115.      *
  116.      * @var bool $add_help_option Whether to add a help option or not
  117.      */
  118.     public $add_help_option = true;
  119.  
  120.     /**
  121.      * Boolean that determine if the command line parser should add the version
  122.      * (-v, --version) option automatically.
  123.      * Note that the version option is also generated only if the version
  124.      * property is not empty, it's up to you to provide a version string of
  125.      * course.
  126.      *
  127.      * @var bool $add_version_option Whether to add a version option or not
  128.      */
  129.     public $add_version_option = true;
  130.  
  131.     /**
  132.      * The command line parser renderer instance.
  133.      *
  134.      * @var    object that implements Console_CommandLine_Renderer interface
  135.      * @access protected
  136.      */
  137.     public $renderer = false;
  138.  
  139.     /**
  140.      * The command line parser outputter instance.
  141.      *
  142.      * @var Console_CommandLine_Outputter An outputter
  143.      */
  144.     public $outputter = false;
  145.  
  146.     /**
  147.      * The command line message provider instance.
  148.      *
  149.      * @var Console_CommandLine_MessageProvider A message provider instance
  150.      */
  151.     public $message_provider = false;
  152.  
  153.     /**
  154.      * Boolean that tells the parser to be POSIX compliant, POSIX demands the
  155.      * following behavior: the first non-option stops option processing.
  156.      *
  157.      * @var bool $force_posix Whether to force posix compliance or not
  158.      */
  159.     public $force_posix = false;
  160.  
  161.     /**
  162.      * Boolean that tells the parser to set relevant options default values,
  163.      * according to the option action.
  164.      *
  165.      * @see Console_CommandLine_Option::setDefaults()
  166.      * @var bool $force_options_defaults Whether to force option default values
  167.      */
  168.     public $force_options_defaults = false;
  169.  
  170.     /**
  171.      * An array of Console_CommandLine_Option objects.
  172.      *
  173.      * @var array $options The options array
  174.      */
  175.     public $options = array();
  176.  
  177.     /**
  178.      * An array of Console_CommandLine_Argument objects.
  179.      *
  180.      * @var array $args The arguments array
  181.      */
  182.     public $args = array();
  183.  
  184.     /**
  185.      * An array of Console_CommandLine_Command objects (sub commands).
  186.      *
  187.      * @var array $commands The commands array
  188.      */
  189.     public $commands = array();
  190.  
  191.     /**
  192.      * Parent, only relevant in Command objects but left here for interface
  193.      * convenience.
  194.      *
  195.      * @var Console_CommandLine The parent instance
  196.      * @todo move Console_CommandLine::parent to Console_CommandLine_Command
  197.      */
  198.     public $parent = false;
  199.  
  200.     /**
  201.      * Array of valid actions for an option, this array will also store user
  202.      * registered actions.
  203.      *
  204.      * The array format is:
  205.      * <pre>
  206.      * array(
  207.      *     <ActionName:string> => array(<ActionClass:string>, <builtin:bool>)
  208.      * )
  209.      * </pre>
  210.      *
  211.      * @var array $actions List of valid actions
  212.      */
  213.     public static $actions = array(
  214.         'StoreTrue'   => array('Console_CommandLine_Action_StoreTrue'true),
  215.         'StoreFalse'  => array('Console_CommandLine_Action_StoreFalse'true),
  216.         'StoreString' => array('Console_CommandLine_Action_StoreString'true),
  217.         'StoreInt'    => array('Console_CommandLine_Action_StoreInt'true),
  218.         'StoreFloat'  => array('Console_CommandLine_Action_StoreFloat'true),
  219.         'StoreArray'  => array('Console_CommandLine_Action_StoreArray'true),
  220.         'Callback'    => array('Console_CommandLine_Action_Callback'true),
  221.         'Counter'     => array('Console_CommandLine_Action_Counter'true),
  222.         'Help'        => array('Console_CommandLine_Action_Help'true),
  223.         'Version'     => array('Console_CommandLine_Action_Version'true),
  224.         'Password'    => array('Console_CommandLine_Action_Password'true),
  225.         'List'        => array('Console_CommandLine_Action_List'true),
  226.     );
  227.  
  228.     /**
  229.      * Custom errors messages for this command
  230.      *
  231.      * This array is of the form:
  232.      * <code>
  233.      * <?php
  234.      * array(
  235.      *     $messageName => $messageText,
  236.      *     $messageName => $messageText,
  237.      *     ...
  238.      * );
  239.      * ?>
  240.      * </code>
  241.      *
  242.      * If specified, these messages override the messages provided by the
  243.      * default message provider. For example:
  244.      * <code>
  245.      * <?php
  246.      * $messages = array(
  247.      *     'ARGUMENT_REQUIRED' => 'The argument foo is required.',
  248.      * );
  249.      * ?>
  250.      * </code>
  251.      *
  252.      * @var array 
  253.      * @see Console_CommandLine_MessageProvider_Default
  254.      */
  255.     public $messages = array();
  256.  
  257.     // }}}
  258.     // {{{ Private properties
  259.  
  260.     /**
  261.      * Array of options that must be dispatched at the end.
  262.      *
  263.      * @var array $_dispatchLater Options to be dispatched
  264.      */
  265.     private $_dispatchLater = array();
  266.  
  267.     // }}}
  268.     // __construct() {{{
  269.  
  270.     /**
  271.      * Constructor.
  272.      * Example:
  273.      *
  274.      * <code>
  275.      * $parser = new Console_CommandLine(array(
  276.      *     'name'               => 'yourprogram', // defaults to argv[0]
  277.      *     'description'        => 'Description of your program',
  278.      *     'version'            => '0.0.1', // your program version
  279.      *     'add_help_option'    => true, // or false to disable --version option
  280.      *     'add_version_option' => true, // or false to disable --help option
  281.      *     'force_posix'        => false // or true to force posix compliance
  282.      * ));
  283.      * </code>
  284.      *
  285.      * @param array $params An optional array of parameters
  286.      *
  287.      * @return void 
  288.      */
  289.     public function __construct(array $params = array()) 
  290.     {
  291.         if (isset($params['name'])) {
  292.             $this->name = $params['name'];
  293.         else if (isset($argv&& count($argv> 0{
  294.             $this->name = $argv[0];
  295.         else if (isset($_SERVER['argv']&& count($_SERVER['argv']> 0{
  296.             $this->name = $_SERVER['argv'][0];
  297.         else if (isset($_SERVER['SCRIPT_NAME'])) {
  298.             $this->name = basename($_SERVER['SCRIPT_NAME']);
  299.         }
  300.         if (isset($params['description'])) {
  301.             $this->description = $params['description'];
  302.         }
  303.         if (isset($params['version'])) {
  304.             $this->version = $params['version'];
  305.         }
  306.         if (isset($params['add_version_option'])) {
  307.             $this->add_version_option = $params['add_version_option'];
  308.         }
  309.         if (isset($params['add_help_option'])) {
  310.             $this->add_help_option = $params['add_help_option'];
  311.         }
  312.         if (isset($params['force_posix'])) {
  313.             $this->force_posix = $params['force_posix'];
  314.         else if (getenv('POSIXLY_CORRECT')) {
  315.             $this->force_posix = true;
  316.         }
  317.         if (isset($params['messages']&& is_array($params['messages'])) {
  318.             $this->messages = $params['messages'];
  319.         }
  320.         // set default instances
  321.         $this->renderer         = new Console_CommandLine_Renderer_Default($this);
  322.         $this->outputter        = new Console_CommandLine_Outputter_Default();
  323.     }
  324.  
  325.     // }}}
  326.     // accept() {{{
  327.  
  328.     /**
  329.      * Method to allow Console_CommandLine to accept either:
  330.      *  + a custom renderer,
  331.      *  + a custom outputter,
  332.      *  + or a custom message provider
  333.      *
  334.      * @param mixed $instance The custom instance
  335.      *
  336.      * @return void 
  337.      * @throws Console_CommandLine_Exception if wrong argument passed
  338.      */
  339.     public function accept($instance
  340.     {
  341.         if ($instance instanceof Console_CommandLine_Renderer{
  342.             if (property_exists($instance'parser'&& !$instance->parser{
  343.                 $instance->parser = $this;
  344.             }
  345.             $this->renderer = $instance;
  346.         else if ($instance instanceof Console_CommandLine_Outputter{
  347.             $this->outputter = $instance;
  348.         else if ($instance instanceof Console_CommandLine_MessageProvider{
  349.             $this->message_provider = $instance;
  350.         else {
  351.             throw Console_CommandLine_Exception::factory(
  352.                 'INVALID_CUSTOM_INSTANCE',
  353.                 array(),
  354.                 $this,
  355.                 $this->messages
  356.             );
  357.         }
  358.     }
  359.  
  360.     // }}}
  361.     // fromXmlFile() {{{
  362.  
  363.     /**
  364.      * Returns a command line parser instance built from an xml file.
  365.      *
  366.      * Example:
  367.      * <code>
  368.      * require_once 'Console/CommandLine.php';
  369.      * $parser = Console_CommandLine::fromXmlFile('path/to/file.xml');
  370.      * $result = $parser->parse();
  371.      * </code>
  372.      *
  373.      * @param string $file Path to the xml file
  374.      *
  375.      * @return Console_CommandLine The parser instance
  376.      */
  377.     public static function fromXmlFile($file
  378.     {
  379.         include_once 'Console/CommandLine/XmlParser.php';
  380.         return Console_CommandLine_XmlParser::parse($file);
  381.     }
  382.  
  383.     // }}}
  384.     // fromXmlString() {{{
  385.  
  386.     /**
  387.      * Returns a command line parser instance built from an xml string.
  388.      *
  389.      * Example:
  390.      * <code>
  391.      * require_once 'Console/CommandLine.php';
  392.      * $xmldata = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
  393.      * <command>
  394.      *   <description>Compress files</description>
  395.      *   <option name="quiet">
  396.      *     <short_name>-q</short_name>
  397.      *     <long_name>--quiet</long_name>
  398.      *     <description>be quiet when run</description>
  399.      *     <action>StoreTrue/action>
  400.      *   </option>
  401.      *   <argument name="files">
  402.      *     <description>a list of files</description>
  403.      *     <multiple>true</multiple>
  404.      *   </argument>
  405.      * </command>';
  406.      * $parser = Console_CommandLine::fromXmlString($xmldata);
  407.      * $result = $parser->parse();
  408.      * </code>
  409.      *
  410.      * @param string $string The xml data
  411.      *
  412.      * @return Console_CommandLine The parser instance
  413.      */
  414.     public static function fromXmlString($string
  415.     {
  416.         include_once 'Console/CommandLine/XmlParser.php';
  417.         return Console_CommandLine_XmlParser::parseString($string);
  418.     }
  419.  
  420.     // }}}
  421.     // addArgument() {{{
  422.  
  423.     /**
  424.      * Adds an argument to the command line parser and returns it.
  425.      *
  426.      * Adds an argument with the name $name and set its attributes with the
  427.      * array $params, then return the Console_CommandLine_Argument instance
  428.      * created.
  429.      * The method accepts another form: you can directly pass a
  430.      * Console_CommandLine_Argument object as the sole argument, this allows
  431.      * you to contruct the argument separately, in order to reuse it in
  432.      * different command line parsers or commands for example.
  433.      *
  434.      * Example:
  435.      * <code>
  436.      * $parser = new Console_CommandLine();
  437.      * // add an array argument
  438.      * $parser->addArgument('input_files', array('multiple'=>true));
  439.      * // add a simple argument
  440.      * $parser->addArgument('output_file');
  441.      * $result = $parser->parse();
  442.      * print_r($result->args['input_files']);
  443.      * print_r($result->args['output_file']);
  444.      * // will print:
  445.      * // array('file1', 'file2')
  446.      * // 'file3'
  447.      * // if the command line was:
  448.      * // myscript.php file1 file2 file3
  449.      * </code>
  450.      *
  451.      * In a terminal, the help will be displayed like this:
  452.      * <code>
  453.      * $ myscript.php install -h
  454.      * Usage: myscript.php <input_files...> <output_file>
  455.      * </code>
  456.      *
  457.      * @param mixed $name   A string containing the argument name or an
  458.      *                       instance of Console_CommandLine_Argument
  459.      * @param array $params An array containing the argument attributes
  460.      *
  461.      * @return Console_CommandLine_Argument the added argument
  462.      * @see Console_CommandLine_Argument
  463.      */
  464.     public function addArgument($name$params = array())
  465.     {
  466.         if ($name instanceof Console_CommandLine_Argument{
  467.             $argument $name;
  468.         else {
  469.             include_once 'Console/CommandLine/Argument.php';
  470.             $argument = new Console_CommandLine_Argument($name$params);
  471.         }
  472.         $argument->validate();
  473.         $this->args[$argument->name$argument;
  474.         return $argument;
  475.     }
  476.  
  477.     // }}}
  478.     // addCommand() {{{
  479.  
  480.     /**
  481.      * Adds a sub-command to the command line parser.
  482.      *
  483.      * Adds a command with the given $name to the parser and returns the
  484.      * Console_CommandLine_Command instance, you can then populate the command
  485.      * with options, configure it, etc... like you would do for the main parser
  486.      * because the class Console_CommandLine_Command inherits from
  487.      * Console_CommandLine.
  488.      *
  489.      * An example:
  490.      * <code>
  491.      * $parser = new Console_CommandLine();
  492.      * $install_cmd = $parser->addCommand('install');
  493.      * $install_cmd->addOption(
  494.      *     'verbose',
  495.      *     array(
  496.      *         'short_name'  => '-v',
  497.      *         'long_name'   => '--verbose',
  498.      *         'description' => 'be noisy when installing stuff',
  499.      *         'action'      => 'StoreTrue'
  500.      *      )
  501.      * );
  502.      * $parser->parse();
  503.      * </code>
  504.      * Then in a terminal:
  505.      * <code>
  506.      * $ myscript.php install -h
  507.      * Usage: myscript.php install [options]
  508.      *
  509.      * Options:
  510.      *   -h, --help     display this help message and exit
  511.      *   -v, --verbose  be noisy when installing stuff
  512.      *
  513.      * $ myscript.php install --verbose
  514.      * Installing whatever...
  515.      * $
  516.      * </code>
  517.      *
  518.      * @param mixed $name   A string containing the command name or an
  519.      *                       instance of Console_CommandLine_Command
  520.      * @param array $params An array containing the command attributes
  521.      *
  522.      * @return Console_CommandLine_Command the added subcommand
  523.      * @see    Console_CommandLine_Command
  524.      */
  525.     public function addCommand($name$params = array())
  526.     {
  527.         if ($name instanceof Console_CommandLine_Command{
  528.             $command $name;
  529.         else {
  530.             include_once 'Console/CommandLine/Command.php';
  531.             $params['name'$name;
  532.             $command        = new Console_CommandLine_Command($params);
  533.         }
  534.         $command->parent                = $this;
  535.         $this->commands[$command->name$command;
  536.         return $command;
  537.     }
  538.  
  539.     // }}}
  540.     // addOption() {{{
  541.  
  542.     /**
  543.      * Adds an option to the command line parser and returns it.
  544.      *
  545.      * Adds an option with the name $name and set its attributes with the
  546.      * array $params, then return the Console_CommandLine_Option instance
  547.      * created.
  548.      * The method accepts another form: you can directly pass a
  549.      * Console_CommandLine_Option object as the sole argument, this allows
  550.      * you to contruct the option separately, in order to reuse it in different
  551.      * command line parsers or commands for example.
  552.      *
  553.      * Example:
  554.      * <code>
  555.      * $parser = new Console_CommandLine();
  556.      * $parser->addOption('path', array(
  557.      *     'short_name'  => '-p',  // a short name
  558.      *     'long_name'   => '--path', // a long name
  559.      *     'description' => 'path to the dir', // a description msg
  560.      *     'action'      => 'StoreString',
  561.      *     'default'     => '/tmp' // a default value
  562.      * ));
  563.      * $parser->parse();
  564.      * </code>
  565.      *
  566.      * In a terminal, the help will be displayed like this:
  567.      * <code>
  568.      * $ myscript.php --help
  569.      * Usage: myscript.php [options]
  570.      *
  571.      * Options:
  572.      *   -h, --help  display this help message and exit
  573.      *   -p, --path  path to the dir
  574.      *
  575.      * </code>
  576.      *
  577.      * Various methods to specify an option, these 3 commands are equivalent:
  578.      * <code>
  579.      * $ myscript.php --path=some/path
  580.      * $ myscript.php -p some/path
  581.      * $ myscript.php -psome/path
  582.      * </code>
  583.      *
  584.      * @param mixed $name   A string containing the option name or an
  585.      *                       instance of Console_CommandLine_Option
  586.      * @param array $params An array containing the option attributes
  587.      *
  588.      * @return Console_CommandLine_Option The added option
  589.      * @see    Console_CommandLine_Option
  590.      */
  591.     public function addOption($name$params = array())
  592.     {
  593.         include_once 'Console/CommandLine/Option.php';
  594.         if ($name instanceof Console_CommandLine_Option{
  595.             $opt $name;
  596.         else {
  597.             $opt = new Console_CommandLine_Option($name$params);
  598.         }
  599.         $opt->validate();
  600.         if ($this->force_options_defaults{
  601.             $opt->setDefaults();
  602.         }
  603.         $this->options[$opt->name$opt;
  604.         if (!empty($opt->choices&& $opt->add_list_option{
  605.             $this->addOption('list_' $opt->namearray(
  606.                 'long_name'     => '--list-' $opt->name,
  607.                 'description'   => $this->message_provider->get(
  608.                     'LIST_OPTION_MESSAGE',
  609.                     array('name' => $opt->name)
  610.                 ),
  611.                 'action'        => 'List',
  612.                 'action_params' => array('list' => $opt->choices),
  613.             ));
  614.         }
  615.         return $opt;
  616.     }
  617.  
  618.     // }}}
  619.     // displayError() {{{
  620.  
  621.     /**
  622.      * Displays an error to the user and exit with $exitCode.
  623.      *
  624.      * @param string $error    The error message
  625.      * @param int    $exitCode The exit code number
  626.      *
  627.      * @return void 
  628.      */
  629.     public function displayError($error$exitCode = 1)
  630.     {
  631.         $this->outputter->stderr($this->renderer->error($error));
  632.         exit($exitCode);
  633.     }
  634.  
  635.     // }}}
  636.     // displayUsage() {{{
  637.  
  638.     /**
  639.      * Displays the usage help message to the user and exit with $exitCode
  640.      *
  641.      * @param int $exitCode The exit code number
  642.      *
  643.      * @return void 
  644.      */
  645.     public function displayUsage($exitCode = 1)
  646.     {
  647.         $this->outputter->stderr($this->renderer->usage());
  648.         exit($exitCode);
  649.     }
  650.  
  651.     // }}}
  652.     // displayVersion() {{{
  653.  
  654.     /**
  655.      * Displays the program version to the user
  656.      *
  657.      * @return void 
  658.      */
  659.     public function displayVersion()
  660.     {
  661.         $this->outputter->stdout($this->renderer->version());
  662.         exit(0);
  663.     }
  664.  
  665.     // }}}
  666.     // findOption() {{{
  667.  
  668.     /**
  669.      * Finds the option that matches the given short_name (ex: -v), long_name
  670.      * (ex: --verbose) or name (ex: verbose).
  671.      *
  672.      * @param string $str The option identifier
  673.      *
  674.      * @return mixed A Console_CommandLine_Option instance or false
  675.      */
  676.     public function findOption($str)
  677.     {
  678.         $str trim($str);
  679.         if ($str === ''{
  680.             return false;
  681.         }
  682.         $matches = array();
  683.         foreach ($this->options as $opt{
  684.             if ($opt->short_name == $str || $opt->long_name == $str ||
  685.                 $opt->name == $str{
  686.                 // exact match
  687.                 return $opt;
  688.             }
  689.             if (substr($opt->long_name0strlen($str)) === $str{
  690.                 // abbreviated long option
  691.                 $matches[$opt;
  692.             }
  693.         }
  694.         if ($count count($matches)) {
  695.             if ($count > 1{
  696.                 $matches_str '';
  697.                 $padding     '';
  698.                 foreach ($matches as $opt{
  699.                     $matches_str .= $padding $opt->long_name;
  700.                     $padding      ', ';
  701.                 }
  702.                 throw Console_CommandLine_Exception::factory(
  703.                     'OPTION_AMBIGUOUS',
  704.                     array('name' => $str'matches' => $matches_str),
  705.                     $this,
  706.                     $this->messages
  707.                 );
  708.             }
  709.             return $matches[0];
  710.         }
  711.         return false;
  712.     }
  713.     // }}}
  714.     // registerAction() {{{
  715.  
  716.     /**
  717.      * Registers a custom action for the parser, an example:
  718.      *
  719.      * <code>
  720.      *
  721.      * // in this example we create a "range" action:
  722.      * // the user will be able to enter something like:
  723.      * // $ <program> -r 1,5
  724.      * // and in the result we will have:
  725.      * // $result->options['range']: array(1, 5)
  726.      *
  727.      * require_once 'Console/CommandLine.php';
  728.      * require_once 'Console/CommandLine/Action.php';
  729.      *
  730.      * class ActionRange extends Console_CommandLine_Action
  731.      * {
  732.      *     public function execute($value=false, $params=array())
  733.      *     {
  734.      *         $range = explode(',', str_replace(' ', '', $value));
  735.      *         if (count($range) != 2) {
  736.      *             throw new Exception(sprintf(
  737.      *                 'Option "%s" must be 2 integers separated by a comma',
  738.      *                 $this->option->name
  739.      *             ));
  740.      *         }
  741.      *         $this->setResult($range);
  742.      *     }
  743.      * }
  744.      * // then we can register our action
  745.      * Console_CommandLine::registerAction('Range', 'ActionRange');
  746.      * // and now our action is available !
  747.      * $parser = new Console_CommandLine();
  748.      * $parser->addOption('range', array(
  749.      *     'short_name'  => '-r',
  750.      *     'long_name'   => '--range',
  751.      *     'action'      => 'Range', // note our custom action
  752.      *     'description' => 'A range of two integers separated by a comma'
  753.      * ));
  754.      * // etc...
  755.      *
  756.      * </code>
  757.      *
  758.      * @param string $name  The name of the custom action
  759.      * @param string $class The class name of the custom action
  760.      *
  761.      * @return void 
  762.      */
  763.     public static function registerAction($name$class
  764.     {
  765.         if (!isset(self::$actions[$name])) {
  766.             if (!class_exists($class)) {
  767.                 self::triggerError('action_class_does_not_exists',
  768.                     E_USER_ERROR,
  769.                     array('{$name}' => $name'{$class}' => $class));
  770.             }
  771.             self::$actions[$name= array($classfalse);
  772.         }
  773.     }
  774.  
  775.     // }}}
  776.     // triggerError() {{{
  777.  
  778.     /**
  779.      * A wrapper for programming errors triggering.
  780.      *
  781.      * @param string $msgId  Identifier of the message
  782.      * @param int    $level  The php error level
  783.      * @param array  $params An array of search=>replaces entries
  784.      *
  785.      * @return void 
  786.      * @todo remove Console::triggerError() and use exceptions only
  787.      */
  788.     public static function triggerError($msgId$level$params=array()) 
  789.     {
  790.         if (isset(self::$errors[$msgId])) {
  791.             $msg = str_replace(array_keys($params),
  792.                 array_values($params)self::$errors[$msgId])
  793.             trigger_error($msg$level);
  794.         else {
  795.             trigger_error('unknown error'$level);
  796.         }
  797.     }
  798.  
  799.     // }}}
  800.     // parse() {{{
  801.  
  802.     /**
  803.      * Parses the command line arguments and returns a
  804.      * Console_CommandLine_Result instance.
  805.      *
  806.      * @param integer $userArgc Number of arguments (optional)
  807.      * @param array   $userArgv Array containing arguments (optional)
  808.      *
  809.      * @return Console_CommandLine_Result The result instance
  810.      * @throws Exception on user errors
  811.      */
  812.     public function parse($userArgc=null$userArgv=null)
  813.     {
  814.         $this->addBuiltinOptions();
  815.         if ($userArgc !== null && $userArgv !== null{
  816.             $argc $userArgc;
  817.             $argv $userArgv;
  818.         else {
  819.             list($argc$argv$this->getArgcArgv();
  820.         }
  821.         // build an empty result
  822.         include_once 'Console/CommandLine/Result.php';
  823.         $result = new Console_CommandLine_Result();
  824.         if (!($this instanceof Console_CommandLine_Command)) {
  825.             // remove script name if we're not in a subcommand
  826.             array_shift($argv);
  827.             $argc--;
  828.         }
  829.         // will contain aruments
  830.         $args = array();
  831.         foreach ($this->options as $name=>$option{
  832.             $result->options[$name$option->default;
  833.         }
  834.         // parse command line tokens
  835.         while ($argc--{
  836.             $token array_shift($argv);
  837.             try {
  838.                 if ($cmd $this->_getSubCommand($token)) {
  839.                     $result->command_name = $cmd->name;
  840.                     $result->command      = $cmd->parse($argc$argv);
  841.                     break;
  842.                 else {
  843.                     $this->parseToken($token$result$args$argc);
  844.                 }
  845.             catch (Exception $exc{
  846.                 throw $exc;
  847.             }
  848.         }
  849.         // Check if an invalid subcommand was specified. If there are
  850.         // subcommands and no arguments, but an argument was provided, it is
  851.         // an invalid subcommand.
  852.         if (   count($this->commands> 0
  853.             && count($this->args=== 0
  854.             && count($args> 0
  855.         {
  856.             throw Console_CommandLine_Exception::factory(
  857.                 'INVALID_SUBCOMMAND',
  858.                 array('command' => $args[0]),
  859.                 $this,
  860.                 $this->messages
  861.             );
  862.         }
  863.         // minimum argument number check
  864.         $argnum = 0;
  865.         foreach ($this->args as $name=>$arg{
  866.             if (!$arg->optional{
  867.                 $argnum++;
  868.             }
  869.         }
  870.         if (count($args$argnum{
  871.             throw Console_CommandLine_Exception::factory(
  872.                 'ARGUMENT_REQUIRED',
  873.                 array('argnum' => $argnum'plural' => $argnum>1 ? 's'''),
  874.                 $this,
  875.                 $this->messages
  876.             );
  877.         }
  878.         // handle arguments
  879.         $c count($this->args);
  880.         foreach ($this->args as $name=>$arg{
  881.             $c--;
  882.             if ($arg->multiple{
  883.                 $result->args[$name$c array_splice($args0-$c$args;
  884.             else {
  885.                 $result->args[$namearray_shift($args);
  886.             }
  887.         }
  888.         // dispatch deferred options
  889.         foreach ($this->_dispatchLater as $optArray{
  890.             $optArray[0]->dispatchAction($optArray[1]$optArray[2]$this);
  891.         }
  892.         return $result;
  893.     }
  894.  
  895.     // }}}
  896.     // parseToken() {{{
  897.  
  898.     /**
  899.      * Parses the command line token and modifies *by reference* the $options
  900.      * and $args arrays.
  901.      *
  902.      * @param string $token  The command line token to parse
  903.      * @param object $result The Console_CommandLine_Result instance
  904.      * @param array  &$args  The argv array
  905.      * @param int    $argc   Number of lasting args
  906.      *
  907.      * @return void 
  908.      * @access protected
  909.      * @throws Exception on user errors
  910.      */
  911.     protected function parseToken($token$result&$args$argc)
  912.     {
  913.         static $lastopt  = false;
  914.         static $stopflag = false;
  915.         $last  $argc === 0;
  916.         $token = trim($token);
  917.         if (!$stopflag && $lastopt{
  918.             if (substr($token01== '-'{
  919.                 if ($lastopt->argument_optional{
  920.                     $this->_dispatchAction($lastopt''$result);
  921.                     if ($lastopt->action != 'StoreArray'{
  922.                         $lastopt = false;
  923.                     }
  924.                 else if (isset($result->options[$lastopt->name])) {
  925.                     // case of an option that expect a list of args
  926.                     $lastopt = false;
  927.                 else {
  928.                     throw Console_CommandLine_Exception::factory(
  929.                         'OPTION_VALUE_REQUIRED',
  930.                         array('name' => $lastopt->name),
  931.                         $this,
  932.                         $this->messages
  933.                     );
  934.                 }
  935.             else {
  936.                 // when a StoreArray option is positioned last, the behavior
  937.                 // is to consider that if there's already an element in the
  938.                 // array, and the commandline expects one or more args, we
  939.                 // leave last tokens to arguments
  940.                 if ($lastopt->action == 'StoreArray' && 
  941.                     !empty($result->options[$lastopt->name]&&
  942.                     count($this->args($argc count($args))) {
  943.                     $args[$token;
  944.                     return;
  945.                 }
  946.                 $this->_dispatchAction($lastopt$token$result);
  947.                 if ($lastopt->action != 'StoreArray'{
  948.                     $lastopt = false;
  949.                 }
  950.                 return;
  951.             }
  952.         }
  953.         if (!$stopflag && substr($token02== '--'{
  954.             // a long option
  955.             $optkv explode('='$token2);
  956.             if (trim($optkv[0]== '--'{
  957.                 // the special argument "--" forces in all cases the end of 
  958.                 // option scanning.
  959.                 $stopflag = true;
  960.                 return;
  961.             }
  962.             $opt $this->findOption($optkv[0]);
  963.             if (!$opt{
  964.                 throw Console_CommandLine_Exception::factory(
  965.                     'OPTION_UNKNOWN',
  966.                     array('name' => $optkv[0]),
  967.                     $this,
  968.                     $this->messages
  969.                 );
  970.             }
  971.             $value = isset($optkv[1]$optkv[1: false;
  972.             if (!$opt->expectsArgument(&& $value !== false{
  973.                 throw Console_CommandLine_Exception::factory(
  974.                     'OPTION_VALUE_UNEXPECTED',
  975.                     array('name' => $opt->name'value' => $value),
  976.                     $this,
  977.                     $this->messages
  978.                 );
  979.             }
  980.             if ($opt->expectsArgument(&& $value === false{
  981.                 // maybe the long option argument is separated by a space, if 
  982.                 // this is the case it will be the next arg
  983.                 if ($last && !$opt->argument_optional{
  984.                     throw Console_CommandLine_Exception::factory(
  985.                         'OPTION_VALUE_REQUIRED',
  986.                         array('name' => $opt->name),
  987.                         $this,
  988.                         $this->messages
  989.                     );
  990.                 }
  991.                 // we will have a value next time
  992.                 $lastopt $opt;
  993.                 return;
  994.             }
  995.             if ($opt->action == 'StoreArray'{
  996.                 $lastopt $opt;
  997.             }
  998.             $this->_dispatchAction($opt$value$result);
  999.         else if (!$stopflag && substr($token01== '-'{
  1000.             // a short option
  1001.             $optname substr($token02);
  1002.             if ($optname == '-'{
  1003.                 // special case of "-": try to read stdin
  1004.                 $args[file_get_contents('php://stdin');
  1005.                 return;
  1006.             }
  1007.             $opt $this->findOption($optname);
  1008.             if (!$opt{
  1009.                 throw Console_CommandLine_Exception::factory(
  1010.                     'OPTION_UNKNOWN',
  1011.                     array('name' => $optname),
  1012.                     $this,
  1013.                     $this->messages
  1014.                 );
  1015.             }
  1016.             // parse other options or set the value
  1017.             // in short: handle -f<value> and -f <value>
  1018.             $next substr($token21);
  1019.             // check if we must wait for a value
  1020.             if ($next === false{
  1021.                 if ($opt->expectsArgument()) {
  1022.                     if ($last && !$opt->argument_optional{
  1023.                         throw Console_CommandLine_Exception::factory(
  1024.                             'OPTION_VALUE_REQUIRED',
  1025.                             array('name' => $opt->name),
  1026.                             $this,
  1027.                             $this->messages
  1028.                         );
  1029.                     }
  1030.                     // we will have a value next time
  1031.                     $lastopt $opt;
  1032.                     return;
  1033.                 }
  1034.                 $value = false;
  1035.             else {
  1036.                 if (!$opt->expectsArgument()) 
  1037.                     if ($nextopt $this->findOption('-' $next)) {
  1038.                         $this->_dispatchAction($optfalse$result);
  1039.                         $this->parseToken('-' substr($token2)$result,
  1040.                             $args$last);
  1041.                         return;
  1042.                     else {
  1043.                         throw Console_CommandLine_Exception::factory(
  1044.                             'OPTION_UNKNOWN',
  1045.                             array('name' => $next),
  1046.                             $this,
  1047.                             $this->messages
  1048.                         );
  1049.                     }
  1050.                 }
  1051.                 if ($opt->action == 'StoreArray'{
  1052.                     $lastopt $opt;
  1053.                 }
  1054.                 $value substr($token2);
  1055.             }
  1056.             $this->_dispatchAction($opt$value$result);
  1057.         else {
  1058.             // We have an argument.
  1059.             // if we are in POSIX compliant mode, we must set the stop flag to 
  1060.             // true in order to stop option parsing.
  1061.             if (!$stopflag && $this->force_posix{
  1062.                 $stopflag = true;
  1063.             }
  1064.             $args[$token;
  1065.         }
  1066.     }
  1067.  
  1068.     // }}}
  1069.     // addBuiltinOptions() {{{
  1070.  
  1071.     /**
  1072.      * Adds the builtin "Help" and "Version" options if needed.
  1073.      *
  1074.      * @return void 
  1075.      */
  1076.     public function addBuiltinOptions()
  1077.     {
  1078.         if ($this->add_help_option{
  1079.             $helpOptionParams = array(
  1080.                 'long_name'   => '--help',
  1081.                 'description' => 'show this help message and exit',
  1082.                 'action'      => 'Help'   
  1083.             );
  1084.             if (!($option $this->findOption('-h')) || $option->action == 'Help'{
  1085.                 // short name is available, take it
  1086.                 $helpOptionParams['short_name''-h';
  1087.             }
  1088.             $this->addOption('help'$helpOptionParams);
  1089.         }
  1090.         if ($this->add_version_option && !empty($this->version)) {
  1091.             $versionOptionParams = array(
  1092.                 'long_name'   => '--version',
  1093.                 'description' => 'show the program version and exit',
  1094.                 'action'      => 'Version'   
  1095.             );
  1096.             if (!$this->findOption('-v')) {
  1097.                 // short name is available, take it
  1098.                 $versionOptionParams['short_name''-v';
  1099.             }
  1100.             $this->addOption('version'$versionOptionParams);
  1101.         }
  1102.     
  1103.  
  1104.     // }}}
  1105.     // getArgcArgv() {{{
  1106.  
  1107.     /**
  1108.      * Tries to return an array containing argc and argv, or trigger an error
  1109.      * if it fails to get them.
  1110.      *
  1111.      * @return array The argc/argv array
  1112.      * @throws Console_CommandLine_Exception
  1113.      */
  1114.     protected function getArgcArgv()
  1115.     {
  1116.         if (php_sapi_name(!= 'cli'{
  1117.             // we have a web request
  1118.             $argv = array($this->name);
  1119.             if (isset($_REQUEST)) {
  1120.                 foreach ($_REQUEST as $key => $value{
  1121.                     if (!is_array($value)) {
  1122.                         $value = array($value);
  1123.                     }
  1124.                     $opt $this->findOption($key);
  1125.                     if ($opt instanceof Console_CommandLine_Option{
  1126.                         // match a configured option
  1127.                         $argv[$opt->short_name ? 
  1128.                             $opt->short_name : $opt->long_name;
  1129.                         foreach ($value as $v{
  1130.                             if ($opt->expectsArgument()) {
  1131.                                 $argv[= isset($_GET[$key]urldecode($v$v;
  1132.                             else if ($v == '0' || $v == 'false'{
  1133.                                 array_pop($argv);
  1134.                             }
  1135.                         }
  1136.                     else if (isset($this->args[$key])) {
  1137.                         // match a configured argument
  1138.                         foreach ($value as $v{
  1139.                             $argv[= isset($_GET[$key]urldecode($v$v;
  1140.                         }
  1141.                     }
  1142.                 }
  1143.             }
  1144.             return array(count($argv)$argv);
  1145.         }
  1146.         if (isset($argc&& isset($argv)) {
  1147.             // case of register_argv_argc = 1
  1148.             return array($argc$argv);
  1149.         }
  1150.         if (isset($_SERVER['argc']&& isset($_SERVER['argv'])) {
  1151.             return array($_SERVER['argc']$_SERVER['argv']);
  1152.         }
  1153.         return array(0array());
  1154.     }
  1155.  
  1156.     // }}}
  1157.     // _dispatchAction() {{{
  1158.  
  1159.     /**
  1160.      * Dispatches the given option or store the option to dispatch it later.
  1161.      *
  1162.      * @param Console_CommandLine_Option $option The option instance
  1163.      * @param string                     $token  Command line token to parse
  1164.      * @param Console_CommandLine_Result $result The result instance
  1165.      *
  1166.      * @return void 
  1167.      */
  1168.     private function _dispatchAction($option$token$result)
  1169.     {
  1170.         if ($option->action == 'Password'{
  1171.             $this->_dispatchLater[= array($option$token$result);
  1172.         else {
  1173.             $option->dispatchAction($token$result$this);
  1174.         }
  1175.     }
  1176.     // }}}
  1177.     // _getSubCommand() {{{
  1178.  
  1179.     /**
  1180.      * Tries to return the subcommand that matches the given token or returns
  1181.      * false if no subcommand was found.
  1182.      *
  1183.      * @param string $token Current command line token
  1184.      *
  1185.      * @return mixed An instance of Console_CommandLine_Command or false
  1186.      */
  1187.     private function _getSubCommand($token)
  1188.     {
  1189.         foreach ($this->commands as $cmd{
  1190.             if ($cmd->name == $token || in_array($token$cmd->aliases)) {
  1191.                 return $cmd;
  1192.             }
  1193.         }
  1194.         return false;
  1195.     }
  1196.  
  1197.     // }}}
  1198. }

Documentation generated on Fri, 19 Jun 2009 12:00:05 +0000 by phpDocumentor 1.4.2. PEAR Logo Copyright © PHP Group 2004.