Managing command line sub-commands

Managing command line sub-commands – how to add sub-commands to the parser and use them

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

Adding sub-commands is quite simple, basically you use the Console_CommandLine::addCommand() method that returns a Console_CommandLine_Command instance and then you build your command instance like you would do for the main parser (add options, arguments etc...). One thing to remember is that sub-commands are exactly the same as main parsers: they have the same properties and methods.

Let's take a simple example to demonstrate the use of sub-commands, in the following code we will build a simple command line program that will have two sub-commands: "foo" and "bar":

<?php
// Include the Console_CommandLine package.
require_once 'Console/CommandLine.php';

// create the parser
$parser = new Console_CommandLine(array(
   
'description' => 'A great program that can foo and bar !',
   
'version'     => '1.0.0'
));

// add a global option to make the program verbose
$parser->addOption('verbose', array(
   
'short_name'  => '-v',
   
'long_name'   => '--verbose',
   
'action'      => 'StoreTrue',
   
'description' => 'turn on verbose output'
));

// add the foo subcommand
$foo_cmd $parser->addCommand('foo', array(
   
'description' => 'output the given string with a foo prefix'
));
$foo_cmd->addOption('reverse', array(
   
'short_name'  => '-r',
   
'long_name'   => '--reverse',
   
'action'      => 'StoreTrue',
   
'description' => 'reverse the given string before echoing it'
));
$foo_cmd->addArgument('text', array(
   
'description' => 'the text to output'
));

// add the bar subcommand
$bar_cmd $parser->addCommand('bar', array(
   
'description' => 'output the given string with a bar prefix'
));
$bar_cmd->addOption('reverse', array(
   
'short_name'  => '-r',
   
'long_name'   => '--reverse',
   
'action'      => 'StoreTrue',
   
'description' => 'reverse the given string before echoing it'
));
$bar_cmd->addArgument('text', array(
   
'description' => 'the text to output'
));

?>

Of course, we could also build our parser with sub-commands using an xml file, the xml file would be:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<command>
   <description>A great program that can foo and bar !</description>
   <version>1.0.0</version>
   <option name="verbose">
       <short_name>-v</short_name>
       <long_name>--verbose</long_name>
       <description>turn on verbose output</description>
       <action>StoreTrue</action>
   </option>
   <command>
       <name>foo</name>
       <description>output the given string with a foo prefix</description>
       <option name="reverse">
           <short_name>-r</short_name>
           <long_name>--reverse</long_name>
           <description>reverse the string before echoing it</description>
           <action>StoreTrue</action>
       </option>
       <argument name="text">
           <description>the text to output</description>
       </argument>
   </command>
   <command>
       <name>bar</name>
       <description>output the given string with a bar prefix</description>
       <option name="reverse">
           <short_name>-r</short_name>
           <long_name>--reverse</long_name>
           <description>reverse the string before echoing it</description>
           <action>StoreTrue</action>
       </option>
       <argument name="text">
           <description>the text to output</description>
       </argument>
   </command>
</command>

Also note that sub-commands support aliases, for example it is possible to have an "update" command and specify "up" as an alias (shortcut) of the command:

<?php

$parser
->addCommand('update', array(
    
'description' => 'Update the given package',
    
'aliases'     => array('up'), // we can have more than 1 alias
));

?>

So far, so good, now let's see how to use this parser. When a user type a sub-command in the command line, the result object will have two properties set: the Console_CommandLine_Result::$command_name that will contain the name (as a string) of the sub-command typed by the user, and the Console_CommandLine_Result::$command that will contain a Console_CommandLine_Result instance, specific to the provided sub-command. For example with the above parser, we would do:

<?php

try {
   
$result $parser->parse();
   
// find which command was entered
   
switch ($result->command_name) {
   case 
'foo':
       
// the user typed the foo command
       // options and arguments for this command are stored in the
       // $result->command instance:
       
print_r($result->command);
       exit(
0);
   case 
'bar':
       
// the user typed the bar command
       // options and arguments for this command are stored in the
       // $result->command instance:
       
print_r($result->command);
       exit(
0);
   default:
       
// no command entered
       
exit(0);
   }
} catch (
Exception $exc) {
   
$parser->displayError($exc->getMessage());
}

?>
    Поддержать сайт на родительском проекте КГБ