Populating the parser

Populating the parser --  how to add options, arguments and subcommands to the parser

Adding options

Some background

Options are used to provide extra information to tune or customize the execution of a program. In case it wasn't clear, options are usually optional. A program should be able to run just fine with no options whatsoever. Pick a random program from the Unix or GNU toolsets. Can it run without any options at all and still make sense? The main exceptions are find, tar, and dd--all of which are mutant oddballs that have been rightly criticized for their non-standard syntax and confusing interfaces.

Lots of people want their programs to have "required options". Think about it. If it's required, then it's not optional! If there is a piece of information that your program absolutely requires in order to run successfully, that's what arguments are for.

As an example of good command-line interface design, consider the humble cp utility, for copying files. It doesn't make much sense to try to copy files without supplying a destination and at least one source. Hence, cp fails if you run it with no arguments. However, it has a flexible, useful syntax that does not require any options at all:
$ cp SOURCE DEST
$ cp SOURCE ... DEST-DIR

You can get pretty far with just that. Most cp implementations provide a bunch of options to tweak exactly how the files are copied: you can preserve mode and modification time, avoid following symlinks, ask before clobbering existing files, etc. But none of this distracts from the core mission of cp, which is to copy either one file to another, or several files to another directory.

Adding options with Console_CommandLine

To add options to your parser, just create the parser as explained in the previous section and use the Console_CommandLine::addOption() method.

The Console_CommandLine::addOption() method takes two arguments:

Now if the user type:

$ <yourprogram> -vo rtl

or (equivalent):

$ <yourprogram> --verbose --orientation=rtl

The output of the above script will be:

Array
(
    [verbose] => 1
    [orientation] => rtl
    [help] => 
    [version] => 
)

Options actions

Actions tell the parser how to handle option values, among other things they tell the parser if the option expects a value or not and how to store this value in the result object.

StoreTrue

This action tells the parser to store the value true in the result object if the option is present in the command line, for example:

$ <yourprogram> -v

will store TRUE in $result->options['verbose'], assuming the option was defined like this:

<?php
$parser->addOption('verbose', array('short_name'=>'-v', 'action'=>'StoreTrue'));
$result = $parser->parse();
?>

StoreFalse

This action tells the parser to store the value false in the result object if the option is present in the command line, for example:

$ <yourprogram> -q

will store FALSE in $result->options['verbose'], assuming the option was defined like this:

<?php
$parser->addOption('verbose', array('short_name'=>'-q', 'action'=>'StoreFalse'));
$result = $parser->parse();
?>

StoreString

This action tells the parser that the option expects a value and to store this value as an integer in the result object, for example:

$ <yourprogram> -o out.txt

will store the string "out.txt" in $result->options['outfile'], assuming the option was defined like this:

<?php
$parser->addOption('outfile', array('short_name'=>'-o', 'action'=>'StoreString'));
$result = $parser->parse();
?>

StoreInt

This action tells the parser that the option expects a value and to store this value as an integer in the result object, for example:

$ <yourprogram> --width=500

will store the integer 500 in $result->options['width'], assuming the option was defined like this:

<?php
$parser->addOption('width', array('long_name'=>'--width', 'action'=>'StoreInt'));
$result = $parser->parse();
?>

StoreFloat

This action tells the parser that the option expects a value and to store this value as a float in the result object, for example:

$ <yourprogram> -l=0.3

will store the float 0.3 in $result->options['level'], assuming the option was defined like this:

<?php
$parser->addOption('level', array('short_name'=>'-l', 'action'=>'StoreFloat'));
$result = $parser->parse();
?>

Counter

This action tells the parser to increment the value in the result object each time it encounters the option in the command line, for example:

$ <yourprogram> -vvvv

or the equivalent:

$ <yourprogram> -v -v -v --verbose

will store the integer 4 in $result->options['verbose_level'], assuming the option was defined like this:

<?php
$parser->addOption('verbose_level', array(
    'short_name' => '-v',
    'long_name'  => '--verbose',
    'action'     => 'Counter'
));
$result = $parser->parse();
?>

Help

This action tells the parser to display the help message if it encounters the option in the command line, most of the time you won't need this since it is handled by Console_CommandLine internally.

Version

This action tells the parser to display the program version if it encounters the option in the command line, as for Help action, chances are that you won't need this since it is handled by Console_CommandLine internally.

Password

This action allows the user to either type the password on the commandline or to be prompted for it (will not echo on unix systems), some examples:

<?php
$parser->addOption('password', array('short_name'=>'-p', 'action'=>'Password'));
$result = $parser->parse();
?>

$ <yourprogram> -ps3cret

will store the string "s3ecret" in $result->options['password']

whereas:

$ <yourprogram> -p

will "prompt" the user for entering his/her password without echoing it, and will store "s3ecret" in $result->options['password']

Callback

This action allows to specify a PHP callback to handle user input. The callback must be a php callable and must accept five arguments:

  • the value of the option;

  • the Console_CommandLine_Option_Callback instance

  • the Console_CommandLine_Result instance

  • the Console_CommandLine instance (the parser).

  • an array of additional parameters that you specify when building your option.

Your callback function must return the modified (or not modified) value (the first argument).

All these arguments should give you enough flexibility to build complex callback actions.

Here is a simple example:

<?php
/**
 * A simple encryption callback.
 *
 */
function encryptCallback($value, $option, $result, $parser, $params=array())
{
    if (!isset($params['salt'])) {
        $params['salt'] = '';
    }
    return sha1($params['salt'] . $value);
}

require_once 'Console/CommandLine.php';

$parser = new Console_CommandLine();
$parser->addOption('encrypt', array(
    'short_name'    => '-e',
    'long_name'     => '--encrypt',
    'action'        => 'Callback',
    'description'   => 'encrypt the given value using sha1 + salt',
    'callback'      => 'encryptCallback',
    'action_params' => array('salt' => 'x2897ZHKx7200j1__2')
));

try {
    $result = $parser->parse();
    echo $result->options['encrypt'] . "\n";
} catch (Exception $exc) {
    $parser->displayError($exc->getMessage());
}
?>

Now if the user type:

$ <yourprogram> -e foobar

The output of the above script will be:

7f12da3b1c126d7a47745b09dc0040c92cee1700

Adding arguments

Some background

Arguments are for those pieces of information that your program absolutely, positively requires to run.

A good user interface should have as few absolute requirements as possible. If your program requires 17 distinct pieces of information in order to run successfully, it doesn't much matter how you get that information from the user, most people will give up and walk away before they successfully run the program. This applies whether the user interface is a command-line, a configuration file, or a GUI: if you make that many demands on your users, most of them will simply give up.

In short, try to minimize the amount of information that users are absolutely required to supply and use sensible defaults whenever possible. Of course, you also want to make your programs reasonably flexible. That's what options are for.

Adding arguments with Console_CommandLine

To add arguments to your parser, just create the parser as explained in the previous section and use the Console_CommandLine::addArgument() method.

The Console_CommandLine::addArgument() method takes two arguments:

If the user type:

$ <yourprogram> file1 file2 file3

The output of the above script will be:

Array
(
    [input_files] => Array
        (
            [0] => file1
            [1] => file2
        )

    [output_file] => file3
)

Adding sub-commands

What are sub-commands ?

Some programs are very complex, you can't do anything about that but you need to provide the best user interface possible in order to have happy users. In some cases, sub-commands can be very useful, take the PEAR installer for example, it is much more clearer to separate the installer functionalities than to mix all functionalities in the same interface, that's why you have an interface like:

$ pear install <options> <pkgname>
$ pear upgrade <options> <pkgname>
and so on...

Adding sub-commands with Console_CommandLine

TODO: develop this section