I've been using CLImate at work lately. I love it, but I hated having to use getopt()
or $argv
to talk to my command line arguments. I looked into various console libraries, but they also did output in addition to argument processing. I saw in #29 you said you weren't opposed to CLImate doing argument parsing, so I added it. I tried to follow the existing API and internal code conventions as much as I can.
This change adds an argument manager to the CLImate object. Users can define arguments against the argument manager, parse arguments on the command line into those defined arguments, retrieve CLI argument values, and generate a usage statement based on user defined arguments.
An argument is defined as something that's passed along to a script, either in a short form ("-a foo"), long form ("--foo bar"), both, or neither to process arguments after the prefixed argument list. Arguments can be:
- marked as required
- have default values
- have their values cast to string, int, float, or boolean data types
- set true if they're defined on the command line (for cases where a value isn't needed like "-f" to force or "--help" to show a usage statement).
- have values set in "-key value" or "-key=value" form on the command line.
Arguments that are required but not defined on the command line generate an exception that's sent back to calling code.
Here's a simple example. Assume you have a script that pushes a local directory to some SSH server. That script needs a username, a password, a host, maybe an option to print usage, one to overwrite existing files, maybe a verbosity option, and a path to push to my server. I may store the username and hostname internally, but let users enter their own if they like, but they have to provide a password. I'd probably call it by running:
php ./pusher.php -u myusername -p secret -h hostname --force -v ./push_me
That script would look like:
<?php
require __DIR__ . '/vendor/autoload.php';
use League\CLImate\CLImate;
$climate = new CLImate;
$climate->description('Awesome SCP Pusher v0.01');
$climate->arguments->add([
'user' => [
'prefix' => 'u',
'longPrefix' => 'user',
'description' => 'SSH Username',
'defaultValue' => 'myuser',
],
'password' => [
'prefix' => 'p',
'longPrefix' => 'password',
'description' => 'SSH Password',
'required' => true,
],
'host' => [
'prefix' => 'H',
'longPrefix' => 'host',
'description' => 'SSH Hostname',
'defaultValue' => 'myhost.local'
],
'force' => [
'prefix' => 'f',
'longPrefix' => 'force',
'description' => 'Overwrite files on copy',
'definedOnly' => true,
],
'verbose' => [
'prefix' => 'v',
'longPrefix' => 'verbose',
'description' => 'Verbose output',
'definedOnly' => true,
],
'help' => [
'prefix' => 'h',
'longPrefix' => 'help',
'description' => 'Print a usage statement',
'definedOnly' => true,
],
'path' => [
'description' => 'The path to push',
],
]);
// Print a usage statement on --help
if ($climate->arguments->defined('help')) {
$climate->usage();
exit();
}
// Parse arguments.
try {
$climate->arguments->parse();
} catch (Exception $e) {
// Let us know if arguments were missing.
$climate->error($e->getMessage());
$climate->br();
$climate->usage();
exit();
}
// Echo instead of push.
$climate->out('Username: ' . $climate->arguments->get('user'));
$climate->out('Hostname: ' . $climate->arguments->get('host'));
if ($climate->arguments->get('force')) {
$climate->out("I'm going to overwrite on copy.");
}
Here's an execution that echo's the usage statement:
$ php ./pusher.php -h
Awesome SCP Pusher v0.01
Usage: ./pusher.php [-f, --force] [-H host, --host host (default: myhost.local)] [-h, --help] [-p password, --password password] [-u user, --user user (default: myuser)] [-v, --verbose] [path]
Required Arguments:
-p password, --password password
SSH Password
Optional Arguments:
-f, --force
Overwrite files on copy
-h, --help
Print a usage statement
-H host, --host host (default: myhost.local)
SSH Hostname
-u user, --user user (default: myuser)
SSH Username
-v, --verbose
Verbose output
And an execution that spits out those debug out()
s:
$ php ./pusher.php -u bob -p secret -f ./my-dir
Username: bob
Hostname: myhost.local
I'm going to overwrite on copy.
The new changes pass all tests with 100% new code coverage, minus one line that needs to talk to the global scope to get $argv
. Tests use a mocked $argv
.
Let me know if this looks okay to you. If it does and gets merged I can write public-facing documentation for this too.