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