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

Documentation generated on Thu, 25 Oct 2012 08:00:04 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.