Chris@18
|
1 <?php
|
Chris@18
|
2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
|
Chris@18
|
3 /**
|
Chris@18
|
4 * PHP Version 5
|
Chris@18
|
5 *
|
Chris@18
|
6 * Copyright (c) 2001-2015, The PEAR developers
|
Chris@18
|
7 *
|
Chris@18
|
8 * This source file is subject to the BSD-2-Clause license,
|
Chris@18
|
9 * that is bundled with this package in the file LICENSE, and is
|
Chris@18
|
10 * available through the world-wide-web at the following url:
|
Chris@18
|
11 * http://opensource.org/licenses/bsd-license.php.
|
Chris@18
|
12 *
|
Chris@18
|
13 * @category Console
|
Chris@18
|
14 * @package Console_Getopt
|
Chris@18
|
15 * @author Andrei Zmievski <andrei@php.net>
|
Chris@18
|
16 * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause
|
Chris@18
|
17 * @version CVS: $Id$
|
Chris@18
|
18 * @link http://pear.php.net/package/Console_Getopt
|
Chris@18
|
19 */
|
Chris@18
|
20
|
Chris@18
|
21 require_once 'PEAR.php';
|
Chris@18
|
22
|
Chris@18
|
23 /**
|
Chris@18
|
24 * Command-line options parsing class.
|
Chris@18
|
25 *
|
Chris@18
|
26 * @category Console
|
Chris@18
|
27 * @package Console_Getopt
|
Chris@18
|
28 * @author Andrei Zmievski <andrei@php.net>
|
Chris@18
|
29 * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause
|
Chris@18
|
30 * @link http://pear.php.net/package/Console_Getopt
|
Chris@18
|
31 */
|
Chris@18
|
32 class Console_Getopt
|
Chris@18
|
33 {
|
Chris@18
|
34
|
Chris@18
|
35 /**
|
Chris@18
|
36 * Parses the command-line options.
|
Chris@18
|
37 *
|
Chris@18
|
38 * The first parameter to this function should be the list of command-line
|
Chris@18
|
39 * arguments without the leading reference to the running program.
|
Chris@18
|
40 *
|
Chris@18
|
41 * The second parameter is a string of allowed short options. Each of the
|
Chris@18
|
42 * option letters can be followed by a colon ':' to specify that the option
|
Chris@18
|
43 * requires an argument, or a double colon '::' to specify that the option
|
Chris@18
|
44 * takes an optional argument.
|
Chris@18
|
45 *
|
Chris@18
|
46 * The third argument is an optional array of allowed long options. The
|
Chris@18
|
47 * leading '--' should not be included in the option name. Options that
|
Chris@18
|
48 * require an argument should be followed by '=', and options that take an
|
Chris@18
|
49 * option argument should be followed by '=='.
|
Chris@18
|
50 *
|
Chris@18
|
51 * The return value is an array of two elements: the list of parsed
|
Chris@18
|
52 * options and the list of non-option command-line arguments. Each entry in
|
Chris@18
|
53 * the list of parsed options is a pair of elements - the first one
|
Chris@18
|
54 * specifies the option, and the second one specifies the option argument,
|
Chris@18
|
55 * if there was one.
|
Chris@18
|
56 *
|
Chris@18
|
57 * Long and short options can be mixed.
|
Chris@18
|
58 *
|
Chris@18
|
59 * Most of the semantics of this function are based on GNU getopt_long().
|
Chris@18
|
60 *
|
Chris@18
|
61 * @param array $args an array of command-line arguments
|
Chris@18
|
62 * @param string $short_options specifies the list of allowed short options
|
Chris@18
|
63 * @param array $long_options specifies the list of allowed long options
|
Chris@18
|
64 * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
|
Chris@18
|
65 *
|
Chris@18
|
66 * @return array two-element array containing the list of parsed options and
|
Chris@18
|
67 * the non-option arguments
|
Chris@18
|
68 */
|
Chris@18
|
69 public static function getopt2($args, $short_options, $long_options = null, $skip_unknown = false)
|
Chris@18
|
70 {
|
Chris@18
|
71 return Console_Getopt::doGetopt(2, $args, $short_options, $long_options, $skip_unknown);
|
Chris@18
|
72 }
|
Chris@18
|
73
|
Chris@18
|
74 /**
|
Chris@18
|
75 * This function expects $args to start with the script name (POSIX-style).
|
Chris@18
|
76 * Preserved for backwards compatibility.
|
Chris@18
|
77 *
|
Chris@18
|
78 * @param array $args an array of command-line arguments
|
Chris@18
|
79 * @param string $short_options specifies the list of allowed short options
|
Chris@18
|
80 * @param array $long_options specifies the list of allowed long options
|
Chris@18
|
81 *
|
Chris@18
|
82 * @see getopt2()
|
Chris@18
|
83 * @return array two-element array containing the list of parsed options and
|
Chris@18
|
84 * the non-option arguments
|
Chris@18
|
85 */
|
Chris@18
|
86 public static function getopt($args, $short_options, $long_options = null, $skip_unknown = false)
|
Chris@18
|
87 {
|
Chris@18
|
88 return Console_Getopt::doGetopt(1, $args, $short_options, $long_options, $skip_unknown);
|
Chris@18
|
89 }
|
Chris@18
|
90
|
Chris@18
|
91 /**
|
Chris@18
|
92 * The actual implementation of the argument parsing code.
|
Chris@18
|
93 *
|
Chris@18
|
94 * @param int $version Version to use
|
Chris@18
|
95 * @param array $args an array of command-line arguments
|
Chris@18
|
96 * @param string $short_options specifies the list of allowed short options
|
Chris@18
|
97 * @param array $long_options specifies the list of allowed long options
|
Chris@18
|
98 * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
|
Chris@18
|
99 *
|
Chris@18
|
100 * @return array
|
Chris@18
|
101 */
|
Chris@18
|
102 public static function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false)
|
Chris@18
|
103 {
|
Chris@18
|
104 // in case you pass directly readPHPArgv() as the first arg
|
Chris@18
|
105 if (PEAR::isError($args)) {
|
Chris@18
|
106 return $args;
|
Chris@18
|
107 }
|
Chris@18
|
108
|
Chris@18
|
109 if (empty($args)) {
|
Chris@18
|
110 return array(array(), array());
|
Chris@18
|
111 }
|
Chris@18
|
112
|
Chris@18
|
113 $non_opts = $opts = array();
|
Chris@18
|
114
|
Chris@18
|
115 settype($args, 'array');
|
Chris@18
|
116
|
Chris@18
|
117 if ($long_options) {
|
Chris@18
|
118 sort($long_options);
|
Chris@18
|
119 }
|
Chris@18
|
120
|
Chris@18
|
121 /*
|
Chris@18
|
122 * Preserve backwards compatibility with callers that relied on
|
Chris@18
|
123 * erroneous POSIX fix.
|
Chris@18
|
124 */
|
Chris@18
|
125 if ($version < 2) {
|
Chris@18
|
126 if (isset($args[0]{0}) && $args[0]{0} != '-') {
|
Chris@18
|
127 array_shift($args);
|
Chris@18
|
128 }
|
Chris@18
|
129 }
|
Chris@18
|
130
|
Chris@18
|
131 for ($i = 0; $i < count($args); $i++) {
|
Chris@18
|
132 $arg = $args[$i];
|
Chris@18
|
133 /* The special element '--' means explicit end of
|
Chris@18
|
134 options. Treat the rest of the arguments as non-options
|
Chris@18
|
135 and end the loop. */
|
Chris@18
|
136 if ($arg == '--') {
|
Chris@18
|
137 $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
|
Chris@18
|
138 break;
|
Chris@18
|
139 }
|
Chris@18
|
140
|
Chris@18
|
141 if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
|
Chris@18
|
142 $non_opts = array_merge($non_opts, array_slice($args, $i));
|
Chris@18
|
143 break;
|
Chris@18
|
144 } elseif (strlen($arg) > 1 && $arg{1} == '-') {
|
Chris@18
|
145 $error = Console_Getopt::_parseLongOption(substr($arg, 2),
|
Chris@18
|
146 $long_options,
|
Chris@18
|
147 $opts,
|
Chris@18
|
148 $i,
|
Chris@18
|
149 $args,
|
Chris@18
|
150 $skip_unknown);
|
Chris@18
|
151 if (PEAR::isError($error)) {
|
Chris@18
|
152 return $error;
|
Chris@18
|
153 }
|
Chris@18
|
154 } elseif ($arg == '-') {
|
Chris@18
|
155 // - is stdin
|
Chris@18
|
156 $non_opts = array_merge($non_opts, array_slice($args, $i));
|
Chris@18
|
157 break;
|
Chris@18
|
158 } else {
|
Chris@18
|
159 $error = Console_Getopt::_parseShortOption(substr($arg, 1),
|
Chris@18
|
160 $short_options,
|
Chris@18
|
161 $opts,
|
Chris@18
|
162 $i,
|
Chris@18
|
163 $args,
|
Chris@18
|
164 $skip_unknown);
|
Chris@18
|
165 if (PEAR::isError($error)) {
|
Chris@18
|
166 return $error;
|
Chris@18
|
167 }
|
Chris@18
|
168 }
|
Chris@18
|
169 }
|
Chris@18
|
170
|
Chris@18
|
171 return array($opts, $non_opts);
|
Chris@18
|
172 }
|
Chris@18
|
173
|
Chris@18
|
174 /**
|
Chris@18
|
175 * Parse short option
|
Chris@18
|
176 *
|
Chris@18
|
177 * @param string $arg Argument
|
Chris@18
|
178 * @param string[] $short_options Available short options
|
Chris@18
|
179 * @param string[][] &$opts
|
Chris@18
|
180 * @param int &$argIdx
|
Chris@18
|
181 * @param string[] $args
|
Chris@18
|
182 * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
|
Chris@18
|
183 *
|
Chris@18
|
184 * @return void
|
Chris@18
|
185 */
|
Chris@18
|
186 protected static function _parseShortOption($arg, $short_options, &$opts, &$argIdx, $args, $skip_unknown)
|
Chris@18
|
187 {
|
Chris@18
|
188 for ($i = 0; $i < strlen($arg); $i++) {
|
Chris@18
|
189 $opt = $arg{$i};
|
Chris@18
|
190 $opt_arg = null;
|
Chris@18
|
191
|
Chris@18
|
192 /* Try to find the short option in the specifier string. */
|
Chris@18
|
193 if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':') {
|
Chris@18
|
194 if ($skip_unknown === true) {
|
Chris@18
|
195 break;
|
Chris@18
|
196 }
|
Chris@18
|
197
|
Chris@18
|
198 $msg = "Console_Getopt: unrecognized option -- $opt";
|
Chris@18
|
199 return PEAR::raiseError($msg);
|
Chris@18
|
200 }
|
Chris@18
|
201
|
Chris@18
|
202 if (strlen($spec) > 1 && $spec{1} == ':') {
|
Chris@18
|
203 if (strlen($spec) > 2 && $spec{2} == ':') {
|
Chris@18
|
204 if ($i + 1 < strlen($arg)) {
|
Chris@18
|
205 /* Option takes an optional argument. Use the remainder of
|
Chris@18
|
206 the arg string if there is anything left. */
|
Chris@18
|
207 $opts[] = array($opt, substr($arg, $i + 1));
|
Chris@18
|
208 break;
|
Chris@18
|
209 }
|
Chris@18
|
210 } else {
|
Chris@18
|
211 /* Option requires an argument. Use the remainder of the arg
|
Chris@18
|
212 string if there is anything left. */
|
Chris@18
|
213 if ($i + 1 < strlen($arg)) {
|
Chris@18
|
214 $opts[] = array($opt, substr($arg, $i + 1));
|
Chris@18
|
215 break;
|
Chris@18
|
216 } else if (isset($args[++$argIdx])) {
|
Chris@18
|
217 $opt_arg = $args[$argIdx];
|
Chris@18
|
218 /* Else use the next argument. */;
|
Chris@18
|
219 if (Console_Getopt::_isShortOpt($opt_arg)
|
Chris@18
|
220 || Console_Getopt::_isLongOpt($opt_arg)) {
|
Chris@18
|
221 $msg = "option requires an argument --$opt";
|
Chris@18
|
222 return PEAR::raiseError("Console_Getopt: " . $msg);
|
Chris@18
|
223 }
|
Chris@18
|
224 } else {
|
Chris@18
|
225 $msg = "option requires an argument --$opt";
|
Chris@18
|
226 return PEAR::raiseError("Console_Getopt: " . $msg);
|
Chris@18
|
227 }
|
Chris@18
|
228 }
|
Chris@18
|
229 }
|
Chris@18
|
230
|
Chris@18
|
231 $opts[] = array($opt, $opt_arg);
|
Chris@18
|
232 }
|
Chris@18
|
233 }
|
Chris@18
|
234
|
Chris@18
|
235 /**
|
Chris@18
|
236 * Checks if an argument is a short option
|
Chris@18
|
237 *
|
Chris@18
|
238 * @param string $arg Argument to check
|
Chris@18
|
239 *
|
Chris@18
|
240 * @return bool
|
Chris@18
|
241 */
|
Chris@18
|
242 protected static function _isShortOpt($arg)
|
Chris@18
|
243 {
|
Chris@18
|
244 return strlen($arg) == 2 && $arg[0] == '-'
|
Chris@18
|
245 && preg_match('/[a-zA-Z]/', $arg[1]);
|
Chris@18
|
246 }
|
Chris@18
|
247
|
Chris@18
|
248 /**
|
Chris@18
|
249 * Checks if an argument is a long option
|
Chris@18
|
250 *
|
Chris@18
|
251 * @param string $arg Argument to check
|
Chris@18
|
252 *
|
Chris@18
|
253 * @return bool
|
Chris@18
|
254 */
|
Chris@18
|
255 protected static function _isLongOpt($arg)
|
Chris@18
|
256 {
|
Chris@18
|
257 return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
|
Chris@18
|
258 preg_match('/[a-zA-Z]+$/', substr($arg, 2));
|
Chris@18
|
259 }
|
Chris@18
|
260
|
Chris@18
|
261 /**
|
Chris@18
|
262 * Parse long option
|
Chris@18
|
263 *
|
Chris@18
|
264 * @param string $arg Argument
|
Chris@18
|
265 * @param string[] $long_options Available long options
|
Chris@18
|
266 * @param string[][] &$opts
|
Chris@18
|
267 * @param int &$argIdx
|
Chris@18
|
268 * @param string[] $args
|
Chris@18
|
269 *
|
Chris@18
|
270 * @return void|PEAR_Error
|
Chris@18
|
271 */
|
Chris@18
|
272 protected static function _parseLongOption($arg, $long_options, &$opts, &$argIdx, $args, $skip_unknown)
|
Chris@18
|
273 {
|
Chris@18
|
274 @list($opt, $opt_arg) = explode('=', $arg, 2);
|
Chris@18
|
275
|
Chris@18
|
276 $opt_len = strlen($opt);
|
Chris@18
|
277
|
Chris@18
|
278 for ($i = 0; $i < count($long_options); $i++) {
|
Chris@18
|
279 $long_opt = $long_options[$i];
|
Chris@18
|
280 $opt_start = substr($long_opt, 0, $opt_len);
|
Chris@18
|
281
|
Chris@18
|
282 $long_opt_name = str_replace('=', '', $long_opt);
|
Chris@18
|
283
|
Chris@18
|
284 /* Option doesn't match. Go on to the next one. */
|
Chris@18
|
285 if ($long_opt_name != $opt) {
|
Chris@18
|
286 continue;
|
Chris@18
|
287 }
|
Chris@18
|
288
|
Chris@18
|
289 $opt_rest = substr($long_opt, $opt_len);
|
Chris@18
|
290
|
Chris@18
|
291 /* Check that the options uniquely matches one of the allowed
|
Chris@18
|
292 options. */
|
Chris@18
|
293 if ($i + 1 < count($long_options)) {
|
Chris@18
|
294 $next_option_rest = substr($long_options[$i + 1], $opt_len);
|
Chris@18
|
295 } else {
|
Chris@18
|
296 $next_option_rest = '';
|
Chris@18
|
297 }
|
Chris@18
|
298
|
Chris@18
|
299 if ($opt_rest != '' && $opt{0} != '=' &&
|
Chris@18
|
300 $i + 1 < count($long_options) &&
|
Chris@18
|
301 $opt == substr($long_options[$i+1], 0, $opt_len) &&
|
Chris@18
|
302 $next_option_rest != '' &&
|
Chris@18
|
303 $next_option_rest{0} != '=') {
|
Chris@18
|
304
|
Chris@18
|
305 $msg = "Console_Getopt: option --$opt is ambiguous";
|
Chris@18
|
306 return PEAR::raiseError($msg);
|
Chris@18
|
307 }
|
Chris@18
|
308
|
Chris@18
|
309 if (substr($long_opt, -1) == '=') {
|
Chris@18
|
310 if (substr($long_opt, -2) != '==') {
|
Chris@18
|
311 /* Long option requires an argument.
|
Chris@18
|
312 Take the next argument if one wasn't specified. */;
|
Chris@18
|
313 if (!strlen($opt_arg)) {
|
Chris@18
|
314 if (!isset($args[++$argIdx])) {
|
Chris@18
|
315 $msg = "Console_Getopt: option requires an argument --$opt";
|
Chris@18
|
316 return PEAR::raiseError($msg);
|
Chris@18
|
317 }
|
Chris@18
|
318 $opt_arg = $args[$argIdx];
|
Chris@18
|
319 }
|
Chris@18
|
320
|
Chris@18
|
321 if (Console_Getopt::_isShortOpt($opt_arg)
|
Chris@18
|
322 || Console_Getopt::_isLongOpt($opt_arg)) {
|
Chris@18
|
323 $msg = "Console_Getopt: option requires an argument --$opt";
|
Chris@18
|
324 return PEAR::raiseError($msg);
|
Chris@18
|
325 }
|
Chris@18
|
326 }
|
Chris@18
|
327 } else if ($opt_arg) {
|
Chris@18
|
328 $msg = "Console_Getopt: option --$opt doesn't allow an argument";
|
Chris@18
|
329 return PEAR::raiseError($msg);
|
Chris@18
|
330 }
|
Chris@18
|
331
|
Chris@18
|
332 $opts[] = array('--' . $opt, $opt_arg);
|
Chris@18
|
333 return;
|
Chris@18
|
334 }
|
Chris@18
|
335
|
Chris@18
|
336 if ($skip_unknown === true) {
|
Chris@18
|
337 return;
|
Chris@18
|
338 }
|
Chris@18
|
339
|
Chris@18
|
340 return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
|
Chris@18
|
341 }
|
Chris@18
|
342
|
Chris@18
|
343 /**
|
Chris@18
|
344 * Safely read the $argv PHP array across different PHP configurations.
|
Chris@18
|
345 * Will take care on register_globals and register_argc_argv ini directives
|
Chris@18
|
346 *
|
Chris@18
|
347 * @return mixed the $argv PHP array or PEAR error if not registered
|
Chris@18
|
348 */
|
Chris@18
|
349 public static function readPHPArgv()
|
Chris@18
|
350 {
|
Chris@18
|
351 global $argv;
|
Chris@18
|
352 if (!is_array($argv)) {
|
Chris@18
|
353 if (!@is_array($_SERVER['argv'])) {
|
Chris@18
|
354 if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
|
Chris@18
|
355 $msg = "Could not read cmd args (register_argc_argv=Off?)";
|
Chris@18
|
356 return PEAR::raiseError("Console_Getopt: " . $msg);
|
Chris@18
|
357 }
|
Chris@18
|
358 return $GLOBALS['HTTP_SERVER_VARS']['argv'];
|
Chris@18
|
359 }
|
Chris@18
|
360 return $_SERVER['argv'];
|
Chris@18
|
361 }
|
Chris@18
|
362 return $argv;
|
Chris@18
|
363 }
|
Chris@18
|
364
|
Chris@18
|
365 }
|