xref: /dokuwiki/vendor/splitbrain/php-cli/src/Options.php (revision 43d3f077ff275484c96c5da0d5066a80938f2cb3)
1cbeaa4a0SAndreas Gohr<?php
2cbeaa4a0SAndreas Gohr
3cbeaa4a0SAndreas Gohrnamespace splitbrain\phpcli;
4cbeaa4a0SAndreas Gohr
5cbeaa4a0SAndreas Gohr/**
6cbeaa4a0SAndreas Gohr * Class Options
7cbeaa4a0SAndreas Gohr *
8cbeaa4a0SAndreas Gohr * Parses command line options passed to the CLI script. Allows CLI scripts to easily register all accepted options and
9cbeaa4a0SAndreas Gohr * commands and even generates a help text from this setup.
10cbeaa4a0SAndreas Gohr *
11cbeaa4a0SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
12cbeaa4a0SAndreas Gohr * @license MIT
13cbeaa4a0SAndreas Gohr */
14cbeaa4a0SAndreas Gohrclass Options
15cbeaa4a0SAndreas Gohr{
16cbeaa4a0SAndreas Gohr    /** @var  array keeps the list of options to parse */
17cbeaa4a0SAndreas Gohr    protected $setup;
18cbeaa4a0SAndreas Gohr
19cbeaa4a0SAndreas Gohr    /** @var  array store parsed options */
20cbeaa4a0SAndreas Gohr    protected $options = array();
21cbeaa4a0SAndreas Gohr
22cbeaa4a0SAndreas Gohr    /** @var string current parsed command if any */
23cbeaa4a0SAndreas Gohr    protected $command = '';
24cbeaa4a0SAndreas Gohr
25cbeaa4a0SAndreas Gohr    /** @var  array passed non-option arguments */
26cbeaa4a0SAndreas Gohr    protected $args = array();
27cbeaa4a0SAndreas Gohr
28cbeaa4a0SAndreas Gohr    /** @var  string the executed script */
29cbeaa4a0SAndreas Gohr    protected $bin;
30cbeaa4a0SAndreas Gohr
31cbeaa4a0SAndreas Gohr    /** @var  Colors for colored help output */
32cbeaa4a0SAndreas Gohr    protected $colors;
33cbeaa4a0SAndreas Gohr
34cbeaa4a0SAndreas Gohr    /**
35cbeaa4a0SAndreas Gohr     * Constructor
36cbeaa4a0SAndreas Gohr     *
37cbeaa4a0SAndreas Gohr     * @param Colors $colors optional configured color object
38cbeaa4a0SAndreas Gohr     * @throws Exception when arguments can't be read
39cbeaa4a0SAndreas Gohr     */
40cbeaa4a0SAndreas Gohr    public function __construct(Colors $colors = null)
41cbeaa4a0SAndreas Gohr    {
42cbeaa4a0SAndreas Gohr        if (!is_null($colors)) {
43cbeaa4a0SAndreas Gohr            $this->colors = $colors;
44cbeaa4a0SAndreas Gohr        } else {
45cbeaa4a0SAndreas Gohr            $this->colors = new Colors();
46cbeaa4a0SAndreas Gohr        }
47cbeaa4a0SAndreas Gohr
48cbeaa4a0SAndreas Gohr        $this->setup = array(
49cbeaa4a0SAndreas Gohr            '' => array(
50cbeaa4a0SAndreas Gohr                'opts' => array(),
51cbeaa4a0SAndreas Gohr                'args' => array(),
52cbeaa4a0SAndreas Gohr                'help' => ''
53cbeaa4a0SAndreas Gohr            )
54cbeaa4a0SAndreas Gohr        ); // default command
55cbeaa4a0SAndreas Gohr
56cbeaa4a0SAndreas Gohr        $this->args = $this->readPHPArgv();
57cbeaa4a0SAndreas Gohr        $this->bin = basename(array_shift($this->args));
58cbeaa4a0SAndreas Gohr
59cbeaa4a0SAndreas Gohr        $this->options = array();
60cbeaa4a0SAndreas Gohr    }
61cbeaa4a0SAndreas Gohr
62cbeaa4a0SAndreas Gohr    /**
63cbeaa4a0SAndreas Gohr     * Sets the help text for the tool itself
64cbeaa4a0SAndreas Gohr     *
65cbeaa4a0SAndreas Gohr     * @param string $help
66cbeaa4a0SAndreas Gohr     */
67cbeaa4a0SAndreas Gohr    public function setHelp($help)
68cbeaa4a0SAndreas Gohr    {
69cbeaa4a0SAndreas Gohr        $this->setup['']['help'] = $help;
70cbeaa4a0SAndreas Gohr    }
71cbeaa4a0SAndreas Gohr
72cbeaa4a0SAndreas Gohr    /**
73cbeaa4a0SAndreas Gohr     * Register the names of arguments for help generation and number checking
74cbeaa4a0SAndreas Gohr     *
75cbeaa4a0SAndreas Gohr     * This has to be called in the order arguments are expected
76cbeaa4a0SAndreas Gohr     *
77cbeaa4a0SAndreas Gohr     * @param string $arg argument name (just for help)
78cbeaa4a0SAndreas Gohr     * @param string $help help text
79cbeaa4a0SAndreas Gohr     * @param bool $required is this a required argument
80cbeaa4a0SAndreas Gohr     * @param string $command if theses apply to a sub command only
81cbeaa4a0SAndreas Gohr     * @throws Exception
82cbeaa4a0SAndreas Gohr     */
83cbeaa4a0SAndreas Gohr    public function registerArgument($arg, $help, $required = true, $command = '')
84cbeaa4a0SAndreas Gohr    {
85cbeaa4a0SAndreas Gohr        if (!isset($this->setup[$command])) {
86cbeaa4a0SAndreas Gohr            throw new Exception("Command $command not registered");
87cbeaa4a0SAndreas Gohr        }
88cbeaa4a0SAndreas Gohr
89cbeaa4a0SAndreas Gohr        $this->setup[$command]['args'][] = array(
90cbeaa4a0SAndreas Gohr            'name' => $arg,
91cbeaa4a0SAndreas Gohr            'help' => $help,
92cbeaa4a0SAndreas Gohr            'required' => $required
93cbeaa4a0SAndreas Gohr        );
94cbeaa4a0SAndreas Gohr    }
95cbeaa4a0SAndreas Gohr
96cbeaa4a0SAndreas Gohr    /**
97cbeaa4a0SAndreas Gohr     * This registers a sub command
98cbeaa4a0SAndreas Gohr     *
99cbeaa4a0SAndreas Gohr     * Sub commands have their own options and use their own function (not main()).
100cbeaa4a0SAndreas Gohr     *
101cbeaa4a0SAndreas Gohr     * @param string $command
102cbeaa4a0SAndreas Gohr     * @param string $help
103cbeaa4a0SAndreas Gohr     * @throws Exception
104cbeaa4a0SAndreas Gohr     */
105cbeaa4a0SAndreas Gohr    public function registerCommand($command, $help)
106cbeaa4a0SAndreas Gohr    {
107cbeaa4a0SAndreas Gohr        if (isset($this->setup[$command])) {
108cbeaa4a0SAndreas Gohr            throw new Exception("Command $command already registered");
109cbeaa4a0SAndreas Gohr        }
110cbeaa4a0SAndreas Gohr
111cbeaa4a0SAndreas Gohr        $this->setup[$command] = array(
112cbeaa4a0SAndreas Gohr            'opts' => array(),
113cbeaa4a0SAndreas Gohr            'args' => array(),
114cbeaa4a0SAndreas Gohr            'help' => $help
115cbeaa4a0SAndreas Gohr        );
116cbeaa4a0SAndreas Gohr
117cbeaa4a0SAndreas Gohr    }
118cbeaa4a0SAndreas Gohr
119cbeaa4a0SAndreas Gohr    /**
120cbeaa4a0SAndreas Gohr     * Register an option for option parsing and help generation
121cbeaa4a0SAndreas Gohr     *
122cbeaa4a0SAndreas Gohr     * @param string $long multi character option (specified with --)
123cbeaa4a0SAndreas Gohr     * @param string $help help text for this option
124cbeaa4a0SAndreas Gohr     * @param string|null $short one character option (specified with -)
125cbeaa4a0SAndreas Gohr     * @param bool|string $needsarg does this option require an argument? give it a name here
126cbeaa4a0SAndreas Gohr     * @param string $command what command does this option apply to
127cbeaa4a0SAndreas Gohr     * @throws Exception
128cbeaa4a0SAndreas Gohr     */
129cbeaa4a0SAndreas Gohr    public function registerOption($long, $help, $short = null, $needsarg = false, $command = '')
130cbeaa4a0SAndreas Gohr    {
131cbeaa4a0SAndreas Gohr        if (!isset($this->setup[$command])) {
132cbeaa4a0SAndreas Gohr            throw new Exception("Command $command not registered");
133cbeaa4a0SAndreas Gohr        }
134cbeaa4a0SAndreas Gohr
135cbeaa4a0SAndreas Gohr        $this->setup[$command]['opts'][$long] = array(
136cbeaa4a0SAndreas Gohr            'needsarg' => $needsarg,
137cbeaa4a0SAndreas Gohr            'help' => $help,
138cbeaa4a0SAndreas Gohr            'short' => $short
139cbeaa4a0SAndreas Gohr        );
140cbeaa4a0SAndreas Gohr
141cbeaa4a0SAndreas Gohr        if ($short) {
142cbeaa4a0SAndreas Gohr            if (strlen($short) > 1) {
143cbeaa4a0SAndreas Gohr                throw new Exception("Short options should be exactly one ASCII character");
144cbeaa4a0SAndreas Gohr            }
145cbeaa4a0SAndreas Gohr
146cbeaa4a0SAndreas Gohr            $this->setup[$command]['short'][$short] = $long;
147cbeaa4a0SAndreas Gohr        }
148cbeaa4a0SAndreas Gohr    }
149cbeaa4a0SAndreas Gohr
150cbeaa4a0SAndreas Gohr    /**
151cbeaa4a0SAndreas Gohr     * Checks the actual number of arguments against the required number
152cbeaa4a0SAndreas Gohr     *
153cbeaa4a0SAndreas Gohr     * Throws an exception if arguments are missing.
154cbeaa4a0SAndreas Gohr     *
155cbeaa4a0SAndreas Gohr     * This is run from CLI automatically and usually does not need to be called directly
156cbeaa4a0SAndreas Gohr     *
157cbeaa4a0SAndreas Gohr     * @throws Exception
158cbeaa4a0SAndreas Gohr     */
159cbeaa4a0SAndreas Gohr    public function checkArguments()
160cbeaa4a0SAndreas Gohr    {
161cbeaa4a0SAndreas Gohr        $argc = count($this->args);
162cbeaa4a0SAndreas Gohr
163cbeaa4a0SAndreas Gohr        $req = 0;
164cbeaa4a0SAndreas Gohr        foreach ($this->setup[$this->command]['args'] as $arg) {
165cbeaa4a0SAndreas Gohr            if (!$arg['required']) {
166cbeaa4a0SAndreas Gohr                break;
167cbeaa4a0SAndreas Gohr            } // last required arguments seen
168cbeaa4a0SAndreas Gohr            $req++;
169cbeaa4a0SAndreas Gohr        }
170cbeaa4a0SAndreas Gohr
171cbeaa4a0SAndreas Gohr        if ($req > $argc) {
172cbeaa4a0SAndreas Gohr            throw new Exception("Not enough arguments", Exception::E_OPT_ARG_REQUIRED);
173cbeaa4a0SAndreas Gohr        }
174cbeaa4a0SAndreas Gohr    }
175cbeaa4a0SAndreas Gohr
176cbeaa4a0SAndreas Gohr    /**
177cbeaa4a0SAndreas Gohr     * Parses the given arguments for known options and command
178cbeaa4a0SAndreas Gohr     *
179cbeaa4a0SAndreas Gohr     * The given $args array should NOT contain the executed file as first item anymore! The $args
180cbeaa4a0SAndreas Gohr     * array is stripped from any options and possible command. All found otions can be accessed via the
181cbeaa4a0SAndreas Gohr     * getOpt() function
182cbeaa4a0SAndreas Gohr     *
183cbeaa4a0SAndreas Gohr     * Note that command options will overwrite any global options with the same name
184cbeaa4a0SAndreas Gohr     *
185cbeaa4a0SAndreas Gohr     * This is run from CLI automatically and usually does not need to be called directly
186cbeaa4a0SAndreas Gohr     *
187cbeaa4a0SAndreas Gohr     * @throws Exception
188cbeaa4a0SAndreas Gohr     */
189cbeaa4a0SAndreas Gohr    public function parseOptions()
190cbeaa4a0SAndreas Gohr    {
191cbeaa4a0SAndreas Gohr        $non_opts = array();
192cbeaa4a0SAndreas Gohr
193cbeaa4a0SAndreas Gohr        $argc = count($this->args);
194cbeaa4a0SAndreas Gohr        for ($i = 0; $i < $argc; $i++) {
195cbeaa4a0SAndreas Gohr            $arg = $this->args[$i];
196cbeaa4a0SAndreas Gohr
197cbeaa4a0SAndreas Gohr            // The special element '--' means explicit end of options. Treat the rest of the arguments as non-options
198cbeaa4a0SAndreas Gohr            // and end the loop.
199cbeaa4a0SAndreas Gohr            if ($arg == '--') {
200cbeaa4a0SAndreas Gohr                $non_opts = array_merge($non_opts, array_slice($this->args, $i + 1));
201cbeaa4a0SAndreas Gohr                break;
202cbeaa4a0SAndreas Gohr            }
203cbeaa4a0SAndreas Gohr
204cbeaa4a0SAndreas Gohr            // '-' is stdin - a normal argument
205cbeaa4a0SAndreas Gohr            if ($arg == '-') {
206cbeaa4a0SAndreas Gohr                $non_opts = array_merge($non_opts, array_slice($this->args, $i));
207cbeaa4a0SAndreas Gohr                break;
208cbeaa4a0SAndreas Gohr            }
209cbeaa4a0SAndreas Gohr
210cbeaa4a0SAndreas Gohr            // first non-option
211cbeaa4a0SAndreas Gohr            if ($arg{0} != '-') {
212cbeaa4a0SAndreas Gohr                $non_opts = array_merge($non_opts, array_slice($this->args, $i));
213cbeaa4a0SAndreas Gohr                break;
214cbeaa4a0SAndreas Gohr            }
215cbeaa4a0SAndreas Gohr
216cbeaa4a0SAndreas Gohr            // long option
217cbeaa4a0SAndreas Gohr            if (strlen($arg) > 1 && $arg{1} == '-') {
218cbeaa4a0SAndreas Gohr                $arg = explode('=', substr($arg, 2), 2);
219cbeaa4a0SAndreas Gohr                $opt = array_shift($arg);
220cbeaa4a0SAndreas Gohr                $val = array_shift($arg);
221cbeaa4a0SAndreas Gohr
222cbeaa4a0SAndreas Gohr                if (!isset($this->setup[$this->command]['opts'][$opt])) {
223cbeaa4a0SAndreas Gohr                    throw new Exception("No such option '$opt'", Exception::E_UNKNOWN_OPT);
224cbeaa4a0SAndreas Gohr                }
225cbeaa4a0SAndreas Gohr
226cbeaa4a0SAndreas Gohr                // argument required?
227cbeaa4a0SAndreas Gohr                if ($this->setup[$this->command]['opts'][$opt]['needsarg']) {
228cbeaa4a0SAndreas Gohr                    if (is_null($val) && $i + 1 < $argc && !preg_match('/^--?[\w]/', $this->args[$i + 1])) {
229cbeaa4a0SAndreas Gohr                        $val = $this->args[++$i];
230cbeaa4a0SAndreas Gohr                    }
231cbeaa4a0SAndreas Gohr                    if (is_null($val)) {
232cbeaa4a0SAndreas Gohr                        throw new Exception("Option $opt requires an argument",
233cbeaa4a0SAndreas Gohr                            Exception::E_OPT_ARG_REQUIRED);
234cbeaa4a0SAndreas Gohr                    }
235cbeaa4a0SAndreas Gohr                    $this->options[$opt] = $val;
236cbeaa4a0SAndreas Gohr                } else {
237cbeaa4a0SAndreas Gohr                    $this->options[$opt] = true;
238cbeaa4a0SAndreas Gohr                }
239cbeaa4a0SAndreas Gohr
240cbeaa4a0SAndreas Gohr                continue;
241cbeaa4a0SAndreas Gohr            }
242cbeaa4a0SAndreas Gohr
243cbeaa4a0SAndreas Gohr            // short option
244cbeaa4a0SAndreas Gohr            $opt = substr($arg, 1);
245cbeaa4a0SAndreas Gohr            if (!isset($this->setup[$this->command]['short'][$opt])) {
246cbeaa4a0SAndreas Gohr                throw new Exception("No such option $arg", Exception::E_UNKNOWN_OPT);
247cbeaa4a0SAndreas Gohr            } else {
248cbeaa4a0SAndreas Gohr                $opt = $this->setup[$this->command]['short'][$opt]; // store it under long name
249cbeaa4a0SAndreas Gohr            }
250cbeaa4a0SAndreas Gohr
251cbeaa4a0SAndreas Gohr            // argument required?
252cbeaa4a0SAndreas Gohr            if ($this->setup[$this->command]['opts'][$opt]['needsarg']) {
253cbeaa4a0SAndreas Gohr                $val = null;
254cbeaa4a0SAndreas Gohr                if ($i + 1 < $argc && !preg_match('/^--?[\w]/', $this->args[$i + 1])) {
255cbeaa4a0SAndreas Gohr                    $val = $this->args[++$i];
256cbeaa4a0SAndreas Gohr                }
257cbeaa4a0SAndreas Gohr                if (is_null($val)) {
258cbeaa4a0SAndreas Gohr                    throw new Exception("Option $arg requires an argument",
259cbeaa4a0SAndreas Gohr                        Exception::E_OPT_ARG_REQUIRED);
260cbeaa4a0SAndreas Gohr                }
261cbeaa4a0SAndreas Gohr                $this->options[$opt] = $val;
262cbeaa4a0SAndreas Gohr            } else {
263cbeaa4a0SAndreas Gohr                $this->options[$opt] = true;
264cbeaa4a0SAndreas Gohr            }
265cbeaa4a0SAndreas Gohr        }
266cbeaa4a0SAndreas Gohr
267cbeaa4a0SAndreas Gohr        // parsing is now done, update args array
268cbeaa4a0SAndreas Gohr        $this->args = $non_opts;
269cbeaa4a0SAndreas Gohr
270cbeaa4a0SAndreas Gohr        // if not done yet, check if first argument is a command and reexecute argument parsing if it is
271cbeaa4a0SAndreas Gohr        if (!$this->command && $this->args && isset($this->setup[$this->args[0]])) {
272cbeaa4a0SAndreas Gohr            // it is a command!
273cbeaa4a0SAndreas Gohr            $this->command = array_shift($this->args);
274cbeaa4a0SAndreas Gohr            $this->parseOptions(); // second pass
275cbeaa4a0SAndreas Gohr        }
276cbeaa4a0SAndreas Gohr    }
277cbeaa4a0SAndreas Gohr
278cbeaa4a0SAndreas Gohr    /**
279cbeaa4a0SAndreas Gohr     * Get the value of the given option
280cbeaa4a0SAndreas Gohr     *
281cbeaa4a0SAndreas Gohr     * Please note that all options are accessed by their long option names regardless of how they were
282cbeaa4a0SAndreas Gohr     * specified on commandline.
283cbeaa4a0SAndreas Gohr     *
284cbeaa4a0SAndreas Gohr     * Can only be used after parseOptions() has been run
285cbeaa4a0SAndreas Gohr     *
286cbeaa4a0SAndreas Gohr     * @param mixed $option
287cbeaa4a0SAndreas Gohr     * @param bool|string $default what to return if the option was not set
288*43d3f077SAndreas Gohr     * @return bool|string|string[]
289cbeaa4a0SAndreas Gohr     */
290cbeaa4a0SAndreas Gohr    public function getOpt($option = null, $default = false)
291cbeaa4a0SAndreas Gohr    {
292cbeaa4a0SAndreas Gohr        if ($option === null) {
293cbeaa4a0SAndreas Gohr            return $this->options;
294cbeaa4a0SAndreas Gohr        }
295cbeaa4a0SAndreas Gohr
296cbeaa4a0SAndreas Gohr        if (isset($this->options[$option])) {
297cbeaa4a0SAndreas Gohr            return $this->options[$option];
298cbeaa4a0SAndreas Gohr        }
299cbeaa4a0SAndreas Gohr        return $default;
300cbeaa4a0SAndreas Gohr    }
301cbeaa4a0SAndreas Gohr
302cbeaa4a0SAndreas Gohr    /**
303cbeaa4a0SAndreas Gohr     * Return the found command if any
304cbeaa4a0SAndreas Gohr     *
305cbeaa4a0SAndreas Gohr     * @return string
306cbeaa4a0SAndreas Gohr     */
307cbeaa4a0SAndreas Gohr    public function getCmd()
308cbeaa4a0SAndreas Gohr    {
309cbeaa4a0SAndreas Gohr        return $this->command;
310cbeaa4a0SAndreas Gohr    }
311cbeaa4a0SAndreas Gohr
312cbeaa4a0SAndreas Gohr    /**
313cbeaa4a0SAndreas Gohr     * Get all the arguments passed to the script
314cbeaa4a0SAndreas Gohr     *
315cbeaa4a0SAndreas Gohr     * This will not contain any recognized options or the script name itself
316cbeaa4a0SAndreas Gohr     *
317cbeaa4a0SAndreas Gohr     * @return array
318cbeaa4a0SAndreas Gohr     */
319cbeaa4a0SAndreas Gohr    public function getArgs()
320cbeaa4a0SAndreas Gohr    {
321cbeaa4a0SAndreas Gohr        return $this->args;
322cbeaa4a0SAndreas Gohr    }
323cbeaa4a0SAndreas Gohr
324cbeaa4a0SAndreas Gohr    /**
325cbeaa4a0SAndreas Gohr     * Builds a help screen from the available options. You may want to call it from -h or on error
326cbeaa4a0SAndreas Gohr     *
327cbeaa4a0SAndreas Gohr     * @return string
328*43d3f077SAndreas Gohr     *
329*43d3f077SAndreas Gohr     * @throws Exception
330cbeaa4a0SAndreas Gohr     */
331cbeaa4a0SAndreas Gohr    public function help()
332cbeaa4a0SAndreas Gohr    {
333cbeaa4a0SAndreas Gohr        $tf = new TableFormatter($this->colors);
334cbeaa4a0SAndreas Gohr        $text = '';
335cbeaa4a0SAndreas Gohr
336cbeaa4a0SAndreas Gohr        $hascommands = (count($this->setup) > 1);
337cbeaa4a0SAndreas Gohr        foreach ($this->setup as $command => $config) {
338cbeaa4a0SAndreas Gohr            $hasopts = (bool)$this->setup[$command]['opts'];
339cbeaa4a0SAndreas Gohr            $hasargs = (bool)$this->setup[$command]['args'];
340cbeaa4a0SAndreas Gohr
341cbeaa4a0SAndreas Gohr            // usage or command syntax line
342cbeaa4a0SAndreas Gohr            if (!$command) {
343cbeaa4a0SAndreas Gohr                $text .= $this->colors->wrap('USAGE:', Colors::C_BROWN);
344cbeaa4a0SAndreas Gohr                $text .= "\n";
345cbeaa4a0SAndreas Gohr                $text .= '   ' . $this->bin;
346cbeaa4a0SAndreas Gohr                $mv = 2;
347cbeaa4a0SAndreas Gohr            } else {
348cbeaa4a0SAndreas Gohr                $text .= "\n";
349cbeaa4a0SAndreas Gohr                $text .= $this->colors->wrap('   ' . $command, Colors::C_PURPLE);
350cbeaa4a0SAndreas Gohr                $mv = 4;
351cbeaa4a0SAndreas Gohr            }
352cbeaa4a0SAndreas Gohr
353cbeaa4a0SAndreas Gohr            if ($hasopts) {
354cbeaa4a0SAndreas Gohr                $text .= ' ' . $this->colors->wrap('<OPTIONS>', Colors::C_GREEN);
355cbeaa4a0SAndreas Gohr            }
356cbeaa4a0SAndreas Gohr
357cbeaa4a0SAndreas Gohr            if (!$command && $hascommands) {
358cbeaa4a0SAndreas Gohr                $text .= ' ' . $this->colors->wrap('<COMMAND> ...', Colors::C_PURPLE);
359cbeaa4a0SAndreas Gohr            }
360cbeaa4a0SAndreas Gohr
361cbeaa4a0SAndreas Gohr            foreach ($this->setup[$command]['args'] as $arg) {
362cbeaa4a0SAndreas Gohr                $out = $this->colors->wrap('<' . $arg['name'] . '>', Colors::C_CYAN);
363cbeaa4a0SAndreas Gohr
364cbeaa4a0SAndreas Gohr                if (!$arg['required']) {
365cbeaa4a0SAndreas Gohr                    $out = '[' . $out . ']';
366cbeaa4a0SAndreas Gohr                }
367cbeaa4a0SAndreas Gohr                $text .= ' ' . $out;
368cbeaa4a0SAndreas Gohr            }
369cbeaa4a0SAndreas Gohr            $text .= "\n";
370cbeaa4a0SAndreas Gohr
371cbeaa4a0SAndreas Gohr            // usage or command intro
372cbeaa4a0SAndreas Gohr            if ($this->setup[$command]['help']) {
373cbeaa4a0SAndreas Gohr                $text .= "\n";
374cbeaa4a0SAndreas Gohr                $text .= $tf->format(
375cbeaa4a0SAndreas Gohr                    array($mv, '*'),
376cbeaa4a0SAndreas Gohr                    array('', $this->setup[$command]['help'] . "\n")
377cbeaa4a0SAndreas Gohr                );
378cbeaa4a0SAndreas Gohr            }
379cbeaa4a0SAndreas Gohr
380cbeaa4a0SAndreas Gohr            // option description
381cbeaa4a0SAndreas Gohr            if ($hasopts) {
382cbeaa4a0SAndreas Gohr                if (!$command) {
383cbeaa4a0SAndreas Gohr                    $text .= "\n";
384cbeaa4a0SAndreas Gohr                    $text .= $this->colors->wrap('OPTIONS:', Colors::C_BROWN);
385cbeaa4a0SAndreas Gohr                }
386cbeaa4a0SAndreas Gohr                $text .= "\n";
387cbeaa4a0SAndreas Gohr                foreach ($this->setup[$command]['opts'] as $long => $opt) {
388cbeaa4a0SAndreas Gohr
389cbeaa4a0SAndreas Gohr                    $name = '';
390cbeaa4a0SAndreas Gohr                    if ($opt['short']) {
391cbeaa4a0SAndreas Gohr                        $name .= '-' . $opt['short'];
392cbeaa4a0SAndreas Gohr                        if ($opt['needsarg']) {
393cbeaa4a0SAndreas Gohr                            $name .= ' <' . $opt['needsarg'] . '>';
394cbeaa4a0SAndreas Gohr                        }
395cbeaa4a0SAndreas Gohr                        $name .= ', ';
396cbeaa4a0SAndreas Gohr                    }
397cbeaa4a0SAndreas Gohr                    $name .= "--$long";
398cbeaa4a0SAndreas Gohr                    if ($opt['needsarg']) {
399cbeaa4a0SAndreas Gohr                        $name .= ' <' . $opt['needsarg'] . '>';
400cbeaa4a0SAndreas Gohr                    }
401cbeaa4a0SAndreas Gohr
402cbeaa4a0SAndreas Gohr                    $text .= $tf->format(
403cbeaa4a0SAndreas Gohr                        array($mv, '30%', '*'),
404cbeaa4a0SAndreas Gohr                        array('', $name, $opt['help']),
405cbeaa4a0SAndreas Gohr                        array('', 'green', '')
406cbeaa4a0SAndreas Gohr                    );
407cbeaa4a0SAndreas Gohr                    $text .= "\n";
408cbeaa4a0SAndreas Gohr                }
409cbeaa4a0SAndreas Gohr            }
410cbeaa4a0SAndreas Gohr
411cbeaa4a0SAndreas Gohr            // argument description
412cbeaa4a0SAndreas Gohr            if ($hasargs) {
413cbeaa4a0SAndreas Gohr                if (!$command) {
414cbeaa4a0SAndreas Gohr                    $text .= "\n";
415cbeaa4a0SAndreas Gohr                    $text .= $this->colors->wrap('ARGUMENTS:', Colors::C_BROWN);
416cbeaa4a0SAndreas Gohr                }
417cbeaa4a0SAndreas Gohr                $text .= "\n";
418cbeaa4a0SAndreas Gohr                foreach ($this->setup[$command]['args'] as $arg) {
419cbeaa4a0SAndreas Gohr                    $name = '<' . $arg['name'] . '>';
420cbeaa4a0SAndreas Gohr
421cbeaa4a0SAndreas Gohr                    $text .= $tf->format(
422cbeaa4a0SAndreas Gohr                        array($mv, '30%', '*'),
423cbeaa4a0SAndreas Gohr                        array('', $name, $arg['help']),
424cbeaa4a0SAndreas Gohr                        array('', 'cyan', '')
425cbeaa4a0SAndreas Gohr                    );
426cbeaa4a0SAndreas Gohr                }
427cbeaa4a0SAndreas Gohr            }
428cbeaa4a0SAndreas Gohr
429cbeaa4a0SAndreas Gohr            // head line and intro for following command documentation
430cbeaa4a0SAndreas Gohr            if (!$command && $hascommands) {
431cbeaa4a0SAndreas Gohr                $text .= "\n";
432cbeaa4a0SAndreas Gohr                $text .= $this->colors->wrap('COMMANDS:', Colors::C_BROWN);
433cbeaa4a0SAndreas Gohr                $text .= "\n";
434cbeaa4a0SAndreas Gohr                $text .= $tf->format(
435cbeaa4a0SAndreas Gohr                    array($mv, '*'),
436cbeaa4a0SAndreas Gohr                    array('', 'This tool accepts a command as first parameter as outlined below:')
437cbeaa4a0SAndreas Gohr                );
438cbeaa4a0SAndreas Gohr                $text .= "\n";
439cbeaa4a0SAndreas Gohr            }
440cbeaa4a0SAndreas Gohr        }
441cbeaa4a0SAndreas Gohr
442cbeaa4a0SAndreas Gohr        return $text;
443cbeaa4a0SAndreas Gohr    }
444cbeaa4a0SAndreas Gohr
445cbeaa4a0SAndreas Gohr    /**
446cbeaa4a0SAndreas Gohr     * Safely read the $argv PHP array across different PHP configurations.
447cbeaa4a0SAndreas Gohr     * Will take care on register_globals and register_argc_argv ini directives
448cbeaa4a0SAndreas Gohr     *
449cbeaa4a0SAndreas Gohr     * @throws Exception
450cbeaa4a0SAndreas Gohr     * @return array the $argv PHP array or PEAR error if not registered
451cbeaa4a0SAndreas Gohr     */
452cbeaa4a0SAndreas Gohr    private function readPHPArgv()
453cbeaa4a0SAndreas Gohr    {
454cbeaa4a0SAndreas Gohr        global $argv;
455cbeaa4a0SAndreas Gohr        if (!is_array($argv)) {
456cbeaa4a0SAndreas Gohr            if (!@is_array($_SERVER['argv'])) {
457cbeaa4a0SAndreas Gohr                if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
458cbeaa4a0SAndreas Gohr                    throw new Exception(
459cbeaa4a0SAndreas Gohr                        "Could not read cmd args (register_argc_argv=Off?)",
460cbeaa4a0SAndreas Gohr                        Exception::E_ARG_READ
461cbeaa4a0SAndreas Gohr                    );
462cbeaa4a0SAndreas Gohr                }
463cbeaa4a0SAndreas Gohr                return $GLOBALS['HTTP_SERVER_VARS']['argv'];
464cbeaa4a0SAndreas Gohr            }
465cbeaa4a0SAndreas Gohr            return $_SERVER['argv'];
466cbeaa4a0SAndreas Gohr        }
467cbeaa4a0SAndreas Gohr        return $argv;
468cbeaa4a0SAndreas Gohr    }
469cbeaa4a0SAndreas Gohr}
470cbeaa4a0SAndreas Gohr
471