Unix style options and flags with command line PHP
Published by Nicholas Dunbar on December 24th, 2013
Looking for a more intelligent way to parse in options from the command line into your PHP script other than $argv? For example, you may want to be able to place options in any order like the following:
php somescript.php -pv -o value
or
php somescript.php -p -o value -v
and have it still work the same.
I discovered a while ago, a great little script called OptionParser.php by Michael J. I. Jackson (found at https://github.com/mjijackson/optionparser) which helps parse in parameters from the command line, but it is a little limited (read on for the solution to the limitation). For example, it works well for situations like the following:
php somescript.php --option value
The contents of somescript.php being the following lines of code:
require_once("OptionParser.php");
// Initialize the command parser
$parser = new OptionParser();
// Set the rules
$parser->addRule('o|option::', "Enter -o or --option and then the value you want to print");
$parser->parse();
$opt = $parser->getOption('o');
$parser = new OptionParser();
// Set the rules
$parser->addRule('o|option::', "Enter -o or --option and then the value you want to print");
$parser->parse();
$opt = $parser->getOption('o');
echo $opt;
Which will output from our command line example above:
value
But the problem with the OptionParser.php is it does not support blank options like:
php somescript.php --option
or grouped blank options (grouped flags) like the following example:
php somescript.php -o value -vp
These don't need a value as they are simply boolean on or off values.
-o value : print value
-v : yes verbose
-p : yes print to log
(these are just possible examples)
So to support blank flags or options in groups, in concert with the wonderful code of the OptionParser.php, I have programmed a helper class. This is how it works (added code to example somescript.php in bold):
require_once("blankOptions.php");
require_once("OptionParser.php");
//Preserve options without parameters
$blankOptions = new BlankOptions();
//Convert -vp to -v -p
$blankOptions->convertGroupedOptions();
//Convert -v -p to -v blank -p blank
$blankOptions->add('-v');
$blankOptions->add('-p');
$blankOptions = new BlankOptions();
//Convert -vp to -v -p
$blankOptions->convertGroupedOptions();
//Convert -v -p to -v blank -p blank
$blankOptions->add('-v');
$blankOptions->add('-p');
$blankOptions->add('--verbose');
$blankOptions->add('--print');
$blankOptions->add('--print');
//Initialize the command parser
$parser = new OptionParser();
//Set the rules
$parser->addRule('o|option::', "Enter -o or --option and then the value you want to print");
$parser = new OptionParser();
//Set the rules
$parser->addRule('o|option::', "Enter -o or --option and then the value you want to print");
$parser->addRule('v|verbose::', "Enter -v or --verbose to set verbose mode");
$parser->addRule('p|print::', "Enter -p or --print to not print to log");
$parser->parse();
$opt = $parser->getOption('o');
$parser->parse();
$opt = $parser->getOption('o');
echo $opt;
$opt2 = $parser->getOption('v');
if ($opt2 != FALSE){
if ($opt2 != FALSE){
//set verbose mode
}
$opt3 = $parser->getOption('p');
if ($opt3 != FALSE){
if ($opt3 != FALSE){
//don't print to log
}Here is the code for blankOptions.php:
/**
* If blank options are included place holder data is added so that the
* OptionParser {@link OptionParser.php} does not remove those options.
*
* Convert arguments of the style -abc to -a -b -c
* Convert arguments of the style -a -b -c to -a blank -b blank -c blank
*
* This is so that OptionParser.php can handle unix style options like
* grouped options (ex: -abc) and blank options -a -b -c
*/
/**
* Add in blank parameters into options left blank
*
* @author nicholas dunbar
*/
class BlankOptions {
/**
* Create a utility object that works with OptionParser.php
*/
public function __construct() {
}
/**
* This adds a parameter after any flags that may have been left blank
* usually $_SERVER['argv']
*
* @param string $flag
* The flag name usually starting with a - "dash" in the command line
* @param array $argv
* list of argument variables that were passed in from the command line
* @param string $param
* The value of the flag. Example: --print true
* @return array
* $_SERVER['argv'] with the transformations applied
*/
public function add($flag, array &$argv = NULL, $param = 'blank'){
if ($argv === NULL) {
if (isset($_SERVER['argv'])) {
$argv = $_SERVER['argv'];
} else {
$argv = array();
}
}
//Detect things like -h or -p since the OptionParser removes options if they are empty
$lowercase_argv = array_map('strtolower', $argv);
$index = array_search($flag, $lowercase_argv);
if ($index !== FALSE){
if ( !(isset($argv[$index+1])) || (substr($argv[$index+1], 0, 1) == '-') ){
array_splice($argv, $index+1, 0, array($param));
$_SERVER['argv'] = $argv;
}
}
return $_SERVER['argv'];
}
/**
* Convert arguments of the style -abc to -a -b -c
*
* @param array $argv
* Pass by reference the argument array from $_SERVER['argv']
* @return array
* $_SERVER['argv'] with the transformations applied
*/
public function convertGroupedOptions(array $argv = NULL){
if ($argv === NULL) {
if (isset($_SERVER['argv'])) {
$argv = $_SERVER['argv'];
} else {
$argv = array();
}
}
$new_argv = array();
while ($option = array_pop($argv)){
$matches = array();
echo preg_match('/^(-{1})([a-z]+)$/i', $option, $matches)."\n";
if
(
strlen($option) > 2 &&
preg_match('/^(-{1})([a-z]+)$/i', $option, $matches)
)
{
if (isset($matches[2])){
$chars = str_split($matches[2]);
while ($one_char_opt = array_pop($chars)){
array_unshift($new_argv, '-'.$one_char_opt);
}
} else {
array_unshift($new_argv, $option);
}
} else {
array_unshift($new_argv, $option);
}
}
$argv = $new_argv;
$_SERVER['argv'] = $new_argv;
return $_SERVER['argv'];
}
}