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