PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/inc/cliopts.php

http://github.com/splitbrain/dokuwiki
PHP | 504 lines | 302 code | 37 blank | 165 comment | 78 complexity | 3f9fe5451f276bd1700305d8a1291708 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, GPL-2.0
  1. <?php
  2. /**
  3. * Brutally chopped and modified from http://pear.php.net/package/Console_Getopts
  4. *
  5. * PHP Version 5
  6. *
  7. * Copyright (c) 1997-2004 The PHP Group
  8. *
  9. * LICENSE: This source file is subject to the New BSD license that is
  10. * available through the world-wide-web at the following URI:
  11. * http://www.opensource.org/licenses/bsd-license.php. If you did not receive
  12. * a copy of the New BSD License and are unable to obtain it through the web,
  13. * please send a note to license@php.net so we can mail you a copy immediately.
  14. *
  15. * @category Console
  16. * @package Console_Getopt
  17. * @author Andrei Zmievski <andrei@php.net>
  18. * @modified Harry Fuecks hfuecks gmail.com
  19. * @modified Tanguy Ortolo <tanguy+dokuwiki@ortolo.eu>
  20. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  21. * @version CVS: $Id$
  22. * @link http://pear.php.net/package/Console_Getopt
  23. *
  24. */
  25. //------------------------------------------------------------------------------
  26. /**
  27. * Sets up CLI environment based on SAPI and PHP version
  28. * Helps resolve some issues between the CGI and CLI SAPIs
  29. * as well is inconsistencies between PHP 4.3+ and older versions
  30. */
  31. if (version_compare(phpversion(), '4.3.0', '<') || php_sapi_name() == 'cgi') {
  32. // Handle output buffering
  33. @ob_end_flush();
  34. ob_implicit_flush(true);
  35. // PHP ini settings
  36. set_time_limit(0);
  37. ini_set('track_errors', true);
  38. ini_set('html_errors', false);
  39. ini_set('magic_quotes_runtime', false);
  40. // Define stream constants
  41. define('STDIN', fopen('php://stdin', 'r'));
  42. define('STDOUT', fopen('php://stdout', 'w'));
  43. define('STDERR', fopen('php://stderr', 'w'));
  44. // Close the streams on script termination
  45. register_shutdown_function(
  46. create_function('',
  47. 'fclose(STDIN); fclose(STDOUT); fclose(STDERR); return true;')
  48. );
  49. }
  50. //------------------------------------------------------------------------------
  51. /**
  52. * Error codes
  53. */
  54. define('DOKU_CLI_OPTS_UNKNOWN_OPT',1); //Unrecognized option
  55. define('DOKU_CLI_OPTS_OPT_ARG_REQUIRED',2); //Option requires argument
  56. define('DOKU_CLI_OPTS_OPT_ARG_DENIED',3); //Option not allowed argument
  57. define('DOKU_CLI_OPTS_OPT_ABIGUOUS',4);//Option abiguous
  58. define('DOKU_CLI_OPTS_ARG_READ',5);//Could not read argv
  59. //------------------------------------------------------------------------------
  60. /**
  61. * Command-line options parsing class.
  62. *
  63. * @author Andrei Zmievski <andrei@php.net>
  64. *
  65. */
  66. class Doku_Cli_Opts {
  67. /**
  68. * <?php ?>
  69. * @see http://www.sitepoint.com/article/php-command-line-1/3
  70. * @param string executing file name - this MUST be passed the __FILE__ constant
  71. * @param string short options
  72. * @param array (optional) long options
  73. * @return Doku_Cli_Opts_Container or Doku_Cli_Opts_Error
  74. */
  75. function & getOptions($bin_file, $short_options, $long_options = null) {
  76. $args = Doku_Cli_Opts::readPHPArgv();
  77. if ( Doku_Cli_Opts::isError($args) ) {
  78. return $args;
  79. }
  80. // Compatibility between "php extensions.php" and "./extensions.php"
  81. if ( realpath($_SERVER['argv'][0]) == $bin_file ) {
  82. $options = Doku_Cli_Opts::getOpt($args,$short_options,$long_options);
  83. } else {
  84. $options = Doku_Cli_Opts::getOpt2($args,$short_options,$long_options);
  85. }
  86. if ( Doku_Cli_Opts::isError($options) ) {
  87. return $options;
  88. }
  89. $container = new Doku_Cli_Opts_Container($options);
  90. return $container;
  91. }
  92. /**
  93. * Parses the command-line options.
  94. *
  95. * The first parameter to this function should be the list of command-line
  96. * arguments without the leading reference to the running program.
  97. *
  98. * The second parameter is a string of allowed short options. Each of the
  99. * option letters can be followed by a colon ':' to specify that the option
  100. * requires an argument, or a double colon '::' to specify that the option
  101. * takes an optional argument.
  102. *
  103. * The third argument is an optional array of allowed long options. The
  104. * leading '--' should not be included in the option name. Options that
  105. * require an argument should be followed by '=', and options that take an
  106. * option argument should be followed by '=='.
  107. *
  108. * The return value is an array of two elements: the list of parsed
  109. * options and the list of non-option command-line arguments. Each entry in
  110. * the list of parsed options is a pair of elements - the first one
  111. * specifies the option, and the second one specifies the option argument,
  112. * if there was one.
  113. *
  114. * Long and short options can be mixed.
  115. *
  116. * Most of the semantics of this function are based on GNU getopt_long().
  117. *
  118. * @param array $args an array of command-line arguments
  119. * @param string $short_options specifies the list of allowed short options
  120. * @param array $long_options specifies the list of allowed long options
  121. *
  122. * @return array two-element array containing the list of parsed options and
  123. * the non-option arguments
  124. * @access public
  125. */
  126. function getopt2($args, $short_options, $long_options = null) {
  127. return Doku_Cli_Opts::doGetopt(
  128. 2, $args, $short_options, $long_options
  129. );
  130. }
  131. /**
  132. * This function expects $args to start with the script name (POSIX-style).
  133. * Preserved for backwards compatibility.
  134. *
  135. * @param array $args an array of command-line arguments
  136. * @param string $short_options specifies the list of allowed short options
  137. * @param array $long_options specifies the list of allowed long options
  138. *
  139. * @see getopt2()
  140. * @return array two-element array containing the list of parsed options and
  141. * the non-option arguments
  142. */
  143. function getopt($args, $short_options, $long_options = null) {
  144. return Doku_Cli_Opts::doGetopt(
  145. 1, $args, $short_options, $long_options
  146. );
  147. }
  148. /**
  149. * The actual implementation of the argument parsing code.
  150. *
  151. * @param int $version Version to use
  152. * @param array $args an array of command-line arguments
  153. * @param string $short_options specifies the list of allowed short options
  154. * @param array $long_options specifies the list of allowed long options
  155. *
  156. * @return array
  157. */
  158. function doGetopt($version, $args, $short_options, $long_options = null) {
  159. // in case you pass directly readPHPArgv() as the first arg
  160. if (Doku_Cli_Opts::isError($args)) {
  161. return $args;
  162. }
  163. if (empty($args)) {
  164. return array(array(), array());
  165. }
  166. $opts = array();
  167. $non_opts = array();
  168. settype($args, 'array');
  169. if ($long_options && is_array($long_options)) {
  170. sort($long_options);
  171. }
  172. /*
  173. * Preserve backwards compatibility with callers that relied on
  174. * erroneous POSIX fix.
  175. */
  176. if ($version < 2) {
  177. if (isset($args[0]{0}) && $args[0]{0} != '-') {
  178. array_shift($args);
  179. }
  180. }
  181. reset($args);
  182. while (list($i, $arg) = each($args)) {
  183. /* The special element '--' means explicit end of
  184. options. Treat the rest of the arguments as non-options
  185. and end the loop. */
  186. if ($arg == '--') {
  187. $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
  188. break;
  189. }
  190. if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
  191. $non_opts = array_merge($non_opts, array_slice($args, $i));
  192. break;
  193. } elseif (strlen($arg) > 1 && $arg{1} == '-') {
  194. $error = Doku_Cli_Opts::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
  195. if (Doku_Cli_Opts::isError($error))
  196. return $error;
  197. } elseif ($arg == '-') {
  198. // - is stdin
  199. $non_opts = array_merge($non_opts, array_slice($args, $i));
  200. break;
  201. } else {
  202. $error = Doku_Cli_Opts::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
  203. if (Doku_Cli_Opts::isError($error))
  204. return $error;
  205. }
  206. }
  207. return array($opts, $non_opts);
  208. }
  209. /**
  210. * Parse short option
  211. *
  212. * @param string $arg Argument
  213. * @param string[] $short_options Available short options
  214. * @param string[][] &$opts
  215. * @param string[] &$args
  216. *
  217. * @access private
  218. * @return void
  219. */
  220. function _parseShortOption($arg, $short_options, &$opts, &$args) {
  221. $len = strlen($arg);
  222. for ($i = 0; $i < $len; $i++) {
  223. $opt = $arg{$i};
  224. $opt_arg = null;
  225. /* Try to find the short option in the specifier string. */
  226. if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
  227. {
  228. return Doku_Cli_Opts::raiseError(
  229. DOKU_CLI_OPTS_UNKNOWN_OPT,
  230. "Unrecognized option -- $opt"
  231. );
  232. }
  233. if (strlen($spec) > 1 && $spec{1} == ':') {
  234. if (strlen($spec) > 2 && $spec{2} == ':') {
  235. if ($i + 1 < strlen($arg)) {
  236. /* Option takes an optional argument. Use the remainder of
  237. the arg string if there is anything left. */
  238. $opts[] = array($opt, substr($arg, $i + 1));
  239. break;
  240. }
  241. } else {
  242. /* Option requires an argument. Use the remainder of the arg
  243. string if there is anything left. */
  244. if ($i + 1 < strlen($arg)) {
  245. $opts[] = array($opt, substr($arg, $i + 1));
  246. break;
  247. } else if (list(, $opt_arg) = each($args)) {
  248. /* Else use the next argument. */;
  249. if (Doku_Cli_Opts::_isShortOpt($opt_arg) || Doku_Cli_Opts::_isLongOpt($opt_arg))
  250. return Doku_Cli_Opts::raiseError(
  251. DOKU_CLI_OPTS_OPT_ARG_REQUIRED,
  252. "option requires an argument --$opt"
  253. );
  254. }
  255. else
  256. return Doku_Cli_Opts::raiseError(
  257. DOKU_CLI_OPTS_OPT_ARG_REQUIRED,
  258. "Option requires an argument -- $opt"
  259. );
  260. }
  261. }
  262. $opts[] = array($opt, $opt_arg);
  263. }
  264. }
  265. /**
  266. * Checks if an argument is a short option
  267. *
  268. * @param string $arg Argument to check
  269. *
  270. * @access private
  271. * @return bool
  272. */
  273. function _isShortOpt($arg)
  274. {
  275. return strlen($arg) == 2 && $arg[0] == '-'
  276. && preg_match('/[a-zA-Z]/', $arg[1]);
  277. }
  278. /**
  279. * Checks if an argument is a long option
  280. *
  281. * @param string $arg Argument to check
  282. *
  283. * @access private
  284. * @return bool
  285. */
  286. function _isLongOpt($arg)
  287. {
  288. return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
  289. preg_match('/[a-zA-Z]+$/', substr($arg, 2));
  290. }
  291. /**
  292. * Parse long option
  293. *
  294. * @param string $arg Argument
  295. * @param string[] $long_options Available long options
  296. * @param string[][] &$opts
  297. * @param string[] &$args
  298. *
  299. * @access private
  300. * @return void|PEAR_Error
  301. */
  302. function _parseLongOption($arg, $long_options, &$opts, &$args) {
  303. @list($opt, $opt_arg) = explode('=', $arg, 2);
  304. $opt_len = strlen($opt);
  305. $opt_cnt = count($long_options);
  306. for ($i = 0; $i < $opt_cnt; $i++) {
  307. $long_opt = $long_options[$i];
  308. $opt_start = substr($long_opt, 0, $opt_len);
  309. $long_opt_name = str_replace('=', '', $long_opt);
  310. /* Option doesn't match. Go on to the next one. */
  311. if ($opt_start != $opt)
  312. continue;
  313. $opt_rest = substr($long_opt, $opt_len);
  314. /* Check that the options uniquely matches one of the allowed
  315. options. */
  316. if ($i + 1 < count($long_options)) {
  317. $next_option_rest = substr($long_options[$i + 1], $opt_len);
  318. } else {
  319. $next_option_rest = '';
  320. }
  321. if ($opt_rest != '' && $opt{0} != '=' &&
  322. $i + 1 < $opt_cnt &&
  323. $opt == substr($long_options[$i+1], 0, $opt_len) &&
  324. $next_option_rest != '' &&
  325. $next_option_rest{0} != '=') {
  326. return Doku_Cli_Opts::raiseError(
  327. DOKU_CLI_OPTS_OPT_ABIGUOUS,
  328. "Option --$opt is ambiguous"
  329. );
  330. }
  331. if (substr($long_opt, -1) == '=') {
  332. if (substr($long_opt, -2) != '==') {
  333. /* Long option requires an argument.
  334. Take the next argument if one wasn't specified. */;
  335. if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
  336. return Doku_Cli_Opts::raiseError(
  337. DOKU_CLI_OPTS_OPT_ARG_REQUIRED,
  338. "Option --$opt requires an argument"
  339. );
  340. }
  341. if (Doku_Cli_Opts::_isShortOpt($opt_arg)
  342. || Doku_Cli_Opts::_isLongOpt($opt_arg))
  343. return Doku_Cli_Opts::raiseError(
  344. DOKU_CLI_OPTS_OPT_ARG_REQUIRED,
  345. "Option --$opt requires an argument"
  346. );
  347. }
  348. } else if ($opt_arg) {
  349. return Doku_Cli_Opts::raiseError(
  350. DOKU_CLI_OPTS_OPT_ARG_DENIED,
  351. "Option --$opt doesn't allow an argument"
  352. );
  353. }
  354. $opts[] = array('--' . $opt, $opt_arg);
  355. return;
  356. }
  357. return Doku_Cli_Opts::raiseError(
  358. DOKU_CLI_OPTS_UNKNOWN_OPT,
  359. "Unrecognized option --$opt"
  360. );
  361. }
  362. /**
  363. * Safely read the $argv PHP array across different PHP configurations.
  364. * Will take care on register_globals and register_argc_argv ini directives
  365. *
  366. * @access public
  367. * @return mixed the $argv PHP array or PEAR error if not registered
  368. */
  369. function readPHPArgv() {
  370. global $argv;
  371. if (!is_array($argv)) {
  372. if (!@is_array($_SERVER['argv'])) {
  373. if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
  374. return Doku_Cli_Opts::raiseError(
  375. DOKU_CLI_OPTS_ARG_READ,
  376. "Could not read cmd args (register_argc_argv=Off?)"
  377. );
  378. }
  379. return $GLOBALS['HTTP_SERVER_VARS']['argv'];
  380. }
  381. return $_SERVER['argv'];
  382. }
  383. return $argv;
  384. }
  385. function raiseError($code, $msg) {
  386. return new Doku_Cli_Opts_Error($code, $msg);
  387. }
  388. function isError($obj) {
  389. return is_a($obj, 'Doku_Cli_Opts_Error');
  390. }
  391. }
  392. //------------------------------------------------------------------------------
  393. class Doku_Cli_Opts_Error {
  394. var $code;
  395. var $msg;
  396. function Doku_Cli_Opts_Error($code, $msg) {
  397. $this->code = $code;
  398. $this->msg = $msg;
  399. }
  400. function getMessage() {
  401. return $this->msg;
  402. }
  403. function isError() {
  404. return true;
  405. }
  406. }
  407. //------------------------------------------------------------------------------
  408. class Doku_Cli_Opts_Container {
  409. var $options = array();
  410. var $args = array();
  411. function Doku_Cli_Opts_Container($options) {
  412. foreach ( $options[0] as $option ) {
  413. if ( false !== ( strpos($option[0], '--') ) ) {
  414. $opt_name = substr($option[0], 2);
  415. } else {
  416. $opt_name = $option[0];
  417. }
  418. $this->options[$opt_name] = $option[1];
  419. }
  420. $this->args = $options[1];
  421. }
  422. function has($option) {
  423. return array_key_exists($option, $this->options);
  424. }
  425. function get($option) {
  426. if ( isset($this->options[$option]) ) {
  427. return ( $this->options[$option] ) ;
  428. }
  429. }
  430. function arg($index) {
  431. if ( isset($this->args[$index]) ) {
  432. return $this->args[$index];
  433. }
  434. }
  435. function numArgs() {
  436. return count($this->args);
  437. }
  438. function hasArgs() {
  439. return count($this->args) !== 0;
  440. }
  441. function isError() {
  442. return false;
  443. }
  444. }