Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP Command Line Arguments and Options

Tags:

php

I am writing a small command line application in php.

What is the correct way to handle command line arguments and options?

There seems to be the argv array, $_SERVER['argv'], and getopt but its confusing when to use each?

Also with regards to options i.e. "argument --option" what is the best way to get these?

like image 475
Marty Wallace Avatar asked Aug 20 '12 19:08

Marty Wallace


2 Answers

Arguments, made easy

One day I decided to defeat this monster once and for all. I forged a secret weapon - a function that acts as a storage, a parser and a query function for arguments.


    //  You can initialize it with a multiline string:

    arg("

        -a  --alpha         bool    Some explanation about this option
        -b  --beta          bool    Beta has some notes too
        -n  --number        int     Some number you need for the script
        -   --douglas       int     There is no short form of this
        -o  --others        str     A string of other things

    ");

    //  ... and now you have your arguments nicely wrapped up:

    print arg("alpha");        //  returns the value of -a or --alpha
    print arg("a");            //  same thing
    print arg();               //  returns the whole parsed array
    print arg(1);              //  returns the first unnamed argument
    print arg(2);              //  returns the second unnamed argument
    print arg("douglas",42);   //  value of "douglas", or a reasonable default

Explanation

  • All you need to do is write the argument list as a multiline string. Four columns, looks like a help, but arg() parses your lines and finds out the arguments automatically.

  • Separate columns by two or more spaces - just like you would anyway.

  • Once parsed, each item will be represented by an array of fields, named char, word, type and help, respectively. If there's no short (char) or long (word) version for a parameter, just use a dash. Not for both, obviously.

  • Types are what they seem: bool means there's no value after the parameter; it's false if missing, true if present. The int and str types mean there must be a value, and int makes sure it's an integer. Optional parameters are not supported. Values can be separated by space or equal sign (i.e. "-a=4" or "-a 4")

  • After this first call, you have all your arguments neatly organized in a structure (dump it, you'll see) and you can query their values by name or number.

  • Function arg() has a second parameter for defaults so you'll never have to worry about missing values.

The arg() function itself

function arg($x="",$default=null) {

    static $arginfo = [];

    /* helper */ $contains = function($h,$n) {return (false!==strpos($h,$n));};
    /* helper */ $valuesOf = function($s) {return explode(",",$s);};

    //  called with a multiline string --> parse arguments
    if($contains($x,"\n")) {

        //  parse multiline text input
        $args = $GLOBALS["argv"] ?: [];
        $rows = preg_split('/\s*\n\s*/',trim($x));
        $data = $valuesOf("char,word,type,help");
        foreach($rows as $row) {
            list($char,$word,$type,$help) = preg_split('/\s\s+/',$row);
            $char = trim($char,"-");
            $word = trim($word,"-");
            $key  = $word ?: $char ?: ""; if($key==="") continue;
            $arginfo[$key] = compact($data);
            $arginfo[$key]["value"] = null;
        }

        $nr = 0;
        while($args) {

            $x = array_shift($args); if($x[0]<>"-") {$arginfo[$nr++]["value"]=$x;continue;}
            $x = ltrim($x,"-");
            $v = null; if($contains($x,"=")) list($x,$v) = explode("=",$x,2);
            $k = "";foreach($arginfo as $k=>$arg) if(($arg["char"]==$x)||($arg["word"]==$x)) break;
            $t = $arginfo[$k]["type"];
            switch($t) {
                case "bool" : $v = true; break;
                case "str"  : if(is_null($v)) $v = array_shift($args); break;
                case "int"  : if(is_null($v)) $v = array_shift($args); $v = intval($v); break;
            }
            $arginfo[$k]["value"] = $v;

        }

        return $arginfo;

    }

    //  called with a question --> read argument value
    if($x==="") return $arginfo;
    if(isset($arginfo[$x]["value"])) return $arginfo[$x]["value"];
    return $default;

}

I hope this helps a lot of lost souls out there, like I was. May this little function shed a light upon the beauty of not having to write a help AND a parser and keeping them in sync... Also, once parsed, this approach is lightning fast since it caches the variables so you can call it as many times as you want. It acts like a superglobal.

Also available on my GitHub Gist.

like image 35
dkellner Avatar answered Sep 20 '22 23:09

dkellner


You can retrieve the "raw" arguments using $argv. See also: http://www.php.net/manual/de/reserved.variables.argv.php

Example: php file.php a b c

$argv will contain "file.php", "a", "b" and "c".

Use getopts to get the parameters "parsed", PHP will do the dirty job for you. So it's probably the best way to go in your case as you want to pass the parameters with --options. Have a close look at http://www.php.net/manual/de/function.getopt.php It describes the function well.

like image 137
Daniel M Avatar answered Sep 23 '22 23:09

Daniel M