PageRenderTime 67ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/script/lib/ezc/ConsoleTools/input.php

https://bitbucket.org/renaatdemuynck/chamilo
PHP | 1381 lines | 603 code | 79 blank | 699 comment | 93 complexity | 4dcc1921e31418f1789f96a382f7a592 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT, GPL-2.0
  1. <?php
  2. /**
  3. * File containing the ezcConsoleInput class.
  4. *
  5. * @package ConsoleTools
  6. * @version 1.6.1
  7. * @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved.
  8. * @license http://ez.no/licenses/new_bsd New BSD License
  9. * @filesource
  10. */
  11. /**
  12. * The ezcConsoleInput class handles the given options and arguments on the console.
  13. *
  14. * This class allows the complete handling of options and arguments submitted
  15. * to a console based application.
  16. *
  17. * The next example demonstrate how to capture the console options:
  18. *
  19. * <code>
  20. * $optionHandler = new ezcConsoleInput();
  21. *
  22. * // Register simple parameter -h/--help
  23. * $optionHandler->registerOption( new ezcConsoleOption( 'h', 'help' ) );
  24. *
  25. * // Register complex parameter -f/--file
  26. * $file = new ezcConsoleOption(
  27. * 'f',
  28. * 'file',
  29. * ezcConsoleInput::TYPE_STRING,
  30. * null,
  31. * false,
  32. * 'Process a file.',
  33. * 'Processes a single file.'
  34. * );
  35. * $optionHandler->registerOption( $file );
  36. *
  37. * // Manipulate parameter -f/--file after registration
  38. * $file->multiple = true;
  39. *
  40. * // Register another complex parameter that depends on -f and excludes -h
  41. * $dir = new ezcConsoleOption(
  42. * 'd',
  43. * 'dir',
  44. * ezcConsoleInput::TYPE_STRING,
  45. * null,
  46. * true,
  47. * 'Process a directory.',
  48. * 'Processes a complete directory.',
  49. * array( new ezcConsoleOptionRule( $optionHandler->getOption( 'f' ) ) ),
  50. * array( new ezcConsoleOptionRule( $optionHandler->getOption( 'h' ) ) )
  51. * );
  52. * $optionHandler->registerOption( $dir );
  53. *
  54. * // Register an alias for this parameter
  55. * $optionHandler->registerAlias( 'e', 'extended-dir', $dir );
  56. *
  57. * // Process registered parameters and handle errors
  58. * try
  59. * {
  60. * $optionHandler->process( array( 'example_input.php', '-h' ) );
  61. * }
  62. * catch ( ezcConsoleOptionException $e )
  63. * {
  64. * echo $e->getMessage();
  65. * exit( 1 );
  66. * }
  67. *
  68. * // Process a single parameter
  69. * $file = $optionHandler->getOption( 'f' );
  70. * if ( $file->value === false )
  71. * {
  72. * echo "Parameter -{$file->short}/--{$file->long} was not submitted.\n";
  73. * }
  74. * elseif ( $file->value === true )
  75. * {
  76. * echo "Parameter -{$file->short}/--{$file->long} was submitted without value.\n";
  77. * }
  78. * else
  79. * {
  80. * echo "Parameter -{$file->short}/--{$file->long} was submitted with value '".var_export($file->value, true)."'.\n";
  81. * }
  82. *
  83. * // Process all parameters at once:
  84. * foreach ( $optionHandler->getOptionValues() as $paramShort => $val )
  85. * {
  86. * switch ( true )
  87. * {
  88. * case $val === false:
  89. * echo "Parameter $paramShort was not submitted.\n";
  90. * break;
  91. * case $val === true:
  92. * echo "Parameter $paramShort was submitted without a value.\n";
  93. * break;
  94. * case is_array( $val ):
  95. * echo "Parameter $paramShort was submitted multiple times with value: '".implode(', ', $val)."'.\n";
  96. * break;
  97. * default:
  98. * echo "Parameter $paramShort was submitted with value: '$val'.\n";
  99. * break;
  100. * }
  101. * }
  102. * </code>
  103. *
  104. * @package ConsoleTools
  105. * @version 1.6.1
  106. * @mainclass
  107. *
  108. * @property ezcConsoleArguments $argumentDefinition Optional argument definition.
  109. */
  110. class ezcConsoleInput
  111. {
  112. /**
  113. * Option does not carry a value.
  114. */
  115. const TYPE_NONE = 1;
  116. /**
  117. * Option takes an integer value.
  118. */
  119. const TYPE_INT = 2;
  120. /**
  121. * Option takes a string value.
  122. */
  123. const TYPE_STRING = 3;
  124. /**
  125. * Array of option definitions, indexed by number.
  126. *
  127. * This array stores the ezcConsoleOption objects representing
  128. * the options.
  129. *
  130. * For lookup of an option after its short or long values the attributes
  131. * {@link ezcConsoleInput::$optionShort}
  132. * {@link ezcConsoleInput::$optionLong}
  133. * are used.
  134. *
  135. * @var array(array)
  136. */
  137. private $options = array();
  138. /**
  139. * Short option names.
  140. *
  141. * Each references a key in {@link ezcConsoleInput::$options}.
  142. *
  143. * @var array(string=>int)
  144. */
  145. private $optionShort = array();
  146. /**
  147. * Long option names.
  148. *
  149. * Each references a key in {@link ezcConsoleInput::$options}.
  150. *
  151. * @var array(string=>int)
  152. */
  153. private $optionLong = array();
  154. /**
  155. * Arguments, if submitted, are stored here.
  156. *
  157. * @var array(string)
  158. */
  159. private $arguments = array();
  160. /**
  161. * Wether the process() method has already been called.
  162. *
  163. * @var bool
  164. */
  165. private $processed = false;
  166. /**
  167. * Indicates if an option was submitted, that has the isHelpOption flag set.
  168. *
  169. * @var bool
  170. */
  171. private $helpOptionSet = false;
  172. /**
  173. * Tool object for multi-byte encoding safe string operations.
  174. *
  175. * @var ezcConsoleStringTool
  176. */
  177. private $stringTool;
  178. /**
  179. * Input validator.
  180. *
  181. * @var ezcConsoleInputValidator
  182. */
  183. private $validator;
  184. /**
  185. * Help generator.
  186. *
  187. * @var ezcConsoleInputHelpGenerator
  188. */
  189. private $helpGenerator;
  190. /**
  191. * Collection of properties.
  192. *
  193. * @var array(string=>mixed)
  194. */
  195. protected $properties = array();
  196. /**
  197. * Creates an input handler.
  198. */
  199. public function __construct()
  200. {
  201. $this->argumentDefinition = null;
  202. $this->stringTool = new ezcConsoleStringTool();
  203. // @TODO Verify interface and make plugable
  204. $this->validator = new ezcConsoleStandardInputValidator();
  205. $this->helpGenerator = new ezcConsoleInputStandardHelpGenerator($this);
  206. }
  207. /**
  208. * Registers the new option $option.
  209. *
  210. * This method adds the new option $option to your option collection. If
  211. * already an option with the assigned short or long value exists, an
  212. * exception will be thrown.
  213. *
  214. * @see ezcConsoleInput::unregisterOption()
  215. *
  216. * @param ezcConsoleOption $option
  217. *
  218. * @return ezcConsoleOption The recently registered option.
  219. */
  220. public function registerOption(ezcConsoleOption $option)
  221. {
  222. foreach ($this->optionShort as $short => $ref)
  223. {
  224. if ($short === $option->short)
  225. {
  226. throw new ezcConsoleOptionAlreadyRegisteredException($short);
  227. }
  228. }
  229. foreach ($this->optionLong as $long => $ref)
  230. {
  231. if ($long === $option->long)
  232. {
  233. throw new ezcConsoleOptionAlreadyRegisteredException($long);
  234. }
  235. }
  236. $this->options[] = $option;
  237. $this->optionLong[$option->long] = $option;
  238. if ($option->short !== "")
  239. {
  240. $this->optionShort[$option->short] = $option;
  241. }
  242. return $option;
  243. }
  244. /**
  245. * Registers an alias for an option.
  246. *
  247. * Registers a new alias for an existing option. Aliases can
  248. * be used as if they were a normal option.
  249. *
  250. * The alias is registered with the short option name $short and the
  251. * long option name $long. The alias references to the existing
  252. * option $option.
  253. *
  254. * @see ezcConsoleInput::unregisterAlias()
  255. *
  256. * @param string $short
  257. * @param string $long
  258. * @param ezcConsoleOption $option
  259. *
  260. *
  261. * @throws ezcConsoleOptionNotExistsException
  262. * If the referenced option is not registered.
  263. * @throws ezcConsoleOptionAlreadyRegisteredException
  264. * If another option/alias has taken the provided short or long name.
  265. * @return void
  266. */
  267. public function registerAlias($short, $long, ezcConsoleOption $option)
  268. {
  269. if (! isset($this->optionShort[$option->short]) || ! isset($this->optionLong[$option->long]))
  270. {
  271. throw new ezcConsoleOptionNotExistsException($option->long);
  272. }
  273. if (isset($this->optionShort[$short]) || isset($this->optionLong[$long]))
  274. {
  275. throw new ezcConsoleOptionAlreadyRegisteredException(isset($this->optionShort[$short]) ? "-$short" : "--$long");
  276. }
  277. $this->optionShort[$short] = $option;
  278. $this->optionLong[$long] = $option;
  279. }
  280. /**
  281. * Registers options according to a string specification.
  282. *
  283. * Accepts a string to define parameters and registers all parameters as
  284. * options accordingly. String definition, specified in $optionDef, looks
  285. * like this:
  286. *
  287. * <code>
  288. * [s:|size:][u:|user:][a:|all:]
  289. * </code>
  290. *
  291. * This string registers 3 parameters:
  292. * -s / --size
  293. * -u / --user
  294. * -a / --all
  295. *
  296. * @param string $optionDef
  297. * @return void
  298. *
  299. * @throws ezcConsoleOptionStringNotWellformedException
  300. * If provided string does not have the correct format.
  301. */
  302. public function registerOptionString($optionDef)
  303. {
  304. $regex = '\[([a-z0-9-]+)([:?*+])?([^|]*)\|([a-z0-9-]+)([:?*+])?\]';
  305. // Check string for wellformedness
  306. if (preg_match("/^($regex)+$/", $optionDef) == 0)
  307. {
  308. throw new ezcConsoleOptionStringNotWellformedException("Option definition not wellformed: \"$optionDef\"");
  309. }
  310. if (preg_match_all("/$regex/", $optionDef, $matches))
  311. {
  312. foreach ($matches[1] as $id => $short)
  313. {
  314. $option = null;
  315. $option = new ezcConsoleOption($short, $matches[4][$id]);
  316. if (! empty($matches[2][$id]) || ! empty($matches[5][$id]))
  317. {
  318. switch (! empty($matches[2][$id]) ? $matches[2][$id] : $matches[5][$id])
  319. {
  320. case '*' :
  321. // Allows 0 or more occurances
  322. $option->multiple = true;
  323. break;
  324. case '+' :
  325. // Allows 1 or more occurances
  326. $option->multiple = true;
  327. $option->type = self :: TYPE_STRING;
  328. break;
  329. case '?' :
  330. $option->type = self :: TYPE_STRING;
  331. $option->default = '';
  332. break;
  333. default :
  334. break;
  335. }
  336. }
  337. if (! empty($matches[3][$id]))
  338. {
  339. $option->default = $matches[3][$id];
  340. }
  341. $this->registerOption($option);
  342. }
  343. }
  344. }
  345. /**
  346. * Removes an option.
  347. *
  348. * This function removes an option. All dependencies to that
  349. * specific option are removed completely from every other registered
  350. * option.
  351. *
  352. * @see ezcConsoleInput::registerOption()
  353. *
  354. * @param ezcConsoleOption $option The option object to unregister.
  355. *
  356. * @throws ezcConsoleOptionNotExistsException
  357. * If requesting a not registered option.
  358. * @return void
  359. */
  360. public function unregisterOption(ezcConsoleOption $option)
  361. {
  362. $found = false;
  363. foreach ($this->options as $id => $existParam)
  364. {
  365. if ($existParam === $option)
  366. {
  367. $found = true;
  368. unset($this->options[$id]);
  369. continue;
  370. }
  371. $existParam->removeAllExclusions($option);
  372. $existParam->removeAllDependencies($option);
  373. }
  374. if ($found === false)
  375. {
  376. throw new ezcConsoleOptionNotExistsException($option->long);
  377. }
  378. foreach ($this->optionLong as $name => $existParam)
  379. {
  380. if ($existParam === $option)
  381. {
  382. unset($this->optionLong[$name]);
  383. }
  384. }
  385. foreach ($this->optionShort as $name => $existParam)
  386. {
  387. if ($existParam === $option)
  388. {
  389. unset($this->optionShort[$name]);
  390. }
  391. }
  392. }
  393. /**
  394. * Removes an alias to an option.
  395. *
  396. * This function removes an alias with the short name $short and long
  397. * name $long.
  398. *
  399. * @see ezcConsoleInput::registerAlias()
  400. *
  401. * @throws ezcConsoleOptionNoAliasException
  402. * If the requested short/long name belongs to a real parameter instead.
  403. *
  404. * @param string $short
  405. * @param string $long
  406. * @return void
  407. *
  408. * @todo Check if $short and $long refer to the same option!
  409. */
  410. public function unregisterAlias($short, $long)
  411. {
  412. foreach ($this->options as $id => $option)
  413. {
  414. if ($option->short === $short)
  415. {
  416. throw new ezcConsoleOptionNoAliasException($short);
  417. }
  418. if ($option->long === $long)
  419. {
  420. throw new ezcConsoleOptionNoAliasException($long);
  421. }
  422. }
  423. if (isset($this->optionShort[$short]))
  424. {
  425. unset($this->optionShort[$short]);
  426. }
  427. if (isset($this->optionLong[$long]))
  428. {
  429. unset($this->optionLong[$long]);
  430. }
  431. }
  432. /**
  433. * Returns the definition object for the option with the name $name.
  434. *
  435. * This method receives the long or short name of an option and
  436. * returns the ezcConsoleOption object.
  437. *
  438. * @param string $name Short or long name of the option (without - or --).
  439. * @return ezcConsoleOption
  440. *
  441. * @throws ezcConsoleOptionNotExistsException
  442. * If requesting a not registered parameter.
  443. */
  444. public function getOption($name)
  445. {
  446. $name = $name;
  447. if (isset($this->optionShort[$name]))
  448. {
  449. return $this->optionShort[$name];
  450. }
  451. if (isset($this->optionLong[$name]))
  452. {
  453. return $this->optionLong[$name];
  454. }
  455. throw new ezcConsoleOptionNotExistsException($name);
  456. }
  457. /**
  458. * Process the input parameters.
  459. *
  460. * Actually process the input options and arguments according to the actual
  461. * settings.
  462. *
  463. * Per default this method uses $argc and $argv for processing. You can
  464. * override this setting with your own input, if necessary, using the
  465. * parameters of this method. (Attention, first argument is always the pro
  466. * gram name itself!)
  467. *
  468. * All exceptions thrown by this method contain an additional attribute "option"
  469. * which specifies the parameter on which the error occurred.
  470. *
  471. * @param array(string) $args The arguments
  472. * @return void
  473. *
  474. * @throws ezcConsoleOptionNotExistsException
  475. * If an option that was submitted does not exist.
  476. * @throws ezcConsoleOptionDependencyViolationException
  477. * If a dependency rule was violated.
  478. * @throws ezcConsoleOptionExclusionViolationException
  479. * If an exclusion rule was violated.
  480. * @throws ezcConsoleOptionTypeViolationException
  481. * If the type of a submitted value violates the options type rule.
  482. * @throws ezcConsoleOptionArgumentsViolationException
  483. * If arguments are passed although a parameter disallowed them.
  484. *
  485. * @see ezcConsoleOptionException
  486. */
  487. public function process(array $args = null)
  488. {
  489. if ($this->processed)
  490. {
  491. $this->reset();
  492. }
  493. $this->processed = true;
  494. if (! isset($args))
  495. {
  496. $args = isset($argv) ? $argv : isset($_SERVER['argv']) ? $_SERVER['argv'] : array();
  497. }
  498. $nextIndex = $this->processOptions($args);
  499. if ($this->helpOptionSet())
  500. {
  501. // No need to parse arguments
  502. return;
  503. }
  504. $this->processArguments($args, $nextIndex);
  505. $this->checkRules();
  506. $this->setOptionDefaults();
  507. }
  508. /**
  509. * Sets defaults for options that have not been submitted.
  510. *
  511. * Checks all options if they have been submited. If not and a default
  512. * values is present, this is set as the options value.
  513. */
  514. private function setOptionDefaults()
  515. {
  516. foreach ($this->options as $option)
  517. {
  518. if ($option->value === false || $option->value === array())
  519. {
  520. // Default value to set?
  521. if ($option->default !== null)
  522. {
  523. $option->value = $option->default;
  524. }
  525. }
  526. }
  527. }
  528. /**
  529. * Reads the submitted options from $args array.
  530. *
  531. * Returns the next index to check for arguments.
  532. *
  533. * @param array(string) $args
  534. * @returns int
  535. *
  536. * @throws ezcConsoleOptionNotExistsException
  537. * if a submitted option does not exist.
  538. * @throws ezcConsoleOptionTooManyValuesException
  539. * if an option that expects only a single value was submitted
  540. * with multiple values.
  541. * @throws ezcConsoleOptionTypeViolationException
  542. * if an option was submitted with a value of the wrong type.
  543. * @throws ezcConsoleOptionMissingValueException
  544. * if an option thats expects a value was submitted without.
  545. */
  546. private function processOptions(array $args)
  547. {
  548. $numArgs = count($args);
  549. $i = 1;
  550. while ($i < $numArgs)
  551. {
  552. if ($args[$i] === '--')
  553. {
  554. break;
  555. }
  556. // Equalize parameter handling (long params with =)
  557. if (iconv_substr($args[$i], 0, 2, 'UTF-8') == '--')
  558. {
  559. $this->preprocessLongOption($args, $i);
  560. // Update number of args, changed by preprocessLongOption()
  561. $numArgs = count($args);
  562. }
  563. // Check for parameter
  564. if (iconv_substr($args[$i], 0, 1, 'UTF-8') === '-')
  565. {
  566. if (! $this->hasOption(preg_replace('/^-*/', '', $args[$i])))
  567. {
  568. throw new ezcConsoleOptionNotExistsException($args[$i]);
  569. }
  570. $this->processOption($args, $i);
  571. }
  572. // Must be the arguments
  573. else
  574. {
  575. break;
  576. }
  577. }
  578. // Move pointer over argument sign
  579. isset($args[$i]) && $args[$i] == '--' ? ++ $i : $i;
  580. return $i;
  581. }
  582. /**
  583. * Resets all option and argument values.
  584. *
  585. * This method is called automatically by {@link process()}, if this method
  586. * is called twice or more, and may also be used to manually reset the
  587. * values of all registered {@ezcConsoleOption} and {@link
  588. * ezcConsoleArgument} objects.
  589. */
  590. public function reset()
  591. {
  592. foreach ($this->options as $option)
  593. {
  594. $option->value = false;
  595. }
  596. if ($this->argumentDefinition !== null)
  597. {
  598. foreach ($this->argumentDefinition as $argument)
  599. {
  600. $argument->value = null;
  601. }
  602. }
  603. $this->arguments = array();
  604. }
  605. /**
  606. * Returns true if an option with the given name exists, otherwise false.
  607. *
  608. * Checks if an option with the given name is registered.
  609. *
  610. * @param string $name Short or long name of the option.
  611. * @return bool True if option exists, otherwise false.
  612. */
  613. public function hasOption($name)
  614. {
  615. try
  616. {
  617. $param = $this->getOption($name);
  618. }
  619. catch (ezcConsoleOptionNotExistsException $e)
  620. {
  621. return false;
  622. }
  623. return true;
  624. }
  625. /**
  626. * Returns an array of all registered options.
  627. *
  628. * Returns an array of all registered options in the following format:
  629. * <code>
  630. * array(
  631. * 0 => ezcConsoleOption,
  632. * 1 => ezcConsoleOption,
  633. * 2 => ezcConsoleOption,
  634. * ...
  635. * );
  636. * </code>
  637. *
  638. * @return array(string=>ezcConsoleOption) Registered options.
  639. */
  640. public function getOptions()
  641. {
  642. return $this->options;
  643. }
  644. /**
  645. * Returns the values of all submitted options.
  646. *
  647. * Returns an array of all values submitted to the options. The array is
  648. * indexed by the parameters short name (excluding the '-' prefix). The array
  649. * does not contain any parameter, which value is 'false' (meaning: the
  650. * parameter was not submitted).
  651. *
  652. * @param bool $longnames Wheather to use longnames for indexing.
  653. * @return array(string=>mixed)
  654. */
  655. public function getOptionValues($longnames = false)
  656. {
  657. $res = array();
  658. foreach ($this->options as $param)
  659. {
  660. if ($param->value !== false)
  661. {
  662. $res[($longnames === true) ? $param->long : $param->short] = $param->value;
  663. }
  664. }
  665. return $res;
  666. }
  667. /**
  668. * Returns arguments provided to the program.
  669. *
  670. * This method returns all arguments provided to a program in an
  671. * int indexed array. Arguments are sorted in the way
  672. * they are submitted to the program. You can disable arguments
  673. * through the 'arguments' flag of a parameter, if you want
  674. * to disallow arguments.
  675. *
  676. * Arguments are either the last part of the program call (if the
  677. * last parameter is not a 'multiple' one) or divided via the '--'
  678. * method which is commonly used on Unix (if the last parameter
  679. * accepts multiple values this is required).
  680. *
  681. * @return array(string) Arguments.
  682. */
  683. public function getArguments()
  684. {
  685. return $this->arguments;
  686. }
  687. /**
  688. * Get help information for your options.
  689. *
  690. * This method returns an array of help information for your options,
  691. * indexed by int. Each help info has 2 fields:
  692. *
  693. * 0 => The options names ("<short> / <long>")
  694. * 1 => The help text (depending on the $long parameter)
  695. *
  696. * The $long options determines if you want to get the short or long help
  697. * texts. The array returned can be used by {@link ezcConsoleTable}.
  698. *
  699. * If using the second options, you can filter the options shown in the
  700. * help output (e.g. to show short help for related options). Provide
  701. * as simple number indexed array of short and/or long values to set a filter.
  702. *
  703. * The $paramGrouping option can be used to group options in the help
  704. * output. The structure of this array parameter is as follows:
  705. *
  706. * <code>
  707. * array(
  708. * 'First section' => array(
  709. * 'input',
  710. * 'output'
  711. * 'overwrite',
  712. * ),
  713. * 'Second section' => array(
  714. * 'v',
  715. * 'h',
  716. * ),
  717. * )
  718. * </code>
  719. *
  720. * As can be seen, short option names are possible as well as long ones.
  721. * The key of the first array level is the name of the section, which is
  722. * assigned to an array of options to group under this section. The $params
  723. * parameter still influences if an option is displayed at all.
  724. *
  725. * @param bool $long
  726. * @param array(string) $params
  727. * @param array(string=>array(string)) $paramGrouping
  728. * @return array(array(string)) Table structure as explained.
  729. *
  730. * @apichange In future versions, the default values of $params will change
  731. * to null instead of an empty array. Giving an empty array for
  732. * these will then be taken literally.
  733. */
  734. public function getHelp($long = false, array $params = array(), array $paramGrouping = null)
  735. {
  736. // New handling
  737. $params = ($params === array() || $params === null ? null : $params);
  738. $help = array();
  739. if ($paramGrouping === null)
  740. {
  741. // Original handling
  742. $help = $this->getOptionHelpWithoutGrouping($long, $params);
  743. }
  744. else
  745. {
  746. $help = $this->getOptionHelpWithGrouping($long, $params, $paramGrouping);
  747. }
  748. if ($this->argumentDefinition !== null)
  749. {
  750. $help[] = array("Arguments:", '');
  751. $argumentsHelp = $this->helpGenerator->generateArgumentHelp($long);
  752. if ($argumentsHelp === array())
  753. {
  754. $help[] = array('', "No arguments available.");
  755. }
  756. else
  757. {
  758. $help = array_merge($help, $argumentsHelp);
  759. }
  760. }
  761. return $help;
  762. }
  763. /**
  764. * Creates the option help array in the original, ungrouped way.
  765. *
  766. * Creates the original help array generated by {@link getHelp()}. The
  767. * $long and $params options are the same as they are for this method.
  768. *
  769. * @param bool $long
  770. * @param array $params
  771. * @return array
  772. */
  773. private function getOptionHelpWithoutGrouping($long, $params)
  774. {
  775. return $this->helpGenerator->generateUngroupedOptionHelp($long, $params);
  776. }
  777. /**
  778. * Generates options helo array with ordering and grouping.
  779. *
  780. * @param mixed $long
  781. * @param mixed $params
  782. * @param mixed $paramGrouping
  783. * @return array()
  784. */
  785. private function getOptionHelpWithGrouping($long, $params, $paramGrouping)
  786. {
  787. $rawHelp = $this->helpGenerator->generateGroupedOptionHelp($paramGrouping, $long, $params);
  788. $help = array();
  789. $first = true;
  790. foreach ($rawHelp as $category => $optionsHelp)
  791. {
  792. if (! $first)
  793. {
  794. $help[] = array('', '');
  795. }
  796. else
  797. {
  798. $first = false;
  799. }
  800. $help[] = array($category, '');
  801. $help = array_merge($help, $optionsHelp);
  802. }
  803. return $help;
  804. }
  805. /**
  806. * Get help information for your options as a table.
  807. *
  808. * This method provides the information returned by
  809. * {@link ezcConsoleInput::getHelp()} in a table.
  810. *
  811. * The $paramGrouping option can be used to group options in the help
  812. * output. The structure of this array parameter is as follows:
  813. *
  814. * <code>
  815. * array(
  816. * 'First section' => array(
  817. * 'input',
  818. * 'output'
  819. * 'overwrite',
  820. * ),
  821. * 'Second section' => array(
  822. * 'v',
  823. * 'h',
  824. * ),
  825. * )
  826. * </code>
  827. *
  828. * As can be seen, short option names are possible as well as long ones.
  829. * The key of the first array level is the name of the section, which is
  830. * assigned to an array of options to group under this section. The $params
  831. * parameter still influences if an option as displayed at all.
  832. *
  833. * @param ezcConsoleTable $table The table object to fill.
  834. * @param bool $long Set this to true for getting the
  835. * long help version.
  836. * @param array(string) $params Set of option names to generate help
  837. * for, default is all.
  838. * @param array(string=>array(string)) $paramGrouping
  839. * @return ezcConsoleTable The filled table.
  840. */
  841. public function getHelpTable(ezcConsoleTable $table, $long = false, array $params = array(), $paramGrouping = null)
  842. {
  843. $help = $this->getHelp($long, $params, $paramGrouping);
  844. $i = 0;
  845. foreach ($help as $row)
  846. {
  847. $table[$i][0]->content = $row[0];
  848. $table[$i ++][1]->content = $row[1];
  849. }
  850. return $table;
  851. }
  852. /**
  853. * Returns a standard help output for your program.
  854. *
  855. * This method generates a help text as it's commonly known from Unix
  856. * command line programs. The output will contain the synopsis, your
  857. * provided program description and the selected parameter help
  858. * as also provided by {@link ezcConsoleInput::getHelp()}. The returned
  859. * string can directly be printed to the console.
  860. *
  861. * The $paramGrouping option can be used to group options in the help
  862. * output. The structure of this array parameter is as follows:
  863. *
  864. * <code>
  865. * array(
  866. * 'First section' => array(
  867. * 'input',
  868. * 'output'
  869. * 'overwrite',
  870. * ),
  871. * 'Second section' => array(
  872. * 'v',
  873. * 'h',
  874. * ),
  875. * )
  876. * </code>
  877. *
  878. * As can be seen, short option names are possible as well as long ones.
  879. * The key of the first array level is the name of the section, which is
  880. * assigned to an array of options to group under this section. The $params
  881. * parameter still influences if an option as displayed at all.
  882. *
  883. * @param string $programDesc The description of your program.
  884. * @param int $width The width to adjust the output text to.
  885. * @param bool $long Set this to true for getting the long
  886. * help version.
  887. * @param array(string) $params Set of option names to generate help
  888. * for, default is all.
  889. * @param array(string=>array(string)) $paramGrouping
  890. * @return string The generated help text.
  891. */
  892. public function getHelpText($programDesc, $width = 80, $long = false, array $params = null, $paramGrouping = null)
  893. {
  894. $help = $this->getHelp($long, ($params == null ? array() : $params), $paramGrouping);
  895. // Determine max length of first column text.
  896. $maxLength = 0;
  897. foreach ($help as $row)
  898. {
  899. $maxLength = max($maxLength, iconv_strlen($row[0], 'UTF-8'));
  900. }
  901. // Width of left column
  902. $leftColWidth = $maxLength + 2;
  903. // Width of righ column
  904. $rightColWidth = $width - $leftColWidth;
  905. $res = 'Usage: ' . $this->getSynopsis($params) . PHP_EOL;
  906. $res .= $this->stringTool->wordwrap($programDesc, $width, PHP_EOL);
  907. $res .= PHP_EOL . PHP_EOL;
  908. foreach ($help as $row)
  909. {
  910. $rowParts = explode("\n", $this->stringTool->wordwrap($row[1], $rightColWidth));
  911. $res .= $this->stringTool->strPad($row[0], $leftColWidth, ' ');
  912. $res .= $rowParts[0] . PHP_EOL;
  913. // @TODO: Fix function call in loop header
  914. for($i = 1; $i < sizeof($rowParts); $i ++)
  915. {
  916. $res .= str_repeat(' ', $leftColWidth) . $rowParts[$i] . PHP_EOL;
  917. }
  918. }
  919. return $res;
  920. }
  921. /**
  922. * Returns the synopsis string for the program.
  923. *
  924. * This gives you a synopsis definition for the options and arguments
  925. * defined with this instance of ezcConsoleInput. You can filter the
  926. * options named in the synopsis by submitting their short names in an
  927. * array as the parameter of this method. If the parameter $optionNames
  928. * is set, only those options are listed in the synopsis.
  929. *
  930. * @param array(string) $optionNames
  931. * @return string
  932. */
  933. public function getSynopsis(array $optionNames = null)
  934. {
  935. return $this->helpGenerator->generateSynopsis($optionNames);
  936. }
  937. /**
  938. * Returns if a help option was set.
  939. * This method returns if an option was submitted, which was defined to be
  940. * a help option, using the isHelpOption flag.
  941. *
  942. * @return bool If a help option was set.
  943. */
  944. public function helpOptionSet()
  945. {
  946. return $this->helpOptionSet;
  947. }
  948. /**
  949. * Property read access.
  950. *
  951. * @throws ezcBasePropertyNotFoundException
  952. * If the the desired property is not found.
  953. *
  954. * @param string $propertyName Name of the property.
  955. * @return mixed Value of the property or null.
  956. * @ignore
  957. */
  958. public function __get($propertyName)
  959. {
  960. if (! isset($this->$propertyName))
  961. {
  962. throw new ezcBasePropertyNotFoundException($propertyName);
  963. }
  964. return $this->properties[$propertyName];
  965. }
  966. /**
  967. * Property set access.
  968. *
  969. * @param string $propertyName
  970. * @param string $propertyValue
  971. * @ignore
  972. * @return void
  973. */
  974. public function __set($propertyName, $propertyValue)
  975. {
  976. switch ($propertyName)
  977. {
  978. case "argumentDefinition" :
  979. if (($propertyValue instanceof ezcConsoleArguments) === false && $propertyValue !== null)
  980. {
  981. throw new ezcBaseValueException($propertyName, $propertyValue, "ezcConsoleArguments");
  982. }
  983. break;
  984. default :
  985. throw new ezcBasePropertyNotFoundException($propertyName);
  986. }
  987. $this->properties[$propertyName] = $propertyValue;
  988. }
  989. /**
  990. * Property isset access.
  991. *
  992. * @param string $propertyName Name of the property.
  993. * @return bool True if the property is set, otherwise false.
  994. * @ignore
  995. */
  996. public function __isset($propertyName)
  997. {
  998. return array_key_exists($propertyName, $this->properties);
  999. }
  1000. /**
  1001. * Returns the synopsis string for a single option and its dependencies.
  1002. *
  1003. * This method returns a part of the program synopsis, specifically for a
  1004. * certain parameter. The method recursively adds depending parameters up
  1005. * to the 2nd depth level to the synopsis. The second parameter is used
  1006. * to store the short names of all options that have already been used in
  1007. * the synopsis (to avoid adding an option twice). The 3rd parameter
  1008. * determines the actual deps in the option dependency recursion to
  1009. * terminate that after 2 recursions.
  1010. *
  1011. * @param ezcConsoleOption $option The option to include.
  1012. * @param array(string) $usedOptions Array of used option short names.
  1013. * @param int $depth Current recursion depth.
  1014. * @return string The synopsis for this parameter.
  1015. *
  1016. * @apichange This method is deprecates. Implement your own {@link
  1017. * ezcConsoleInputHelpGenerator} instead, as soon as the
  1018. * interface is made public.
  1019. */
  1020. protected function createOptionSynopsis(ezcConsoleOption $option, &$usedOptions, $depth = 0)
  1021. {
  1022. $synopsis = '';
  1023. // Break after a nesting level of 2
  1024. if ($depth ++ > 2 || (in_array($option->short, $usedOptions['short']) && in_array($option->long, $usedOptions['long'])))
  1025. return $synopsis;
  1026. $usedOptions['short'][] = $option->short;
  1027. $usedOptions['long'][] = $option->long;
  1028. $synopsis .= $option->short !== "" ? "-{$option->short}" : "--{$option->long}";
  1029. if (isset($option->default))
  1030. {
  1031. $synopsis .= " " . ($option->type === ezcConsoleInput :: TYPE_STRING ? '"' : '') . $option->default . ($option->type === ezcConsoleInput :: TYPE_STRING ? '"' : '');
  1032. }
  1033. else
  1034. if ($option->type !== ezcConsoleInput :: TYPE_NONE)
  1035. {
  1036. $synopsis .= " ";
  1037. switch ($option->type)
  1038. {
  1039. case ezcConsoleInput :: TYPE_STRING :
  1040. $synopsis .= "<string>";
  1041. break;
  1042. case ezcConsoleInput :: TYPE_INT :
  1043. $synopsis .= "<int>";
  1044. break;
  1045. }
  1046. }
  1047. foreach ($option->getDependencies() as $rule)
  1048. {
  1049. $deeperSynopsis = $this->createOptionSynopsis($rule->option, $usedOptions, $depth);
  1050. $synopsis .= (iconv_strlen(trim($deeperSynopsis), 'UTF-8') > 0 ? ' ' . $deeperSynopsis : '');
  1051. }
  1052. if ($option->arguments === false)
  1053. {
  1054. $allowsArgs = false;
  1055. }
  1056. // Make the whole thing optional?
  1057. if ($option->mandatory === false)
  1058. {
  1059. $synopsis = "[$synopsis]";
  1060. }
  1061. return $synopsis . ' ';
  1062. }
  1063. /**
  1064. * Process an option.
  1065. *
  1066. * This method does the processing of a single option.
  1067. *
  1068. * @param array(string) $args The arguments array.
  1069. * @param int $i The current position in the arguments array.
  1070. * @return void
  1071. *
  1072. * @throws ezcConsoleOptionTooManyValuesException
  1073. * If an option that expects only a single value was submitted
  1074. * with multiple values.
  1075. * @throws ezcConsoleOptionTypeViolationException
  1076. * If an option was submitted with a value of the wrong type.
  1077. * @throws ezcConsoleOptionMissingValueException
  1078. * If an option thats expects a value was submitted without.
  1079. */
  1080. private function processOption(array $args, &$i)
  1081. {
  1082. $option = $this->getOption(preg_replace('/^-+/', '', $args[$i ++]));
  1083. // Is the actual option a help option?
  1084. if ($option->isHelpOption === true)
  1085. {
  1086. $this->helpOptionSet = true;
  1087. }
  1088. // No value expected
  1089. if ($option->type === ezcConsoleInput :: TYPE_NONE)
  1090. {
  1091. // No value expected
  1092. if (isset($args[$i]) && iconv_substr($args[$i], 0, 1, 'UTF-8') !== '-' && sizeof($args) > ($i + 1))
  1093. {
  1094. // But one found
  1095. throw new ezcConsoleOptionTypeViolationException($option, $args[$i]);
  1096. }
  1097. // Multiple occurance possible
  1098. if ($option->multiple === true)
  1099. {
  1100. $option->value[] = true;
  1101. }
  1102. else
  1103. {
  1104. $option->value = true;
  1105. }
  1106. // Everything fine, nothing to do
  1107. return $i;
  1108. }
  1109. // Value expected, check for it
  1110. if (isset($args[$i]) && iconv_substr($args[$i], 0, 1, 'UTF-8') !== '-')
  1111. {
  1112. // Type check
  1113. if ($this->isCorrectType($option->type, $args[$i]) === false)
  1114. {
  1115. throw new ezcConsoleOptionTypeViolationException($option, $args[$i]);
  1116. }
  1117. // Multiple values possible
  1118. if ($option->multiple === true)
  1119. {
  1120. $option->value[] = $args[$i];
  1121. }
  1122. // Only single value expected, check for multiple
  1123. elseif (isset($option->value) && $option->value !== false)
  1124. {
  1125. throw new ezcConsoleOptionTooManyValuesException($option);
  1126. }
  1127. else
  1128. {
  1129. $option->value = $args[$i];
  1130. }
  1131. $i ++;
  1132. }
  1133. // Value found? If not, use default, if available
  1134. if (! isset($option->value) || $option->value === false || (is_array($option->value) && count($option->value) === 0))
  1135. {
  1136. throw new ezcConsoleOptionMissingValueException($option);
  1137. }
  1138. }
  1139. /**
  1140. * Process arguments given to the program.
  1141. *
  1142. * @param array(string) $args The arguments array.
  1143. * @param int $i Current index in arguments array.
  1144. * @return void
  1145. */
  1146. private function processArguments(array $args, &$i)
  1147. {
  1148. $numArgs = count($args);
  1149. if ($this->argumentDefinition === null || $this->argumentsAllowed() === false)
  1150. {
  1151. // Old argument handling, also used of a set option sets disallowing arguments
  1152. while ($i < $numArgs)
  1153. {
  1154. $this->arguments[] = $args[$i ++];
  1155. }
  1156. }
  1157. else
  1158. {
  1159. $mandatory = true;
  1160. foreach ($this->argumentDefinition as $arg)
  1161. {
  1162. // Check if all followinga arguments are optional
  1163. if ($arg->mandatory === false)
  1164. {
  1165. $mandatory = false;
  1166. }
  1167. // Check if the current argument is present and mandatory
  1168. if ($mandatory === true)
  1169. {
  1170. if (! isset($args[$i]))
  1171. {
  1172. throw new ezcConsoleArgumentMandatoryViolationException($arg);
  1173. }
  1174. }
  1175. else
  1176. {
  1177. // Arguments are optional, if no more left: return.
  1178. if (! isset($args[$i]))
  1179. {
  1180. // Optional and no more arguments left, assign default
  1181. $arg->value = $arg->default;
  1182. continue;
  1183. }
  1184. }
  1185. if ($arg->multiple === true)
  1186. {
  1187. $arg->value = array();
  1188. for($i = $i; $i < $numArgs; ++ $i)
  1189. {
  1190. if ($this->isCorrectType($arg->type, $args[$i]) === false)
  1191. {
  1192. throw new ezcConsoleArgumentTypeViolationException($arg, $args[$i]);
  1193. }
  1194. $arg->value = array_merge($arg->value, array($args[$i]));
  1195. // Keep old handling, too
  1196. $this->arguments[] = $args[$i];
  1197. }
  1198. return;
  1199. }
  1200. else
  1201. {
  1202. if ($this->isCorrectType($arg->type, $args[$i]) === false)
  1203. {
  1204. throw new ezcConsoleArgumentTypeViolationException($arg, $args[$i]);
  1205. }
  1206. $arg->value = $args[$i];
  1207. // Keep old handling, too
  1208. $this->arguments[] = $args[$i];
  1209. }
  1210. ++ $i;
  1211. }
  1212. if ($i < $numArgs)
  1213. {
  1214. throw new ezcConsoleTooManyArgumentsException($args, $i);
  1215. }
  1216. }
  1217. }
  1218. /**
  1219. * Returns if arguments are allowed with the current option submition.
  1220. *
  1221. * @return bool If arguments allowed.
  1222. */
  1223. protected function argumentsAllowed()
  1224. {
  1225. foreach ($this->options as $id => $option)
  1226. {
  1227. if ($option->value !== false && $option->arguments === false)
  1228. {
  1229. return false;
  1230. }
  1231. }
  1232. return true;
  1233. }
  1234. /**
  1235. * Check the rules that may be associated with an option.
  1236. *
  1237. * Options are allowed to have rules associated for dependencies to other
  1238. * options and exclusion of other options or arguments. This method
  1239. * processes the checks.
  1240. *
  1241. * @throws ezcConsoleException
  1242. * in case validation fails.
  1243. */
  1244. private function checkRules()
  1245. {
  1246. // If a help option is set, skip rule checking
  1247. if ($this->helpOptionSet === true)
  1248. {
  1249. return;
  1250. }
  1251. $this->validator->validateOptions($this->options, ($this->arguments !== array()));
  1252. }
  1253. /**
  1254. * Checks if a value is of a given type. Converts the value to the
  1255. * correct PHP type on success.
  1256. *
  1257. * @param int $type The type to check for. One of self::TYPE_*.
  1258. * @param string $val The value to check. Will possibly altered!
  1259. * @return bool True on succesful check, otherwise false.
  1260. */
  1261. private function isCorrectType($type, &$val)
  1262. {
  1263. $res = false;
  1264. switch ($type)
  1265. {
  1266. case ezcConsoleInput :: TYPE_STRING :
  1267. $res = true;
  1268. $val = preg_replace('/^(["\'])(.*)\1$/', '\2', $val);
  1269. break;
  1270. case ezcConsoleInput :: TYPE_INT :
  1271. $res = preg_match('/^[0-9]+$/', $val) ? true : false;
  1272. if ($res)
  1273. {
  1274. $val = (int) $val;
  1275. }
  1276. break;
  1277. }
  1278. return $res;
  1279. }
  1280. /**
  1281. * Split parameter and value for long option names.
  1282. *
  1283. * This method checks for long options, if the value is passed using =. If
  1284. * this is the case parameter and value get split and replaced in the
  1285. * arguments array.
  1286. *
  1287. * @param array(string) $args The arguments array
  1288. * @param int $i Current arguments array position
  1289. * @return void
  1290. */
  1291. private function preprocessLongOption(array &$args, $i)
  1292. {
  1293. // Value given?
  1294. if (preg_match('/^--\w+\=[^ ]/i', $args[$i]))
  1295. {
  1296. // Split param and value and replace current param
  1297. $parts = explode('=', $args[$i], 2);
  1298. array_splice($args, $i, 1, $parts);
  1299. }
  1300. }
  1301. }
  1302. ?>