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