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