A PHP Command-line interface parser

A PHP Command-line interface parser

Today, I decided to build a simple, but highly parametrable script that takes a bunch of images and resizes them with a given method. My first problem was : how to make a good command line arguments parser, so that I don’t have to write each argument in a specific order, or not putting some arguments at all, using a default value…
In bash, this is quite simple, but I wanted to use PHP for the script, so that I could use the GD library that I know well.

Then I found this class, made by Diego Feitosa, that looked nice, but wasn’t exactly what I wanted : I was forced to use an option (i.e. “-something <param>”) for each argument in the command line. It is nice for defining the behaviour of the script, but if you want to script something that process files, you just can’t – or only one file at a time.

In fact, this is exactly what I was doing : processing a lot of files. But with this parser, I couldn’t. So I decided to hack it a bit…

This code is an extended version of the CLI Parser of Diego Feitosa, which allows the user to pass arguments that are not options. I also made other modifications to the script :

  • added a method to check if an argument is valid as a non option (by default, everything that doesn’t start with ‘-‘ can be a non-option)
  • improved usage description : the type awaited by an option is shown, better indentation
  • improved error messages : if the type in the command line doesn’t match with an option, the error is shown. In the version of Diego, only the usage message was shown.
  • auto-inclusion of the option types
  • new option types : Positive Numeric, Negative Numeric, Integers, Enum

You can find the code here.

Here is an example of the usage of this parser. This example is also located in the source code provided above :

require_once('CliParser.inc');
$clistring = new CliTokenString("-c");
$clistring->setDescription("It requires a string");

$clihelp = new CliTokenBoolean("--help");
$clihelp->setDescription("Token that shows a help message");

$clisingleton = new CliTokenBoolean("-e");
$clisingleton->setDescription("It don't require any value. The existence of this argument is enough");

$clibool = new CliTokenBoolean("-b");
$clibool->setDescription("Boolean token");

$clidir = new CliTokenDirectory("-d");
$clidir->setDescription("This token require a directory path as argument. If the argument isn't a directory path, an error message will appear.");

$clifile = new CliTokenDirectory("-f");
$clifile->setDescription("This token require a file path as argument. If the argument isn't a file path, an error message will appear.");

$cliint = new CliTokenInteger("-i");
$cliint->setDescription("This token require an integer path as argument. If the argument isn't an integer, an error message will appear.");

$clienum = new CliTokenEnum("-enum", array('the', 'different', 'values', 'accepted'));
$clienum->setDescription("This token requires its argument to be one of the values specified");

class MyCliParser extends CliParser
{
  public function getHelpMessage()
  {
    global $argv;
    echo sprintf("Usage: %s [options] <file(s)>\nOptions :\n", $argv[0]);
    $this->getDescriptions();
    echo "\n";
    exit;
  }
}

//Building the parser and parsing the arguments
$cli = new MyCliParser($_SERVER["argv"]);
$cli->register($clihelp, false); // false because it not require an argument
$cli->register($clistring);
$cli->register($clisingleton, false); // false because it not require an argument
$cli->register($clibool);
$cli->register($clidir);
$cli->register($clifile);
$cli->register($cliint);
$cli->register($clienum);
$cli->parse();

//Showing the help message if asked
if ($clihelp->getValue())
{
  $cli->getHelpMessage();
  exit;
}

//Showing the options
var_dump($clistring->getValue());
var_dump($clisingleton->getValue());
var_dump($clibool->getValue());
var_dump($clidir->getValue());
var_dump($clifile->getValue());
var_dump($cliint->getValue());

//Showing the non options
var_dump($cli->getNonOptions());

/* Some commands :
php example.php --help
=> will show the help message

php example.php -c foo
=> the $clistring token will be set

php example.php -e -c foo
=> the $clisingleton token will be set

php example.php nonoption1 -i 42
=> one non-option argument : nonoption1

php example.php -i 42 nonoption1
=> same thing, different order

php example.php -i 42.5
=> shows an error : 42.5 is not an integer
*/

3 thoughts on “A PHP Command-line interface parser”

  1. So, are you primarily a Java programmer? ;)

    Seriously, dude, this is way too much code for setup…

    1. This is just an example, showing what can be done with it. You can make this code way smaller.
      Beside, you can reduce the size of the code if you want just a simple command line parser :

      you can remove the description of each argument if you don’t need them
      you can get rid of the extension of the parser class, if you don’t need a different help message
      you can remove the help message shown by default

      … which will give you this shorter piece of code :

      require_once(‘CliParser.inc’);

      $clistring = new CliTokenString(“-c”);
      $clihelp = new CliTokenBoolean(“–help”);
      $clisingleton = new CliTokenBoolean(“-e”);
      $clibool = new CliTokenBoolean(“-b”);
      $clidir = new CliTokenDirectory(“-d”);
      $clifile = new CliTokenDirectory(“-f”);
      $cliint = new CliTokenInteger(“-i”);
      $clienum = new CliTokenEnum(“-enum”, array(‘the’, ‘different’, ‘values’, ‘accepted’));

      //Building the parser and parsing the arguments
      $cli = new CliParser($_SERVER[“argv”]);
      $cli->register($clihelp, false); // false because it not require an argument
      $cli->register($clistring);
      $cli->register($clisingleton, false); // false because it not require an argument
      $cli->register($clibool);
      $cli->register($clidir);
      $cli->register($clifile);
      $cli->register($cliint);
      $cli->register($clienum);
      $cli->parse();

      //Showing the options
      var_dump($clistring->getValue());
      var_dump($clisingleton->getValue());
      var_dump($clibool->getValue());
      var_dump($clidir->getValue());
      var_dump($clifile->getValue());
      var_dump($cliint->getValue());

      //Showing the non options
      var_dump($cli->getNonOptions());

      But yes, you can make smaller, dirtier, non-reusable code.
      Also, remember that it is an extension of something already existing. Most of the setup code doesn’t come from me.

      However, what would you do to make this smaller ?

  2. Je partage un peu l’avis de Teerd Phyre : c’est clair que c’est plus digeste avec argstream en c++ par exemple. Ca gère notamment les listes d’arguments. Perso, je raccourcirai le code en passant la description dans les constructeurs et en allouant les variables dans la fonction register() pour plus de lisibilitĂ©.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>