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 297057 2010-03-29 09:42:19Z rquadling $
  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.3
  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 --help option
  280.      *     'add_version_option' => true, // or false to disable --version 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.             // some properties must cascade to the child command if not 
  534.             // passed explicitely. This is done only in this case, because if 
  535.             // we have a Command object we have no way to determine if theses 
  536.             // properties have already been set
  537.             $cascade = array(
  538.                 'add_help_option',
  539.                 'add_version_option',
  540.                 'outputter',
  541.                 'message_provider',
  542.                 'force_posix',
  543.                 'force_options_defaults'
  544.             );
  545.             foreach ($cascade as $property{
  546.                 if (!isset($params[$property])) {
  547.                     $command->$property $this->$property;
  548.                 }
  549.             }
  550.             if (!isset($params['renderer'])) {
  551.                 $renderer          = clone $this->renderer;
  552.                 $renderer->parser  = $command;
  553.                 $command->renderer = $renderer;
  554.             }
  555.         }
  556.         $command->parent = $this;
  557.         $this->commands[$command->name$command;
  558.         return $command;
  559.     }
  560.  
  561.     // }}}
  562.     // addOption() {{{
  563.  
  564.     /**
  565.      * Adds an option to the command line parser and returns it.
  566.      *
  567.      * Adds an option with the name $name and set its attributes with the
  568.      * array $params, then return the Console_CommandLine_Option instance
  569.      * created.
  570.      * The method accepts another form: you can directly pass a
  571.      * Console_CommandLine_Option object as the sole argument, this allows
  572.      * you to contruct the option separately, in order to reuse it in different
  573.      * command line parsers or commands for example.
  574.      *
  575.      * Example:
  576.      * <code>
  577.      * $parser = new Console_CommandLine();
  578.      * $parser->addOption('path', array(
  579.      *     'short_name'  => '-p',  // a short name
  580.      *     'long_name'   => '--path', // a long name
  581.      *     'description' => 'path to the dir', // a description msg
  582.      *     'action'      => 'StoreString',
  583.      *     'default'     => '/tmp' // a default value
  584.      * ));
  585.      * $parser->parse();
  586.      * </code>
  587.      *
  588.      * In a terminal, the help will be displayed like this:
  589.      * <code>
  590.      * $ myscript.php --help
  591.      * Usage: myscript.php [options]
  592.      *
  593.      * Options:
  594.      *   -h, --help  display this help message and exit
  595.      *   -p, --path  path to the dir
  596.      *
  597.      * </code>
  598.      *
  599.      * Various methods to specify an option, these 3 commands are equivalent:
  600.      * <code>
  601.      * $ myscript.php --path=some/path
  602.      * $ myscript.php -p some/path
  603.      * $ myscript.php -psome/path
  604.      * </code>
  605.      *
  606.      * @param mixed $name   A string containing the option name or an
  607.      *                       instance of Console_CommandLine_Option
  608.      * @param array $params An array containing the option attributes
  609.      *
  610.      * @return Console_CommandLine_Option The added option
  611.      * @see    Console_CommandLine_Option
  612.      */
  613.     public function addOption($name$params = array())
  614.     {
  615.         include_once 'Console/CommandLine/Option.php';
  616.         if ($name instanceof Console_CommandLine_Option{
  617.             $opt $name;
  618.         else {
  619.             $opt = new Console_CommandLine_Option($name$params);
  620.         }
  621.         $opt->validate();
  622.         if ($this->force_options_defaults{
  623.             $opt->setDefaults();
  624.         }
  625.         $this->options[$opt->name$opt;
  626.         if (!empty($opt->choices&& $opt->add_list_option{
  627.             $this->addOption('list_' $opt->namearray(
  628.                 'long_name'     => '--list-' $opt->name,
  629.                 'description'   => $this->message_provider->get(
  630.                     'LIST_OPTION_MESSAGE',
  631.                     array('name' => $opt->name)
  632.                 ),
  633.                 'action'        => 'List',
  634.                 'action_params' => array('list' => $opt->choices),
  635.             ));
  636.         }
  637.         return $opt;
  638.     }
  639.  
  640.     // }}}
  641.     // displayError() {{{
  642.  
  643.     /**
  644.      * Displays an error to the user via stderr and exit with $exitCode if its
  645.      * value is not equals to false.
  646.      *
  647.      * @param string $error    The error message
  648.      * @param int    $exitCode The exit code number (default: 1). If set to
  649.      *                          false, the exit() function will not be called
  650.      *
  651.      * @return void 
  652.      */
  653.     public function displayError($error$exitCode = 1)
  654.     {
  655.         $this->outputter->stderr($this->renderer->error($error));
  656.         if ($exitCode !== false{
  657.             exit($exitCode);
  658.         }
  659.     }
  660.  
  661.     // }}}
  662.     // displayUsage() {{{
  663.  
  664.     /**
  665.      * Displays the usage help message to the user via stdout and exit with
  666.      * $exitCode if its value is not equals to false.
  667.      *
  668.      * @param int $exitCode The exit code number (default: 0). If set to
  669.      *                       false, the exit() function will not be called
  670.      *
  671.      * @return void 
  672.      */
  673.     public function displayUsage($exitCode = 0)
  674.     {
  675.         $this->outputter->stdout($this->renderer->usage());
  676.         if ($exitCode !== false{
  677.             exit($exitCode);
  678.         }
  679.     }
  680.  
  681.     // }}}
  682.     // displayVersion() {{{
  683.  
  684.     /**
  685.      * Displays the program version to the user via stdout and exit with
  686.      * $exitCode if its value is not equals to false.
  687.      *
  688.      *
  689.      * @param int $exitCode The exit code number (default: 0). If set to
  690.      *                       false, the exit() function will not be called
  691.      *
  692.      * @return void 
  693.      */
  694.     public function displayVersion($exitCode = 0)
  695.     {
  696.         $this->outputter->stdout($this->renderer->version());
  697.         if ($exitCode !== false{
  698.             exit($exitCode);
  699.         }
  700.     }
  701.  
  702.     // }}}
  703.     // findOption() {{{
  704.  
  705.     /**
  706.      * Finds the option that matches the given short_name (ex: -v), long_name
  707.      * (ex: --verbose) or name (ex: verbose).
  708.      *
  709.      * @param string $str The option identifier
  710.      *
  711.      * @return mixed A Console_CommandLine_Option instance or false
  712.      */
  713.     public function findOption($str)
  714.     {
  715.         $str trim($str);
  716.         if ($str === ''{
  717.             return false;
  718.         }
  719.         $matches = array();
  720.         foreach ($this->options as $opt{
  721.             if ($opt->short_name == $str || $opt->long_name == $str ||
  722.                 $opt->name == $str{
  723.                 // exact match
  724.                 return $opt;
  725.             }
  726.             if (substr($opt->long_name0strlen($str)) === $str{
  727.                 // abbreviated long option
  728.                 $matches[$opt;
  729.             }
  730.         }
  731.         if ($count count($matches)) {
  732.             if ($count > 1{
  733.                 $matches_str '';
  734.                 $padding     '';
  735.                 foreach ($matches as $opt{
  736.                     $matches_str .= $padding $opt->long_name;
  737.                     $padding      ', ';
  738.                 }
  739.                 throw Console_CommandLine_Exception::factory(
  740.                     'OPTION_AMBIGUOUS',
  741.                     array('name' => $str'matches' => $matches_str),
  742.                     $this,
  743.                     $this->messages
  744.                 );
  745.             }
  746.             return $matches[0];
  747.         }
  748.         return false;
  749.     }
  750.     // }}}
  751.     // registerAction() {{{
  752.  
  753.     /**
  754.      * Registers a custom action for the parser, an example:
  755.      *
  756.      * <code>
  757.      *
  758.      * // in this example we create a "range" action:
  759.      * // the user will be able to enter something like:
  760.      * // $ <program> -r 1,5
  761.      * // and in the result we will have:
  762.      * // $result->options['range']: array(1, 5)
  763.      *
  764.      * require_once 'Console/CommandLine.php';
  765.      * require_once 'Console/CommandLine/Action.php';
  766.      *
  767.      * class ActionRange extends Console_CommandLine_Action
  768.      * {
  769.      *     public function execute($value=false, $params=array())
  770.      *     {
  771.      *         $range = explode(',', str_replace(' ', '', $value));
  772.      *         if (count($range) != 2) {
  773.      *             throw new Exception(sprintf(
  774.      *                 'Option "%s" must be 2 integers separated by a comma',
  775.      *                 $this->option->name
  776.      *             ));
  777.      *         }
  778.      *         $this->setResult($range);
  779.      *     }
  780.      * }
  781.      * // then we can register our action
  782.      * Console_CommandLine::registerAction('Range', 'ActionRange');
  783.      * // and now our action is available !
  784.      * $parser = new Console_CommandLine();
  785.      * $parser->addOption('range', array(
  786.      *     'short_name'  => '-r',
  787.      *     'long_name'   => '--range',
  788.      *     'action'      => 'Range', // note our custom action
  789.      *     'description' => 'A range of two integers separated by a comma'
  790.      * ));
  791.      * // etc...
  792.      *
  793.      * </code>
  794.      *
  795.      * @param string $name  The name of the custom action
  796.      * @param string $class The class name of the custom action
  797.      *
  798.      * @return void 
  799.      */
  800.     public static function registerAction($name$class
  801.     {
  802.         if (!isset(self::$actions[$name])) {
  803.             if (!class_exists($class)) {
  804.                 self::triggerError('action_class_does_not_exists',
  805.                     E_USER_ERROR,
  806.                     array('{$name}' => $name'{$class}' => $class));
  807.             }
  808.             self::$actions[$name= array($classfalse);
  809.         }
  810.     }
  811.  
  812.     // }}}
  813.     // triggerError() {{{
  814.  
  815.     /**
  816.      * A wrapper for programming errors triggering.
  817.      *
  818.      * @param string $msgId  Identifier of the message
  819.      * @param int    $level  The php error level
  820.      * @param array  $params An array of search=>replaces entries
  821.      *
  822.      * @return void 
  823.      * @todo remove Console::triggerError() and use exceptions only
  824.      */
  825.     public static function triggerError($msgId$level$params=array()) 
  826.     {
  827.         if (isset(self::$errors[$msgId])) {
  828.             $msg = str_replace(array_keys($params),
  829.                 array_values($params)self::$errors[$msgId])
  830.             trigger_error($msg$level);
  831.         else {
  832.             trigger_error('unknown error'$level);
  833.         }
  834.     }
  835.  
  836.     // }}}
  837.     // parse() {{{
  838.  
  839.     /**
  840.      * Parses the command line arguments and returns a
  841.      * Console_CommandLine_Result instance.
  842.      *
  843.      * @param integer $userArgc Number of arguments (optional)
  844.      * @param array   $userArgv Array containing arguments (optional)
  845.      *
  846.      * @return Console_CommandLine_Result The result instance
  847.      * @throws Exception on user errors
  848.      */
  849.     public function parse($userArgc=null$userArgv=null)
  850.     {
  851.         $this->addBuiltinOptions();
  852.         if ($userArgc !== null && $userArgv !== null{
  853.             $argc $userArgc;
  854.             $argv $userArgv;
  855.         else {
  856.             list($argc$argv$this->getArgcArgv();
  857.         }
  858.         // build an empty result
  859.         include_once 'Console/CommandLine/Result.php';
  860.         $result = new Console_CommandLine_Result();
  861.         if (!($this instanceof Console_CommandLine_Command)) {
  862.             // remove script name if we're not in a subcommand
  863.             array_shift($argv);
  864.             $argc--;
  865.         }
  866.         // will contain arguments
  867.         $args = array();
  868.         foreach ($this->options as $name=>$option{
  869.             $result->options[$name$option->default;
  870.         }
  871.         // parse command line tokens
  872.         while ($argc--{
  873.             $token array_shift($argv);
  874.             try {
  875.                 if ($cmd $this->_getSubCommand($token)) {
  876.                     $result->command_name = $cmd->name;
  877.                     $result->command      = $cmd->parse($argc$argv);
  878.                     break;
  879.                 else {
  880.                     $this->parseToken($token$result$args$argc);
  881.                 }
  882.             catch (Exception $exc{
  883.                 throw $exc;
  884.             }
  885.         }
  886.         // Parse a null token to allow any undespatched actions to be despatched.
  887.         $this->parseToken(null$result$args0);
  888.         // Check if an invalid subcommand was specified. If there are
  889.         // subcommands and no arguments, but an argument was provided, it is
  890.         // an invalid subcommand.
  891.         if (   count($this->commands> 0
  892.             && count($this->args=== 0
  893.             && count($args> 0
  894.         {
  895.             throw Console_CommandLine_Exception::factory(
  896.                 'INVALID_SUBCOMMAND',
  897.                 array('command' => $args[0]),
  898.                 $this,
  899.                 $this->messages
  900.             );
  901.         }
  902.         // minimum argument number check
  903.         $argnum = 0;
  904.         foreach ($this->args as $name=>$arg{
  905.             if (!$arg->optional{
  906.                 $argnum++;
  907.             }
  908.         }
  909.         if (count($args$argnum{
  910.             throw Console_CommandLine_Exception::factory(
  911.                 'ARGUMENT_REQUIRED',
  912.                 array('argnum' => $argnum'plural' => $argnum>1 ? 's'''),
  913.                 $this,
  914.                 $this->messages
  915.             );
  916.         }
  917.         // handle arguments
  918.         $c count($this->args);
  919.         foreach ($this->args as $name=>$arg{
  920.             $c--;
  921.             if ($arg->multiple{
  922.                 $result->args[$name$c array_splice($args0-$c$args;
  923.             else {
  924.                 $result->args[$namearray_shift($args);
  925.             }
  926.         }
  927.         // dispatch deferred options
  928.         foreach ($this->_dispatchLater as $optArray{
  929.             $optArray[0]->dispatchAction($optArray[1]$optArray[2]$this);
  930.         }
  931.         return $result;
  932.     }
  933.  
  934.     // }}}
  935.     // parseToken() {{{
  936.  
  937.     /**
  938.      * Parses the command line token and modifies *by reference* the $options
  939.      * and $args arrays.
  940.      *
  941.      * @param string $token  The command line token to parse
  942.      * @param object $result The Console_CommandLine_Result instance
  943.      * @param array  &$args  The argv array
  944.      * @param int    $argc   Number of lasting args
  945.      *
  946.      * @return void 
  947.      * @access protected
  948.      * @throws Exception on user errors
  949.      */
  950.     protected function parseToken($token$result&$args$argc)
  951.     {
  952.         static $lastopt  = false;
  953.         static $stopflag = false;
  954.         $last  $argc === 0;
  955.         if (!$stopflag && $lastopt{
  956.             if (substr($token01== '-'{
  957.                 if ($lastopt->argument_optional{
  958.                     $this->_dispatchAction($lastopt''$result);
  959.                     if ($lastopt->action != 'StoreArray'{
  960.                         $lastopt = false;
  961.                     }
  962.                 else if (isset($result->options[$lastopt->name])) {
  963.                     // case of an option that expect a list of args
  964.                     $lastopt = false;
  965.                 else {
  966.                     throw Console_CommandLine_Exception::factory(
  967.                         'OPTION_VALUE_REQUIRED',
  968.                         array('name' => $lastopt->name),
  969.                         $this,
  970.                         $this->messages
  971.                     );
  972.                 }
  973.             else {
  974.                 // when a StoreArray option is positioned last, the behavior
  975.                 // is to consider that if there's already an element in the
  976.                 // array, and the commandline expects one or more args, we
  977.                 // leave last tokens to arguments
  978.                 if ($lastopt->action == 'StoreArray' && 
  979.                     !empty($result->options[$lastopt->name]&&
  980.                     count($this->args($argc count($args))) {
  981.                     if (!is_null($token)) {
  982.                         $args[$token;
  983.                     }
  984.                     return;
  985.                 }
  986.                 if (!is_null($token|| $lastopt->action == 'Password'{
  987.                     $this->_dispatchAction($lastopt$token$result);
  988.                 }
  989.                 if ($lastopt->action != 'StoreArray'{
  990.                     $lastopt = false;
  991.                 }
  992.                 return;
  993.             }
  994.         }
  995.         if (!$stopflag && substr($token02== '--'{
  996.             // a long option
  997.             $optkv explode('='$token2);
  998.             if (trim($optkv[0]== '--'{
  999.                 // the special argument "--" forces in all cases the end of 
  1000.                 // option scanning.
  1001.                 $stopflag = true;
  1002.                 return;
  1003.             }
  1004.             $opt $this->findOption($optkv[0]);
  1005.             if (!$opt{
  1006.                 throw Console_CommandLine_Exception::factory(
  1007.                     'OPTION_UNKNOWN',
  1008.                     array('name' => $optkv[0]),
  1009.                     $this,
  1010.                     $this->messages
  1011.                 );
  1012.             }
  1013.             $value = isset($optkv[1]$optkv[1: false;
  1014.             if (!$opt->expectsArgument(&& $value !== false{
  1015.                 throw Console_CommandLine_Exception::factory(
  1016.                     'OPTION_VALUE_UNEXPECTED',
  1017.                     array('name' => $opt->name'value' => $value),
  1018.                     $this,
  1019.                     $this->messages
  1020.                 );
  1021.             }
  1022.             if ($opt->expectsArgument(&& $value === false{
  1023.                 // maybe the long option argument is separated by a space, if 
  1024.                 // this is the case it will be the next arg
  1025.                 if ($last && !$opt->argument_optional{
  1026.                     throw Console_CommandLine_Exception::factory(
  1027.                         'OPTION_VALUE_REQUIRED',
  1028.                         array('name' => $opt->name),
  1029.                         $this,
  1030.                         $this->messages
  1031.                     );
  1032.                 }
  1033.                 // we will have a value next time
  1034.                 $lastopt $opt;
  1035.                 return;
  1036.             }
  1037.             if ($opt->action == 'StoreArray'{
  1038.                 $lastopt $opt;
  1039.             }
  1040.             $this->_dispatchAction($opt$value$result);
  1041.         else if (!$stopflag && substr($token01== '-'{
  1042.             // a short option
  1043.             $optname substr($token02);
  1044.             if ($optname == '-'{
  1045.                 // special case of "-": try to read stdin
  1046.                 $args[file_get_contents('php://stdin');
  1047.                 return;
  1048.             }
  1049.             $opt $this->findOption($optname);
  1050.             if (!$opt{
  1051.                 throw Console_CommandLine_Exception::factory(
  1052.                     'OPTION_UNKNOWN',
  1053.                     array('name' => $optname),
  1054.                     $this,
  1055.                     $this->messages
  1056.                 );
  1057.             }
  1058.             // parse other options or set the value
  1059.             // in short: handle -f<value> and -f <value>
  1060.             $next substr($token21);
  1061.             // check if we must wait for a value
  1062.             if ($next === false{
  1063.                 if ($opt->expectsArgument()) {
  1064.                     if ($last && !$opt->argument_optional{
  1065.                         throw Console_CommandLine_Exception::factory(
  1066.                             'OPTION_VALUE_REQUIRED',
  1067.                             array('name' => $opt->name),
  1068.                             $this,
  1069.                             $this->messages
  1070.                         );
  1071.                     }
  1072.                     // we will have a value next time
  1073.                     $lastopt $opt;
  1074.                     return;
  1075.                 }
  1076.                 $value = false;
  1077.             else {
  1078.                 if (!$opt->expectsArgument()) 
  1079.                     if ($nextopt $this->findOption('-' $next)) {
  1080.                         $this->_dispatchAction($optfalse$result);
  1081.                         $this->parseToken('-' substr($token2)$result,
  1082.                             $args$last);
  1083.                         return;
  1084.                     else {
  1085.                         throw Console_CommandLine_Exception::factory(
  1086.                             'OPTION_UNKNOWN',
  1087.                             array('name' => $next),
  1088.                             $this,
  1089.                             $this->messages
  1090.                         );
  1091.                     }
  1092.                 }
  1093.                 if ($opt->action == 'StoreArray'{
  1094.                     $lastopt $opt;
  1095.                 }
  1096.                 $value substr($token2);
  1097.             }
  1098.             $this->_dispatchAction($opt$value$result);
  1099.         else {
  1100.             // We have an argument.
  1101.             // if we are in POSIX compliant mode, we must set the stop flag to 
  1102.             // true in order to stop option parsing.
  1103.             if (!$stopflag && $this->force_posix{
  1104.                 $stopflag = true;
  1105.             }
  1106.             if (!is_null($token)) {
  1107.                 $args[$token;
  1108.             }
  1109.         }
  1110.     }
  1111.  
  1112.     // }}}
  1113.     // addBuiltinOptions() {{{
  1114.  
  1115.     /**
  1116.      * Adds the builtin "Help" and "Version" options if needed.
  1117.      *
  1118.      * @return void 
  1119.      */
  1120.     public function addBuiltinOptions()
  1121.     {
  1122.         if ($this->add_help_option{
  1123.             $helpOptionParams = array(
  1124.                 'long_name'   => '--help',
  1125.                 'description' => 'show this help message and exit',
  1126.                 'action'      => 'Help'   
  1127.             );
  1128.             if (!($option $this->findOption('-h')) || $option->action == 'Help'{
  1129.                 // short name is available, take it
  1130.                 $helpOptionParams['short_name''-h';
  1131.             }
  1132.             $this->addOption('help'$helpOptionParams);
  1133.         }
  1134.         if ($this->add_version_option && !empty($this->version)) {
  1135.             $versionOptionParams = array(
  1136.                 'long_name'   => '--version',
  1137.                 'description' => 'show the program version and exit',
  1138.                 'action'      => 'Version'   
  1139.             );
  1140.             if (!$this->findOption('-v')) {
  1141.                 // short name is available, take it
  1142.                 $versionOptionParams['short_name''-v';
  1143.             }
  1144.             $this->addOption('version'$versionOptionParams);
  1145.         }
  1146.     
  1147.  
  1148.     // }}}
  1149.     // getArgcArgv() {{{
  1150.  
  1151.     /**
  1152.      * Tries to return an array containing argc and argv, or trigger an error
  1153.      * if it fails to get them.
  1154.      *
  1155.      * @return array The argc/argv array
  1156.      * @throws Console_CommandLine_Exception
  1157.      */
  1158.     protected function getArgcArgv()
  1159.     {
  1160.         if (php_sapi_name(!= 'cli'{
  1161.             // we have a web request
  1162.             $argv = array($this->name);
  1163.             if (isset($_REQUEST)) {
  1164.                 foreach ($_REQUEST as $key => $value{
  1165.                     if (!is_array($value)) {
  1166.                         $value = array($value);
  1167.                     }
  1168.                     $opt $this->findOption($key);
  1169.                     if ($opt instanceof Console_CommandLine_Option{
  1170.                         // match a configured option
  1171.                         $argv[$opt->short_name ? 
  1172.                             $opt->short_name : $opt->long_name;
  1173.                         foreach ($value as $v{
  1174.                             if ($opt->expectsArgument()) {
  1175.                                 $argv[= isset($_REQUEST[$key]urldecode($v$v;
  1176.                             else if ($v == '0' || $v == 'false'{
  1177.                                 array_pop($argv);
  1178.                             }
  1179.                         }
  1180.                     else if (isset($this->args[$key])) {
  1181.                         // match a configured argument
  1182.                         foreach ($value as $v{
  1183.                             $argv[= isset($_REQUEST[$key]urldecode($v$v;
  1184.                         }
  1185.                     }
  1186.                 }
  1187.             }
  1188.             return array(count($argv)$argv);
  1189.         }
  1190.         if (isset($argc&& isset($argv)) {
  1191.             // case of register_argv_argc = 1
  1192.             return array($argc$argv);
  1193.         }
  1194.         if (isset($_SERVER['argc']&& isset($_SERVER['argv'])) {
  1195.             return array($_SERVER['argc']$_SERVER['argv']);
  1196.         }
  1197.         return array(0array());
  1198.     }
  1199.  
  1200.     // }}}
  1201.     // _dispatchAction() {{{
  1202.  
  1203.     /**
  1204.      * Dispatches the given option or store the option to dispatch it later.
  1205.      *
  1206.      * @param Console_CommandLine_Option $option The option instance
  1207.      * @param string                     $token  Command line token to parse
  1208.      * @param Console_CommandLine_Result $result The result instance
  1209.      *
  1210.      * @return void 
  1211.      */
  1212.     private function _dispatchAction($option$token$result)
  1213.     {
  1214.         if ($option->action == 'Password'{
  1215.             $this->_dispatchLater[= array($option$token$result);
  1216.         else {
  1217.             $option->dispatchAction($token$result$this);
  1218.         }
  1219.     }
  1220.     // }}}
  1221.     // _getSubCommand() {{{
  1222.  
  1223.     /**
  1224.      * Tries to return the subcommand that matches the given token or returns
  1225.      * false if no subcommand was found.
  1226.      *
  1227.      * @param string $token Current command line token
  1228.      *
  1229.      * @return mixed An instance of Console_CommandLine_Command or false
  1230.      */
  1231.     private function _getSubCommand($token)
  1232.     {
  1233.         foreach ($this->commands as $cmd{
  1234.             if ($cmd->name == $token || in_array($token$cmd->aliases)) {
  1235.                 return $cmd;
  1236.             }
  1237.         }
  1238.         return false;
  1239.     }
  1240.  
  1241.     // }}}
  1242. }

Documentation generated on Mon, 11 Mar 2019 15:37:02 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.