123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305 |
- <?php
-
- /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
-
- /**
- * This file is part of the PEAR Console_CommandLine package.
- *
- * A full featured package for managing command-line options and arguments
- * hightly inspired from python optparse module, it allows the developper to
- * easily build complex command line interfaces.
- *
- * PHP version 5
- *
- * LICENSE: This source file is subject to the MIT license that is available
- * through the world-wide-web at the following URI:
- * http://opensource.org/licenses/mit-license.php
- *
- * @category Console
- * @package Console_CommandLine
- * @author David JEAN LOUIS <izimobil@gmail.com>
- * @copyright 2007 David JEAN LOUIS
- * @license http://opensource.org/licenses/mit-license.php MIT License
- * @version CVS: $Id$
- * @link http://pear.php.net/package/Console_CommandLine
- * @since Class available since release 0.1.0
- * @filesource
- */
-
- /**
- * Required unconditionally
- */
- require_once 'Console/CommandLine/Exception.php';
- require_once 'Console/CommandLine/Outputter/Default.php';
- require_once 'Console/CommandLine/Renderer/Default.php';
- require_once 'Console/CommandLine/MessageProvider/Default.php';
-
- /**
- * Main class for parsing command line options and arguments.
- *
- * There are three ways to create parsers with this class:
- * <code>
- * // direct usage
- * $parser = new Console_CommandLine();
- *
- * // with an xml definition file
- * $parser = Console_CommandLine::fromXmlFile('path/to/file.xml');
- *
- * // with an xml definition string
- * $validXmlString = '..your xml string...';
- * $parser = Console_CommandLine::fromXmlString($validXmlString);
- * </code>
- *
- * @category Console
- * @package Console_CommandLine
- * @author David JEAN LOUIS <izimobil@gmail.com>
- * @copyright 2007 David JEAN LOUIS
- * @license http://opensource.org/licenses/mit-license.php MIT License
- * @version Release: 1.2.2
- * @link http://pear.php.net/package/Console_CommandLine
- * @since File available since release 0.1.0
- * @example docs/examples/ex1.php
- * @example docs/examples/ex2.php
- */
- class Console_CommandLine
- {
- // Public properties {{{
-
- /**
- * Error messages.
- *
- * @var array $errors Error messages
- * @todo move this to Console_CommandLine_MessageProvider
- */
- public static $errors = array(
- 'option_bad_name' => 'option name must be a valid php variable name (got: {$name})',
- 'argument_bad_name' => 'argument name must be a valid php variable name (got: {$name})',
- 'argument_no_default' => 'only optional arguments can have a default value',
- 'option_long_and_short_name_missing' => 'you must provide at least an option short name or long name for option "{$name}"',
- 'option_bad_short_name' => 'option "{$name}" short name must be a dash followed by a letter (got: "{$short_name}")',
- 'option_bad_long_name' => 'option "{$name}" long name must be 2 dashes followed by a word (got: "{$long_name}")',
- 'option_unregistered_action' => 'unregistered action "{$action}" for option "{$name}".',
- 'option_bad_action' => 'invalid action for option "{$name}".',
- 'option_invalid_callback' => 'you must provide a valid callback for option "{$name}"',
- 'action_class_does_not_exists' => 'action "{$name}" class "{$class}" not found, make sure that your class is available before calling Console_CommandLine::registerAction()',
- 'invalid_xml_file' => 'XML definition file "{$file}" does not exists or is not readable',
- 'invalid_rng_file' => 'RNG file "{$file}" does not exists or is not readable'
- );
-
- /**
- * The name of the program, if not given it defaults to argv[0].
- *
- * @var string $name Name of your program
- */
- public $name;
-
- /**
- * A description text that will be displayed in the help message.
- *
- * @var string $description Description of your program
- */
- public $description = '';
-
- /**
- * A string that represents the version of the program, if this property is
- * not empty and property add_version_option is not set to false, the
- * command line parser will add a --version option, that will display the
- * property content.
- *
- * @var string $version
- * @access public
- */
- public $version = '';
-
- /**
- * Boolean that determine if the command line parser should add the help
- * (-h, --help) option automatically.
- *
- * @var bool $add_help_option Whether to add a help option or not
- */
- public $add_help_option = true;
-
- /**
- * Boolean that determine if the command line parser should add the version
- * (-v, --version) option automatically.
- * Note that the version option is also generated only if the version
- * property is not empty, it's up to you to provide a version string of
- * course.
- *
- * @var bool $add_version_option Whether to add a version option or not
- */
- public $add_version_option = true;
-
- /**
- * Boolean that determine if providing a subcommand is mandatory.
- *
- * @var bool $subcommand_required Whether a subcommand is required or not
- */
- public $subcommand_required = false;
-
- /**
- * The command line parser renderer instance.
- *
- * @var object that implements Console_CommandLine_Renderer interface
- * @access protected
- */
- public $renderer = false;
-
- /**
- * The command line parser outputter instance.
- *
- * @var Console_CommandLine_Outputter An outputter
- */
- public $outputter = false;
-
- /**
- * The command line message provider instance.
- *
- * @var Console_CommandLine_MessageProvider A message provider instance
- */
- public $message_provider = false;
-
- /**
- * Boolean that tells the parser to be POSIX compliant, POSIX demands the
- * following behavior: the first non-option stops option processing.
- *
- * @var bool $force_posix Whether to force posix compliance or not
- */
- public $force_posix = false;
-
- /**
- * Boolean that tells the parser to set relevant options default values,
- * according to the option action.
- *
- * @see Console_CommandLine_Option::setDefaults()
- * @var bool $force_options_defaults Whether to force option default values
- */
- public $force_options_defaults = false;
-
-
- /**
- * Boolean that tells the parser to treat a single - option as an argument
- * instead of trying to read STDIN.
- *
- * @var bool $avoid_reading_stdin Whether to treat - as an argument
- */
- public $avoid_reading_stdin = false;
-
- /**
- * An array of Console_CommandLine_Option objects.
- *
- * @var array $options The options array
- */
- public $options = array();
-
- /**
- * An array of Console_CommandLine_Argument objects.
- *
- * @var array $args The arguments array
- */
- public $args = array();
-
- /**
- * An array of Console_CommandLine_Command objects (sub commands).
- *
- * @var array $commands The commands array
- */
- public $commands = array();
-
- /**
- * Parent, only relevant in Command objects but left here for interface
- * convenience.
- *
- * @var Console_CommandLine The parent instance
- * @todo move Console_CommandLine::parent to Console_CommandLine_Command
- */
- public $parent = false;
-
- /**
- * Array of valid actions for an option, this array will also store user
- * registered actions.
- *
- * The array format is:
- * <pre>
- * array(
- * <ActionName:string> => array(<ActionClass:string>, <builtin:bool>)
- * )
- * </pre>
- *
- * @var array $actions List of valid actions
- */
- public static $actions = array(
- 'StoreTrue' => array('Console_CommandLine_Action_StoreTrue', true),
- 'StoreFalse' => array('Console_CommandLine_Action_StoreFalse', true),
- 'StoreString' => array('Console_CommandLine_Action_StoreString', true),
- 'StoreInt' => array('Console_CommandLine_Action_StoreInt', true),
- 'StoreFloat' => array('Console_CommandLine_Action_StoreFloat', true),
- 'StoreArray' => array('Console_CommandLine_Action_StoreArray', true),
- 'Callback' => array('Console_CommandLine_Action_Callback', true),
- 'Counter' => array('Console_CommandLine_Action_Counter', true),
- 'Help' => array('Console_CommandLine_Action_Help', true),
- 'Version' => array('Console_CommandLine_Action_Version', true),
- 'Password' => array('Console_CommandLine_Action_Password', true),
- 'List' => array('Console_CommandLine_Action_List', true),
- );
-
- /**
- * Custom errors messages for this command
- *
- * This array is of the form:
- * <code>
- * <?php
- * array(
- * $messageName => $messageText,
- * $messageName => $messageText,
- * ...
- * );
- * ?>
- * </code>
- *
- * If specified, these messages override the messages provided by the
- * default message provider. For example:
- * <code>
- * <?php
- * $messages = array(
- * 'ARGUMENT_REQUIRED' => 'The argument foo is required.',
- * );
- * ?>
- * </code>
- *
- * @var array
- * @see Console_CommandLine_MessageProvider_Default
- */
- public $messages = array();
-
- // }}}
- // {{{ Private properties
-
- /**
- * Array of options that must be dispatched at the end.
- *
- * @var array $_dispatchLater Options to be dispatched
- */
- private $_dispatchLater = array();
-
- private $_lastopt = false;
- private $_stopflag = false;
-
- // }}}
- // __construct() {{{
-
- /**
- * Constructor.
- * Example:
- *
- * <code>
- * $parser = new Console_CommandLine(array(
- * 'name' => 'yourprogram', // defaults to argv[0]
- * 'description' => 'Description of your program',
- * 'version' => '0.0.1', // your program version
- * 'add_help_option' => true, // or false to disable --help option
- * 'add_version_option' => true, // or false to disable --version option
- * 'force_posix' => false // or true to force posix compliance
- * ));
- * </code>
- *
- * @param array $params An optional array of parameters
- *
- * @return void
- */
- public function __construct(array $params = array())
- {
- if (isset($params['name'])) {
- $this->name = $params['name'];
- } else if (isset($argv) && count($argv) > 0) {
- $this->name = $argv[0];
- } else if (isset($_SERVER['argv']) && count($_SERVER['argv']) > 0) {
- $this->name = $_SERVER['argv'][0];
- } else if (isset($_SERVER['SCRIPT_NAME'])) {
- $this->name = basename($_SERVER['SCRIPT_NAME']);
- }
- if (isset($params['description'])) {
- $this->description = $params['description'];
- }
- if (isset($params['version'])) {
- $this->version = $params['version'];
- }
- if (isset($params['add_version_option'])) {
- $this->add_version_option = $params['add_version_option'];
- }
- if (isset($params['add_help_option'])) {
- $this->add_help_option = $params['add_help_option'];
- }
- if (isset($params['subcommand_required'])) {
- $this->subcommand_required = $params['subcommand_required'];
- }
- if (isset($params['force_posix'])) {
- $this->force_posix = $params['force_posix'];
- } else if (getenv('POSIXLY_CORRECT')) {
- $this->force_posix = true;
- }
- if (isset($params['messages']) && is_array($params['messages'])) {
- $this->messages = $params['messages'];
- }
- // set default instances
- $this->renderer = new Console_CommandLine_Renderer_Default($this);
- $this->outputter = new Console_CommandLine_Outputter_Default();
- $this->message_provider = new Console_CommandLine_MessageProvider_Default();
- }
-
- // }}}
- // accept() {{{
-
- /**
- * Method to allow Console_CommandLine to accept either:
- * + a custom renderer,
- * + a custom outputter,
- * + or a custom message provider
- *
- * @param mixed $instance The custom instance
- *
- * @return void
- * @throws Console_CommandLine_Exception if wrong argument passed
- */
- public function accept($instance)
- {
- if ($instance instanceof Console_CommandLine_Renderer) {
- if (property_exists($instance, 'parser') && !$instance->parser) {
- $instance->parser = $this;
- }
- $this->renderer = $instance;
- } else if ($instance instanceof Console_CommandLine_Outputter) {
- $this->outputter = $instance;
- } else if ($instance instanceof Console_CommandLine_MessageProvider) {
- $this->message_provider = $instance;
- } else {
- throw Console_CommandLine_Exception::factory(
- 'INVALID_CUSTOM_INSTANCE',
- array(),
- $this,
- $this->messages
- );
- }
- }
-
- // }}}
- // fromXmlFile() {{{
-
- /**
- * Returns a command line parser instance built from an xml file.
- *
- * Example:
- * <code>
- * require_once 'Console/CommandLine.php';
- * $parser = Console_CommandLine::fromXmlFile('path/to/file.xml');
- * $result = $parser->parse();
- * </code>
- *
- * @param string $file Path to the xml file
- *
- * @return Console_CommandLine The parser instance
- */
- public static function fromXmlFile($file)
- {
- include_once 'Console/CommandLine/XmlParser.php';
- return Console_CommandLine_XmlParser::parse($file);
- }
-
- // }}}
- // fromXmlString() {{{
-
- /**
- * Returns a command line parser instance built from an xml string.
- *
- * Example:
- * <code>
- * require_once 'Console/CommandLine.php';
- * $xmldata = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
- * <command>
- * <description>Compress files</description>
- * <option name="quiet">
- * <short_name>-q</short_name>
- * <long_name>--quiet</long_name>
- * <description>be quiet when run</description>
- * <action>StoreTrue/action>
- * </option>
- * <argument name="files">
- * <description>a list of files</description>
- * <multiple>true</multiple>
- * </argument>
- * </command>';
- * $parser = Console_CommandLine::fromXmlString($xmldata);
- * $result = $parser->parse();
- * </code>
- *
- * @param string $string The xml data
- *
- * @return Console_CommandLine The parser instance
- */
- public static function fromXmlString($string)
- {
- include_once 'Console/CommandLine/XmlParser.php';
- return Console_CommandLine_XmlParser::parseString($string);
- }
-
- // }}}
- // addArgument() {{{
-
- /**
- * Adds an argument to the command line parser and returns it.
- *
- * Adds an argument with the name $name and set its attributes with the
- * array $params, then return the Console_CommandLine_Argument instance
- * created.
- * The method accepts another form: you can directly pass a
- * Console_CommandLine_Argument object as the sole argument, this allows
- * you to contruct the argument separately, in order to reuse it in
- * different command line parsers or commands for example.
- *
- * Example:
- * <code>
- * $parser = new Console_CommandLine();
- * // add an array argument
- * $parser->addArgument('input_files', array('multiple'=>true));
- * // add a simple argument
- * $parser->addArgument('output_file');
- * $result = $parser->parse();
- * print_r($result->args['input_files']);
- * print_r($result->args['output_file']);
- * // will print:
- * // array('file1', 'file2')
- * // 'file3'
- * // if the command line was:
- * // myscript.php file1 file2 file3
- * </code>
- *
- * In a terminal, the help will be displayed like this:
- * <code>
- * $ myscript.php install -h
- * Usage: myscript.php <input_files...> <output_file>
- * </code>
- *
- * @param mixed $name A string containing the argument name or an
- * instance of Console_CommandLine_Argument
- * @param array $params An array containing the argument attributes
- *
- * @return Console_CommandLine_Argument the added argument
- * @see Console_CommandLine_Argument
- */
- public function addArgument($name, $params = array())
- {
- if ($name instanceof Console_CommandLine_Argument) {
- $argument = $name;
- } else {
- include_once 'Console/CommandLine/Argument.php';
- $argument = new Console_CommandLine_Argument($name, $params);
- }
- $argument->validate();
- $this->args[$argument->name] = $argument;
- return $argument;
- }
-
- // }}}
- // addCommand() {{{
-
- /**
- * Adds a sub-command to the command line parser.
- *
- * Adds a command with the given $name to the parser and returns the
- * Console_CommandLine_Command instance, you can then populate the command
- * with options, configure it, etc... like you would do for the main parser
- * because the class Console_CommandLine_Command inherits from
- * Console_CommandLine.
- *
- * An example:
- * <code>
- * $parser = new Console_CommandLine();
- * $install_cmd = $parser->addCommand('install');
- * $install_cmd->addOption(
- * 'verbose',
- * array(
- * 'short_name' => '-v',
- * 'long_name' => '--verbose',
- * 'description' => 'be noisy when installing stuff',
- * 'action' => 'StoreTrue'
- * )
- * );
- * $parser->parse();
- * </code>
- * Then in a terminal:
- * <code>
- * $ myscript.php install -h
- * Usage: myscript.php install [options]
- *
- * Options:
- * -h, --help display this help message and exit
- * -v, --verbose be noisy when installing stuff
- *
- * $ myscript.php install --verbose
- * Installing whatever...
- * $
- * </code>
- *
- * @param mixed $name A string containing the command name or an
- * instance of Console_CommandLine_Command
- * @param array $params An array containing the command attributes
- *
- * @return Console_CommandLine_Command the added subcommand
- * @see Console_CommandLine_Command
- */
- public function addCommand($name, $params = array())
- {
- if ($name instanceof Console_CommandLine_Command) {
- $command = $name;
- } else {
- include_once 'Console/CommandLine/Command.php';
- $params['name'] = $name;
- $command = new Console_CommandLine_Command($params);
- // some properties must cascade to the child command if not
- // passed explicitely. This is done only in this case, because if
- // we have a Command object we have no way to determine if theses
- // properties have already been set
- $cascade = array(
- 'add_help_option',
- 'add_version_option',
- 'outputter',
- 'message_provider',
- 'force_posix',
- 'force_options_defaults'
- );
- foreach ($cascade as $property) {
- if (!isset($params[$property])) {
- $command->$property = $this->$property;
- }
- }
- if (!isset($params['renderer'])) {
- $renderer = clone $this->renderer;
- $renderer->parser = $command;
- $command->renderer = $renderer;
- }
- }
- $command->parent = $this;
- $this->commands[$command->name] = $command;
- return $command;
- }
-
- // }}}
- // addOption() {{{
-
- /**
- * Adds an option to the command line parser and returns it.
- *
- * Adds an option with the name $name and set its attributes with the
- * array $params, then return the Console_CommandLine_Option instance
- * created.
- * The method accepts another form: you can directly pass a
- * Console_CommandLine_Option object as the sole argument, this allows
- * you to contruct the option separately, in order to reuse it in different
- * command line parsers or commands for example.
- *
- * Example:
- * <code>
- * $parser = new Console_CommandLine();
- * $parser->addOption('path', array(
- * 'short_name' => '-p', // a short name
- * 'long_name' => '--path', // a long name
- * 'description' => 'path to the dir', // a description msg
- * 'action' => 'StoreString',
- * 'default' => '/tmp' // a default value
- * ));
- * $parser->parse();
- * </code>
- *
- * In a terminal, the help will be displayed like this:
- * <code>
- * $ myscript.php --help
- * Usage: myscript.php [options]
- *
- * Options:
- * -h, --help display this help message and exit
- * -p, --path path to the dir
- *
- * </code>
- *
- * Various methods to specify an option, these 3 commands are equivalent:
- * <code>
- * $ myscript.php --path=some/path
- * $ myscript.php -p some/path
- * $ myscript.php -psome/path
- * </code>
- *
- * @param mixed $name A string containing the option name or an
- * instance of Console_CommandLine_Option
- * @param array $params An array containing the option attributes
- *
- * @return Console_CommandLine_Option The added option
- * @see Console_CommandLine_Option
- */
- public function addOption($name, $params = array())
- {
- include_once 'Console/CommandLine/Option.php';
- if ($name instanceof Console_CommandLine_Option) {
- $opt = $name;
- } else {
- $opt = new Console_CommandLine_Option($name, $params);
- }
- $opt->validate();
- if ($this->force_options_defaults) {
- $opt->setDefaults();
- }
- $this->options[$opt->name] = $opt;
- if (!empty($opt->choices) && $opt->add_list_option) {
- $this->addOption('list_' . $opt->name, array(
- 'long_name' => '--list-' . $opt->name,
- 'description' => $this->message_provider->get(
- 'LIST_OPTION_MESSAGE',
- array('name' => $opt->name)
- ),
- 'action' => 'List',
- 'action_params' => array('list' => $opt->choices),
- ));
- }
- return $opt;
- }
-
- // }}}
- // displayError() {{{
-
- /**
- * Displays an error to the user via stderr and exit with $exitCode if its
- * value is not equals to false.
- *
- * @param string $error The error message
- * @param int $exitCode The exit code number (default: 1). If set to
- * false, the exit() function will not be called
- *
- * @return void
- */
- public function displayError($error, $exitCode = 1)
- {
- $this->outputter->stderr($this->renderer->error($error));
- if ($exitCode !== false) {
- exit($exitCode);
- }
- }
-
- // }}}
- // displayUsage() {{{
-
- /**
- * Displays the usage help message to the user via stdout and exit with
- * $exitCode if its value is not equals to false.
- *
- * @param int $exitCode The exit code number (default: 0). If set to
- * false, the exit() function will not be called
- *
- * @return void
- */
- public function displayUsage($exitCode = 0)
- {
- $this->outputter->stdout($this->renderer->usage());
- if ($exitCode !== false) {
- exit($exitCode);
- }
- }
-
- // }}}
- // displayVersion() {{{
-
- /**
- * Displays the program version to the user via stdout and exit with
- * $exitCode if its value is not equals to false.
- *
- *
- * @param int $exitCode The exit code number (default: 0). If set to
- * false, the exit() function will not be called
- *
- * @return void
- */
- public function displayVersion($exitCode = 0)
- {
- $this->outputter->stdout($this->renderer->version());
- if ($exitCode !== false) {
- exit($exitCode);
- }
- }
-
- // }}}
- // findOption() {{{
-
- /**
- * Finds the option that matches the given short_name (ex: -v), long_name
- * (ex: --verbose) or name (ex: verbose).
- *
- * @param string $str The option identifier
- *
- * @return mixed A Console_CommandLine_Option instance or false
- */
- public function findOption($str)
- {
- $str = trim($str);
- if ($str === '') {
- return false;
- }
- $matches = array();
- foreach ($this->options as $opt) {
- if ($opt->short_name == $str || $opt->long_name == $str ||
- $opt->name == $str) {
- // exact match
- return $opt;
- }
- if (substr($opt->long_name, 0, strlen($str)) === $str) {
- // abbreviated long option
- $matches[] = $opt;
- }
- }
- if ($count = count($matches)) {
- if ($count > 1) {
- $matches_str = '';
- $padding = '';
- foreach ($matches as $opt) {
- $matches_str .= $padding . $opt->long_name;
- $padding = ', ';
- }
- throw Console_CommandLine_Exception::factory(
- 'OPTION_AMBIGUOUS',
- array('name' => $str, 'matches' => $matches_str),
- $this,
- $this->messages
- );
- }
- return $matches[0];
- }
- return false;
- }
- // }}}
- // registerAction() {{{
-
- /**
- * Registers a custom action for the parser, an example:
- *
- * <code>
- *
- * // in this example we create a "range" action:
- * // the user will be able to enter something like:
- * // $ <program> -r 1,5
- * // and in the result we will have:
- * // $result->options['range']: array(1, 5)
- *
- * require_once 'Console/CommandLine.php';
- * require_once 'Console/CommandLine/Action.php';
- *
- * class ActionRange extends Console_CommandLine_Action
- * {
- * public function execute($value=false, $params=array())
- * {
- * $range = explode(',', str_replace(' ', '', $value));
- * if (count($range) != 2) {
- * throw new Exception(sprintf(
- * 'Option "%s" must be 2 integers separated by a comma',
- * $this->option->name
- * ));
- * }
- * $this->setResult($range);
- * }
- * }
- * // then we can register our action
- * Console_CommandLine::registerAction('Range', 'ActionRange');
- * // and now our action is available !
- * $parser = new Console_CommandLine();
- * $parser->addOption('range', array(
- * 'short_name' => '-r',
- * 'long_name' => '--range',
- * 'action' => 'Range', // note our custom action
- * 'description' => 'A range of two integers separated by a comma'
- * ));
- * // etc...
- *
- * </code>
- *
- * @param string $name The name of the custom action
- * @param string $class The class name of the custom action
- *
- * @return void
- */
- public static function registerAction($name, $class)
- {
- if (!isset(self::$actions[$name])) {
- if (!class_exists($class)) {
- self::triggerError('action_class_does_not_exists',
- E_USER_ERROR,
- array('{$name}' => $name, '{$class}' => $class));
- }
- self::$actions[$name] = array($class, false);
- }
- }
-
- // }}}
- // triggerError() {{{
-
- /**
- * A wrapper for programming errors triggering.
- *
- * @param string $msgId Identifier of the message
- * @param int $level The php error level
- * @param array $params An array of search=>replaces entries
- *
- * @return void
- * @todo remove Console::triggerError() and use exceptions only
- */
- public static function triggerError($msgId, $level, $params=array())
- {
- if (isset(self::$errors[$msgId])) {
- $msg = str_replace(array_keys($params),
- array_values($params), self::$errors[$msgId]);
- trigger_error($msg, $level);
- } else {
- trigger_error('unknown error', $level);
- }
- }
-
- // }}}
- // parse() {{{
-
- /**
- * Parses the command line arguments and returns a
- * Console_CommandLine_Result instance.
- *
- * @param integer $userArgc Number of arguments (optional)
- * @param array $userArgv Array containing arguments (optional)
- *
- * @return Console_CommandLine_Result The result instance
- * @throws Exception on user errors
- */
- public function parse($userArgc=null, $userArgv=null)
- {
- $this->_lastopt = false;
- $this->_stopflag = false;
-
- $this->addBuiltinOptions();
- if ($userArgc !== null && $userArgv !== null) {
- $argc = $userArgc;
- $argv = $userArgv;
- } else {
- list($argc, $argv) = $this->getArgcArgv();
- }
- // build an empty result
- include_once 'Console/CommandLine/Result.php';
- $result = new Console_CommandLine_Result();
- if (!($this instanceof Console_CommandLine_Command)) {
- // remove script name if we're not in a subcommand
- array_shift($argv);
- $argc--;
- }
- // will contain arguments
- $args = array();
- foreach ($this->options as $name=>$option) {
- $result->options[$name] = $option->default;
- }
- // parse command line tokens
- while ($argc--) {
- $token = array_shift($argv);
- try {
- if ($cmd = $this->_getSubCommand($token)) {
- $result->command_name = $cmd->name;
- $result->command = $cmd->parse($argc, $argv);
- break;
- } else {
- $this->parseToken($token, $result, $args, $argc);
- }
- } catch (Exception $exc) {
- throw $exc;
- }
- }
- // Parse a null token to allow any undespatched actions to be despatched.
- $this->parseToken(null, $result, $args, 0);
- // Check if an invalid subcommand was specified. If there are
- // subcommands and no arguments, but an argument was provided, it is
- // an invalid subcommand.
- if ( count($this->commands) > 0
- && count($this->args) === 0
- && count($args) > 0
- ) {
- throw Console_CommandLine_Exception::factory(
- 'INVALID_SUBCOMMAND',
- array('command' => $args[0]),
- $this,
- $this->messages
- );
- }
- // if subcommand_required is set to true we must check that we have a
- // subcommand.
- if ( count($this->commands)
- && $this->subcommand_required
- && !$result->command_name
- ) {
- throw Console_CommandLine_Exception::factory(
- 'SUBCOMMAND_REQUIRED',
- array('commands' => implode(array_keys($this->commands), ', ')),
- $this,
- $this->messages
- );
- }
- // minimum argument number check
- $argnum = 0;
- foreach ($this->args as $name=>$arg) {
- if (!$arg->optional) {
- $argnum++;
- }
- }
- if (count($args) < $argnum) {
- throw Console_CommandLine_Exception::factory(
- 'ARGUMENT_REQUIRED',
- array('argnum' => $argnum, 'plural' => $argnum>1 ? 's': ''),
- $this,
- $this->messages
- );
- }
- // handle arguments
- $c = count($this->args);
- foreach ($this->args as $name=>$arg) {
- $c--;
- if ($arg->multiple) {
- $result->args[$name] = $c ? array_splice($args, 0, -$c) : $args;
- } else {
- $result->args[$name] = array_shift($args);
- }
- if (!$result->args[$name] && $arg->optional && $arg->default) {
- $result->args[$name] = $arg->default;
- }
- // check value is in argument choices
- if (!empty($this->args[$name]->choices)) {
- foreach ($result->args[$name] as $value) {
- if (!in_array($value, $arg->choices)) {
- throw Console_CommandLine_Exception::factory(
- 'ARGUMENT_VALUE_NOT_VALID',
- array(
- 'name' => $name,
- 'choices' => implode('", "', $arg->choices),
- 'value' => implode(' ', $result->args[$name]),
- ),
- $this,
- $arg->messages
- );
- }
- }
- }
- }
- // dispatch deferred options
- foreach ($this->_dispatchLater as $optArray) {
- $optArray[0]->dispatchAction($optArray[1], $optArray[2], $this);
- }
- return $result;
- }
-
- // }}}
- // parseToken() {{{
-
- /**
- * Parses the command line token and modifies *by reference* the $options
- * and $args arrays.
- *
- * @param string $token The command line token to parse
- * @param object $result The Console_CommandLine_Result instance
- * @param array &$args The argv array
- * @param int $argc Number of lasting args
- *
- * @return void
- * @access protected
- * @throws Exception on user errors
- */
- protected function parseToken($token, $result, &$args, $argc)
- {
- $last = $argc === 0;
- if (!$this->_stopflag && $this->_lastopt) {
- if (strlen($token) > ($this->avoid_reading_stdin ? 1 : 0) &&
- substr($token, 0, 1) == '-') {
- if ($this->_lastopt->argument_optional) {
- $this->_dispatchAction($this->_lastopt, '', $result);
- if ($this->_lastopt->action != 'StoreArray') {
- $this->_lastopt = false;
- }
- } else if (isset($result->options[$this->_lastopt->name])) {
- // case of an option that expect a list of args
- $this->_lastopt = false;
- } else {
- throw Console_CommandLine_Exception::factory(
- 'OPTION_VALUE_REQUIRED',
- array('name' => $this->_lastopt->name),
- $this,
- $this->messages
- );
- }
- } else {
- // when a StoreArray option is positioned last, the behavior
- // is to consider that if there's already an element in the
- // array, and the commandline expects one or more args, we
- // leave last tokens to arguments
- if ($this->_lastopt->action == 'StoreArray'
- && !empty($result->options[$this->_lastopt->name])
- && count($this->args) > ($argc + count($args))
- ) {
- if (!is_null($token)) {
- $args[] = $token;
- }
- return;
- }
- if (!is_null($token) || $this->_lastopt->action == 'Password') {
- $this->_dispatchAction($this->_lastopt, $token, $result);
- }
- if ($this->_lastopt->action != 'StoreArray') {
- $this->_lastopt = false;
- }
- return;
- }
- }
- if (!$this->_stopflag && substr($token, 0, 2) == '--') {
- // a long option
- $optkv = explode('=', $token, 2);
- if (trim($optkv[0]) == '--') {
- // the special argument "--" forces in all cases the end of
- // option scanning.
- $this->_stopflag = true;
- return;
- }
- $opt = $this->findOption($optkv[0]);
- if (!$opt) {
- throw Console_CommandLine_Exception::factory(
- 'OPTION_UNKNOWN',
- array('name' => $optkv[0]),
- $this,
- $this->messages
- );
- }
- $value = isset($optkv[1]) ? $optkv[1] : false;
- if (!$opt->expectsArgument() && $value !== false) {
- throw Console_CommandLine_Exception::factory(
- 'OPTION_VALUE_UNEXPECTED',
- array('name' => $opt->name, 'value' => $value),
- $this,
- $this->messages
- );
- }
- if ($opt->expectsArgument() && $value === false) {
- // maybe the long option argument is separated by a space, if
- // this is the case it will be the next arg
- if ($last && !$opt->argument_optional) {
- throw Console_CommandLine_Exception::factory(
- 'OPTION_VALUE_REQUIRED',
- array('name' => $opt->name),
- $this,
- $this->messages
- );
- }
- // we will have a value next time
- $this->_lastopt = $opt;
- return;
- }
- if ($opt->action == 'StoreArray') {
- $this->_lastopt = $opt;
- }
- $this->_dispatchAction($opt, $value, $result);
- } else if (!$this->_stopflag &&
- strlen($token) > ($this->avoid_reading_stdin ? 1 : 0) &&
- substr($token, 0, 1) == '-') {
- // a short option
- $optname = substr($token, 0, 2);
- if ($optname == '-' && !$this->avoid_reading_stdin) {
- // special case of "-": try to read stdin
- $args[] = file_get_contents('php://stdin');
- return;
- }
- $opt = $this->findOption($optname);
- if (!$opt) {
- throw Console_CommandLine_Exception::factory(
- 'OPTION_UNKNOWN',
- array('name' => $optname),
- $this,
- $this->messages
- );
- }
- // parse other options or set the value
- // in short: handle -f<value> and -f <value>
- $next = substr($token, 2, 1);
- // check if we must wait for a value
- if (!$next) {
- if ($opt->expectsArgument()) {
- if ($last && !$opt->argument_optional) {
- throw Console_CommandLine_Exception::factory(
- 'OPTION_VALUE_REQUIRED',
- array('name' => $opt->name),
- $this,
- $this->messages
- );
- }
- // we will have a value next time
- $this->_lastopt = $opt;
- return;
- }
- $value = false;
- } else {
- if (!$opt->expectsArgument()) {
- if ($nextopt = $this->findOption('-' . $next)) {
- $this->_dispatchAction($opt, false, $result);
- $this->parseToken('-' . substr($token, 2), $result,
- $args, $last);
- return;
- } else {
- throw Console_CommandLine_Exception::factory(
- 'OPTION_UNKNOWN',
- array('name' => $next),
- $this,
- $this->messages
- );
- }
- }
- if ($opt->action == 'StoreArray') {
- $this->_lastopt = $opt;
- }
- $value = substr($token, 2);
- }
- $this->_dispatchAction($opt, $value, $result);
- } else {
- // We have an argument.
- // if we are in POSIX compliant mode, we must set the stop flag to
- // true in order to stop option parsing.
- if (!$this->_stopflag && $this->force_posix) {
- $this->_stopflag = true;
- }
- if (!is_null($token)) {
- $args[] = $token;
- }
- }
- }
-
- // }}}
- // addBuiltinOptions() {{{
-
- /**
- * Adds the builtin "Help" and "Version" options if needed.
- *
- * @return void
- */
- public function addBuiltinOptions()
- {
- if ($this->add_help_option) {
- $helpOptionParams = array(
- 'long_name' => '--help',
- 'description' => 'show this help message and exit',
- 'action' => 'Help'
- );
- if (!($option = $this->findOption('-h')) || $option->action == 'Help') {
- // short name is available, take it
- $helpOptionParams['short_name'] = '-h';
- }
- $this->addOption('help', $helpOptionParams);
- }
- if ($this->add_version_option && !empty($this->version)) {
- $versionOptionParams = array(
- 'long_name' => '--version',
- 'description' => 'show the program version and exit',
- 'action' => 'Version'
- );
- if (!$this->findOption('-v')) {
- // short name is available, take it
- $versionOptionParams['short_name'] = '-v';
- }
- $this->addOption('version', $versionOptionParams);
- }
- }
-
- // }}}
- // getArgcArgv() {{{
-
- /**
- * Tries to return an array containing argc and argv, or trigger an error
- * if it fails to get them.
- *
- * @return array The argc/argv array
- * @throws Console_CommandLine_Exception
- */
- protected function getArgcArgv()
- {
- if (php_sapi_name() != 'cli') {
- // we have a web request
- $argv = array($this->name);
- if (isset($_REQUEST)) {
- foreach ($_REQUEST as $key => $value) {
- if (!is_array($value)) {
- $value = array($value);
- }
- $opt = $this->findOption($key);
- if ($opt instanceof Console_CommandLine_Option) {
- // match a configured option
- $argv[] = $opt->short_name ?
- $opt->short_name : $opt->long_name;
- foreach ($value as $v) {
- if ($opt->expectsArgument()) {
- $argv[] = isset($_REQUEST[$key]) ? urldecode($v) : $v;
- } else if ($v == '0' || $v == 'false') {
- array_pop($argv);
- }
- }
- } else if (isset($this->args[$key])) {
- // match a configured argument
- foreach ($value as $v) {
- $argv[] = isset($_REQUEST[$key]) ? urldecode($v) : $v;
- }
- }
- }
- }
- return array(count($argv), $argv);
- }
- if (isset($argc) && isset($argv)) {
- // case of register_argv_argc = 1
- return array($argc, $argv);
- }
- if (isset($_SERVER['argc']) && isset($_SERVER['argv'])) {
- return array($_SERVER['argc'], $_SERVER['argv']);
- }
- return array(0, array());
- }
-
- // }}}
- // _dispatchAction() {{{
-
- /**
- * Dispatches the given option or store the option to dispatch it later.
- *
- * @param Console_CommandLine_Option $option The option instance
- * @param string $token Command line token to parse
- * @param Console_CommandLine_Result $result The result instance
- *
- * @return void
- */
- private function _dispatchAction($option, $token, $result)
- {
- if ($option->action == 'Password') {
- $this->_dispatchLater[] = array($option, $token, $result);
- } else {
- $option->dispatchAction($token, $result, $this);
- }
- }
- // }}}
- // _getSubCommand() {{{
-
- /**
- * Tries to return the subcommand that matches the given token or returns
- * false if no subcommand was found.
- *
- * @param string $token Current command line token
- *
- * @return mixed An instance of Console_CommandLine_Command or false
- */
- private function _getSubCommand($token)
- {
- foreach ($this->commands as $cmd) {
- if ($cmd->name == $token || in_array($token, $cmd->aliases)) {
- return $cmd;
- }
- }
- return false;
- }
-
- // }}}
- }
|