Total Complexity | 63 |
Total Lines | 332 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like Console_Getopt often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Console_Getopt, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
32 | class Console_Getopt |
||
33 | { |
||
34 | /** |
||
35 | * Parses the command-line options. |
||
36 | * |
||
37 | * The first parameter to this function should be the list of command-line |
||
38 | * arguments without the leading reference to the running program. |
||
39 | * |
||
40 | * The second parameter is a string of allowed short options. Each of the |
||
41 | * option letters can be followed by a colon ':' to specify that the option |
||
42 | * requires an argument, or a double colon '::' to specify that the option |
||
43 | * takes an optional argument. |
||
44 | * |
||
45 | * The third argument is an optional array of allowed long options. The |
||
46 | * leading '--' should not be included in the option name. Options that |
||
47 | * require an argument should be followed by '=', and options that take an |
||
48 | * option argument should be followed by '=='. |
||
49 | * |
||
50 | * The return value is an array of two elements: the list of parsed |
||
51 | * options and the list of non-option command-line arguments. Each entry in |
||
52 | * the list of parsed options is a pair of elements - the first one |
||
53 | * specifies the option, and the second one specifies the option argument, |
||
54 | * if there was one. |
||
55 | * |
||
56 | * Long and short options can be mixed. |
||
57 | * |
||
58 | * Most of the semantics of this function are based on GNU getopt_long(). |
||
59 | * |
||
60 | * @param array $args an array of command-line arguments |
||
61 | * @param string $short_options specifies the list of allowed short options |
||
62 | * @param array $long_options specifies the list of allowed long options |
||
63 | * @param bool $skip_unknown suppresses Console_Getopt: unrecognized option |
||
64 | * |
||
65 | * @return array two-element array containing the list of parsed options and |
||
66 | * the non-option arguments |
||
67 | */ |
||
68 | public static function getopt2($args, $short_options, $long_options = null, $skip_unknown = false) |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * This function expects $args to start with the script name (POSIX-style). |
||
75 | * Preserved for backwards compatibility. |
||
76 | * |
||
77 | * @param array $args an array of command-line arguments |
||
78 | * @param string $short_options specifies the list of allowed short options |
||
79 | * @param array $long_options specifies the list of allowed long options |
||
80 | * |
||
81 | * @return array two-element array containing the list of parsed options and |
||
82 | * the non-option arguments |
||
83 | * @see getopt2() |
||
84 | */ |
||
85 | public static function getopt($args, $short_options, $long_options = null, $skip_unknown = false) |
||
86 | { |
||
87 | return self::doGetopt(1, $args, $short_options, $long_options, $skip_unknown); |
||
88 | } |
||
89 | |||
90 | /** |
||
91 | * The actual implementation of the argument parsing code. |
||
92 | * |
||
93 | * @param int $version Version to use |
||
94 | * @param array $args an array of command-line arguments |
||
95 | * @param string $short_options specifies the list of allowed short options |
||
96 | * @param array $long_options specifies the list of allowed long options |
||
97 | * @param bool $skip_unknown suppresses Console_Getopt: unrecognized option |
||
98 | * |
||
99 | * @return array |
||
100 | */ |
||
101 | public static function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false) |
||
102 | { |
||
103 | // in case you pass directly readPHPArgv() as the first arg |
||
104 | if (PEAR::isError($args)) { |
||
105 | return $args; |
||
106 | } |
||
107 | |||
108 | if (empty($args)) { |
||
109 | return [[], []]; |
||
110 | } |
||
111 | |||
112 | $non_opts = $opts = []; |
||
113 | |||
114 | $args = (array)$args; |
||
115 | |||
116 | if ($long_options) { |
||
117 | sort($long_options); |
||
118 | } |
||
119 | |||
120 | /* |
||
121 | * Preserve backwards compatibility with callers that relied on |
||
122 | * erroneous POSIX fix. |
||
123 | */ |
||
124 | if ($version < 2) { |
||
125 | if (isset($args[0][0]) && '-' != $args[0][0]) { |
||
126 | array_shift($args); |
||
127 | } |
||
128 | } |
||
129 | |||
130 | for ($i = 0, $iMax = count($args); $i < $iMax; ++$i) { |
||
131 | $arg = $args[$i]; |
||
132 | /* The special element '--' means explicit end of |
||
133 | options. Treat the rest of the arguments as non-options |
||
134 | and end the loop. */ |
||
135 | if ('--' == $arg) { |
||
136 | $non_opts = array_merge($non_opts, array_slice($args, $i + 1)); |
||
137 | break; |
||
138 | } |
||
139 | |||
140 | if ('-' != $arg[0] || (strlen($arg) > 1 && '-' == $arg[1] && !$long_options)) { |
||
141 | $non_opts = array_merge($non_opts, array_slice($args, $i)); |
||
142 | break; |
||
143 | } elseif (strlen($arg) > 1 && '-' == $arg[1]) { |
||
144 | $error = self::_parseLongOption( |
||
145 | substr($arg, 2), |
||
146 | $long_options, |
||
147 | $opts, |
||
148 | $i, |
||
149 | $args, |
||
150 | $skip_unknown |
||
151 | ); |
||
152 | if (PEAR::isError($error)) { |
||
153 | return $error; |
||
|
|||
154 | } |
||
155 | } elseif ('-' == $arg) { |
||
156 | // - is stdin |
||
157 | $non_opts = array_merge($non_opts, array_slice($args, $i)); |
||
158 | break; |
||
159 | } else { |
||
160 | $error = self::_parseShortOption( |
||
161 | substr($arg, 1), |
||
162 | $short_options, |
||
163 | $opts, |
||
164 | $i, |
||
165 | $args, |
||
166 | $skip_unknown |
||
167 | ); |
||
168 | if (PEAR::isError($error)) { |
||
169 | return $error; |
||
170 | } |
||
171 | } |
||
172 | } |
||
173 | |||
174 | return [$opts, $non_opts]; |
||
175 | } |
||
176 | |||
177 | /** |
||
178 | * Parse short option |
||
179 | * |
||
180 | * @param string $arg Argument |
||
181 | * @param string[] $short_options Available short options |
||
182 | * @param string[][] &$opts |
||
183 | * @param int &$argIdx |
||
184 | * @param string[] $args |
||
185 | * @param bool $skip_unknown suppresses Console_Getopt: unrecognized option |
||
186 | * |
||
187 | * @return void |
||
188 | */ |
||
189 | protected static function _parseShortOption($arg, $short_options, &$opts, &$argIdx, $args, $skip_unknown) |
||
190 | { |
||
191 | for ($i = 0, $iMax = strlen($arg); $i < $iMax; ++$i) { |
||
192 | $opt = $arg[$i]; |
||
193 | $opt_arg = null; |
||
194 | |||
195 | /* Try to find the short option in the specifier string. */ |
||
196 | if (false === ($spec = strstr($short_options, $opt)) || ':' == $arg[$i]) { |
||
197 | if (true === $skip_unknown) { |
||
198 | break; |
||
199 | } |
||
200 | |||
201 | $msg = "Console_Getopt: unrecognized option -- $opt"; |
||
202 | return PEAR::raiseError($msg); |
||
203 | } |
||
204 | |||
205 | if (strlen($spec) > 1 && ':' == $spec[1]) { |
||
206 | if (strlen($spec) > 2 && ':' == $spec[2]) { |
||
207 | if ($i + 1 < strlen($arg)) { |
||
208 | /* Option takes an optional argument. Use the remainder of |
||
209 | the arg string if there is anything left. */ |
||
210 | $opts[] = [$opt, substr($arg, $i + 1)]; |
||
211 | break; |
||
212 | } |
||
213 | } else { |
||
214 | /* Option requires an argument. Use the remainder of the arg |
||
215 | string if there is anything left. */ |
||
216 | if ($i + 1 < strlen($arg)) { |
||
217 | $opts[] = [$opt, substr($arg, $i + 1)]; |
||
218 | break; |
||
219 | } elseif (isset($args[++$argIdx])) { |
||
220 | $opt_arg = $args[$argIdx];/* Else use the next argument. */; |
||
221 | if (self::_isShortOpt($opt_arg) |
||
222 | || self::_isLongOpt($opt_arg)) { |
||
223 | $msg = "option requires an argument --$opt"; |
||
224 | return PEAR::raiseError('Console_Getopt: ' . $msg); |
||
225 | } |
||
226 | } else { |
||
227 | $msg = "option requires an argument --$opt"; |
||
228 | return PEAR::raiseError('Console_Getopt: ' . $msg); |
||
229 | } |
||
230 | } |
||
231 | } |
||
232 | |||
233 | $opts[] = [$opt, $opt_arg]; |
||
234 | } |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * Checks if an argument is a short option |
||
239 | * |
||
240 | * @param string $arg Argument to check |
||
241 | * |
||
242 | * @return bool |
||
243 | */ |
||
244 | protected static function _isShortOpt($arg) |
||
248 | } |
||
249 | |||
250 | /** |
||
251 | * Checks if an argument is a long option |
||
252 | * |
||
253 | * @param string $arg Argument to check |
||
254 | * |
||
255 | * @return bool |
||
256 | */ |
||
257 | protected static function _isLongOpt($arg) |
||
258 | { |
||
259 | return strlen($arg) > 2 && '-' == $arg[0] && '-' == $arg[1] |
||
260 | && preg_match('/[a-zA-Z]+$/', substr($arg, 2)); |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Parse long option |
||
265 | * |
||
266 | * @param string $arg Argument |
||
267 | * @param string[] $long_options Available long options |
||
268 | * @param string[][] &$opts |
||
269 | * @param int &$argIdx |
||
270 | * @param string[] $args |
||
271 | * |
||
272 | * @return void|PEAR_Error |
||
273 | */ |
||
274 | protected static function _parseLongOption($arg, $long_options, &$opts, &$argIdx, $args, $skip_unknown) |
||
275 | { |
||
276 | @list($opt, $opt_arg) = explode('=', $arg, 2); |
||
277 | |||
278 | $opt_len = strlen($opt); |
||
279 | |||
280 | for ($i = 0, $iMax = count($long_options); $i < $iMax; ++$i) { |
||
281 | $long_opt = $long_options[$i]; |
||
282 | $opt_start = substr($long_opt, 0, $opt_len); |
||
283 | |||
284 | $long_opt_name = str_replace('=', '', $long_opt); |
||
285 | |||
286 | /* Option doesn't match. Go on to the next one. */ |
||
287 | if ($long_opt_name != $opt) { |
||
288 | continue; |
||
289 | } |
||
290 | |||
291 | $opt_rest = substr($long_opt, $opt_len); |
||
292 | |||
293 | /* Check that the options uniquely matches one of the allowed |
||
294 | options. */ |
||
295 | if ($i + 1 < count($long_options)) { |
||
296 | $next_option_rest = substr($long_options[$i + 1], $opt_len); |
||
297 | } else { |
||
298 | $next_option_rest = ''; |
||
299 | } |
||
300 | |||
301 | if ('' != $opt_rest && '=' != $opt[0] |
||
302 | && $i + 1 < count($long_options) |
||
303 | && $opt == substr($long_options[$i + 1], 0, $opt_len) |
||
304 | && '' != $next_option_rest |
||
305 | && '=' != $next_option_rest[0]) { |
||
306 | $msg = "Console_Getopt: option --$opt is ambiguous"; |
||
307 | return PEAR::raiseError($msg); |
||
308 | } |
||
309 | |||
310 | if ('=' == substr($long_opt, -1)) { |
||
311 | if ('==' != substr($long_opt, -2)) { |
||
312 | /* Long option requires an argument. |
||
313 | Take the next argument if one wasn't specified. */; |
||
314 | if (!strlen($opt_arg)) { |
||
315 | if (!isset($args[++$argIdx])) { |
||
316 | $msg = "Console_Getopt: option requires an argument --$opt"; |
||
317 | return PEAR::raiseError($msg); |
||
318 | } |
||
319 | $opt_arg = $args[$argIdx]; |
||
320 | } |
||
321 | |||
322 | if (self::_isShortOpt($opt_arg) |
||
323 | || self::_isLongOpt($opt_arg)) { |
||
324 | $msg = "Console_Getopt: option requires an argument --$opt"; |
||
325 | return PEAR::raiseError($msg); |
||
326 | } |
||
327 | } |
||
328 | } elseif ($opt_arg) { |
||
329 | $msg = "Console_Getopt: option --$opt doesn't allow an argument"; |
||
330 | return PEAR::raiseError($msg); |
||
331 | } |
||
332 | |||
333 | $opts[] = ['--' . $opt, $opt_arg]; |
||
334 | return; |
||
335 | } |
||
336 | |||
337 | if (true === $skip_unknown) { |
||
338 | return; |
||
339 | } |
||
340 | |||
341 | return PEAR::raiseError("Console_Getopt: unrecognized option --$opt"); |
||
342 | } |
||
343 | |||
344 | /** |
||
345 | * Safely read the $argv PHP array across different PHP configurations. |
||
346 | * Will take care on register_globals and register_argc_argv ini directives |
||
347 | * |
||
348 | * @return mixed the $argv PHP array or PEAR error if not registered |
||
349 | */ |
||
350 | public static function readPHPArgv() |
||
364 | } |
||
365 | } |
||
366 |