douglasgreen / opt-parser
Command-line option parser for PHP
Installs: 148
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
Type:project
pkg:composer/douglasgreen/opt-parser
Requires
- php: >=8.3
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.93.1
- phpstan/phpstan: ^2.1.38
- phpunit/phpunit: ^12.5.8
- rector/rector: ^2.3.6
README
A POSIX.1-2017 compliant command-line option parser for PHP with GNU extensions.
Overview
OptParser implements the IEEE Std 1003.1-2017 (POSIX.1-2017) utility conventions, supporting:
- Standard option syntax: Short options (
-a), clustering (-abc), and arguments (-a valueor-a=value) - GNU extensions: Long options (
--option,--option=value) for enhanced readability - Strict separation: stdout for data, stderr for diagnostics
- Standard exit codes:
0(success),2(usage error),130(SIGINT), etc. - Type safety: Built-in validation for 20+ data types (paths, dates, networks, etc.)
Unlike PHP's built-in getopt(), OptParser supports positional arguments (terms), subcommands,
automatic help generation, and strict validation with descriptive error messages.
Installation
composer require douglasgreen/opt-parser
POSIX Compliance
This library adheres to POSIX.1-2017 Section 12.2 (Utility Syntax Guidelines) and XCU Section 12.2 (Utility Conventions):
Option Syntax
| Syntax | Description | Example |
|---|---|---|
-a |
Short option | -v (verbose) |
-abc |
Clustered options (equivalent to -a -b -c) |
-vrf (verbose, recursive, force) |
-a value |
Option with argument (space-separated) | -o output.txt |
-a=value |
Option with argument (equals-separated) | -o=output.txt |
--long |
Long option (GNU extension) | --verbose |
--long=value |
Long option with argument | --output=file.txt |
-- |
Option terminator (remaining args are operands) | -- -filename-starting-with-dash |
Important: When using clustered options, if the last option in a cluster requires an argument,
the remaining characters are interpreted as the argument. For example, if -o requires an argument,
-abcovalue is equivalent to -a -b -c -o value.
Exit Codes
OptParser uses standard exit codes per sysexits.h and POSIX conventions:
| Code | Meaning | Usage |
|---|---|---|
0 |
EXIT_SUCCESS |
Successful execution |
1 |
EXIT_FAILURE |
General error (catchall) |
2 |
EX_USAGE |
CLI usage error (invalid arguments, unknown options) |
126 |
- | Command invoked cannot execute (permission denied) |
127 |
- | Command not found |
130 |
- | Fatal error signal (128 + SIGINT(2)) |
Stream Handling
- stdout: Primary data output (results, matched options, machine-readable formats)
- stderr: Diagnostic messages (errors, warnings, progress, help text when explicitly requested via error)
Signal Handling
Long-running operations should handle SIGINT (Ctrl+C) gracefully, performing cleanup and exiting
with status 130.
Usage Guide
Basic Program Structure
#!/usr/bin/env php <?php declare(strict_types=1); use DouglasGreen\OptParser\OptParser; require_once __DIR__ . '/../vendor/autoload.php'; // Define program with name and description $optParser = new OptParser('User Manager', 'Manage system user accounts'); // Define options $optParser // Commands (mutually exclusive operations) ->addCommand(['add', 'a'], 'Add a new user') ->addCommand(['delete', 'd'], 'Delete an existing user') ->addCommand(['list', 'l'], 'List all users') // Terms (positional arguments) ->addTerm('username', 'STRING', 'Username of the user') ->addTerm('email', 'EMAIL', 'Email address of the user') // Term accepting multiple values (e.g., for batch operations) ->addTerm('files', 'INFILE', 'One or more files to process', true, null, true) // Parameters (named arguments with values) ->addParam(['password', 'p'], 'STRING', 'Password for the user') ->addParam(['role', 'r'], 'STRING', 'Role of the user') ->addParam(['output', 'o'], 'OUTFILE', 'Output file path') // Flags (boolean switches) ->addFlag(['verbose', 'v'], 'Enable verbose output') ->addFlag(['quiet', 'q'], 'Suppress non-error output') ->addFlag(['force', 'f'], 'Force operation without confirmation') // Help Sections ->addExample('user-manager add alice alice@example.com -p secret') ->addExample('user-manager delete bob --force') ->addExitCode('0', 'Successful execution') ->addExitCode('1', 'General error') ->addExitCode('2', 'Usage error') ->addEnvironment('NO_COLOR', 'Disable colored output') ->addDocumentation('https://github.com/douglasgreen/opt-parser'); // Define usage patterns (which options go with which commands) $optParser ->addUsage('add', ['username', 'email', 'password', 'role', 'verbose', 'quiet']) ->addUsage('delete', ['username', 'force', 'verbose']) ->addUsage('list', ['output', 'verbose']); // Parse arguments (uses $argv by default) try { $input = $optParser->parse(); } catch (Exception $e) { // Fatal parsing errors exit with code 2 (EX_USAGE) // Use --help for usage information exit(2); } // Execute based on matched command $command = $input->getCommand(); switch ($command) { case 'add': $username = $input->get('username'); $email = $input->get('email'); $password = $input->get('password'); $role = $input->get('role') ?? 'user'; $verbose = $input->get('verbose') ?? false; // Implementation... break; case 'delete': $username = $input->get('username'); $force = $input->get('force') ?? false; // Implementation... break; case 'list': $output = $input->get('output'); $verbose = $input->get('verbose') ?? false; // Implementation... break; case 'batch-delete': // Multiple-value term example: delete multiple users at once $usernames = $input->get('usernames'); // Returns array $force = $input->get('force') ?? false; if (empty($usernames)) { echo "Error: At least one username is required\n"; exit(2); } foreach ($usernames as $username) { if ($force) { echo "Deleted user: $username\n"; } else { echo "Would delete user: $username (use --force to confirm)\n"; } } break; }
Simple Programs (No Subcommands)
If your program doesn't use subcommands, you can use addUsageAll() to automatically allow all
registered options for the main program:
#!/usr/bin/env php <?php declare(strict_types=1); use DouglasGreen\OptParser\OptParser; require_once __DIR__ . '/../vendor/autoload.php'; $optParser = new OptParser('Simple Tool', 'Does one thing well'); $optParser ->addTerm('input', 'STRING', 'Input file to process') ->addParam(['output', 'o'], 'STRING', 'Output file') ->addFlag(['verbose', 'v'], 'Enable verbose output') ->addUsageAll(); // Automatically registers 'input', 'output', and 'verbose' try { $input = $optParser->parse(); } catch (Exception $e) { exit(2); } if ($input->get('verbose')) { echo "Processing " . $input->get('input') . "\n"; }
Option Types
The parser supports four option categories:
| Type | Description | POSIX Equivalent | Example |
|---|---|---|---|
| Command | Subcommand selector (first positional) | Utility operand | git clone |
| Term | Positional argument with validation | Utility operand | file.txt |
| Term (multiple) | Positional argument accepting one or more values | Utility operand(s) | file1.txt file2.txt file3.txt |
| Param | Option requiring an argument | Option with operand | -o file or --output=file |
| Flag | Boolean option without argument | Option without operand | -v or --verbose |
Option Name Resolution
When defining options with multiple names (aliases), the parser designates an official name used for storing and retrieving values. This ensures consistent access regardless of which alias was used on the command line.
Resolution Rules:
- First Long Name: If one or more long names (length > 1) are provided, the first long name is used.
- First Short Name: If only short names are provided, the first short name is used.
Example:
// Names: short 'p', long 'pass', long 'password' $optParser->addParam(['p', 'pass', 'password'], 'STRING', 'Password'); // User provides: --password secret // Official name is 'pass' (first long name) $input->get('pass'); // Returns 'secret' $input->get('password'); // Returns null (not the official name) $input->get('p'); // Returns null (not the official name)
This behavior applies to Commands, Parameters, and Flags.
Multiple-Value Terms
Terms can accept one or more values by setting the multiple parameter to true. When enabled, the
term collects all remaining positional arguments into an array:
// Single-value term (default) $optParser->addTerm('username', 'STRING', 'Username of the user'); // Multiple-value term - collects one or more usernames $optParser->addTerm('usernames', 'STRING', 'One or more usernames', true, null, true);
Usage example:
# Search for multiple users php user-manager.php search alice bob charlie # Delete multiple files php cleanup.php file1.txt file2.txt file3.txt
Accessing values:
$usernames = $input->get('usernames'); // Returns array: ['alice', 'bob', 'charlie'] foreach ($usernames as $username) { echo "Processing: $username\n"; }
Important notes:
- A multiple-value term consumes all remaining positional arguments
- If multiple terms are defined, only the last one should have
multiple: true - The value is always returned as an array (empty array if no values provided)
POSIX Option Syntax Details
Short Options:
- Single hyphen followed by single character:
-a - May be clustered:
-abcequivalent to-a -b -c - Argument may follow immediately or be separated by space:
-ovalueor-o value - If clustering options where the last takes an argument:
-abco valueor-abcovalue
Long Options:
- Double hyphen followed by name:
--verbose - Arguments may be separated by space or equals:
--output fileor--output=file - Names use kebab-case (lowercase with hyphens)
Option Terminator:
- Double hyphen
--indicates end of options - All subsequent arguments are treated as operands (terms), even if they start with
-
Data Types
Built-in validation types (extending POSIX with type safety):
| Type | Validation | Example |
|---|---|---|
STRING |
Any string | "hello" |
INT |
Integer (octal/hex supported) | 42, 0x2A |
FLOAT |
Floating point | 3.14, 1e10 |
BOOL |
Boolean | true, 1, yes |
DATE |
ISO 8601 date | 2024-01-15 |
DATETIME |
ISO 8601 datetime | 2024-01-15 14:30:00 |
TIME |
Time string | 14:30:00 |
INTERVAL |
Date interval | 1 day 2 hours |
EMAIL |
Email address | user@example.com |
URL |
URL | https://example.com |
DOMAIN |
Domain name | example.com |
IP_ADDR |
IP address | 192.168.1.1 |
MAC_ADDR |
MAC address | 00:1B:44:11:3A:B7 |
UUID |
UUID | 550e8400-e29b-41d4-a716-446655440000 |
INFILE |
Readable file path | /path/to/input |
OUTFILE |
Writable file path | /path/to/output |
DIR |
Readable directory | /path/to/dir |
FIXED |
Fixed-point number | 1,234.56 |
Exit Codes
The library uses standard exit codes per sysexits.h and POSIX conventions:
| Code | Constant | Meaning |
|---|---|---|
0 |
EXIT_SUCCESS |
Successful execution |
1 |
EXIT_FAILURE |
General error during execution |
2 |
EX_USAGE |
Command line usage error (invalid arguments, unknown options, missing required options) |
126 |
- | Command invoked cannot execute (permission denied) |
127 |
- | Command not found |
128 |
- | Invalid exit argument |
130 |
- | Script terminated by Ctrl+C (SIGINT) |
Error Handling
Errors are written to stderr with descriptive messages. The parser distinguishes between:
- Usage errors (exit code 2): Invalid syntax, unknown options, missing arguments
- Validation errors (exit code 1): Type mismatches, file not found, invalid formats
- Logic errors: Exceptions thrown during callback execution
Example error output:
error: Unknown option '--verbos'
error: option '--output' requires an argument
error: term 'username' has invalid argument '123': Not a valid string
Advanced Features
Multiple-Value Parameters
Parameters can accept multiple values by setting the multiple parameter to true. Each occurrence
of the option adds a value to an array:
// Multiple-value parameter - collect multiple ignore paths $optParser->addParam(['ignore', 'i'], 'STRING', 'Paths to ignore', null, false, null, true);
Usage example:
# Ignore multiple paths
php cleanup.php --ignore path1 --ignore path2 --ignore path3
Accessing values:
$ignorePaths = $input->get('ignore'); // Returns array: ['path1', 'path2', 'path3'] foreach ($ignorePaths as $path) { echo "Ignoring: $path\n"; }
Multiple-Occurrence Flags
Flags can be specified multiple times to increment a counter by setting the multiple parameter to
true:
// Multiple-occurrence flag - increase verbosity level $optParser->addFlag(['verbose', 'v'], 'Increase verbosity (can be repeated)', true);
Usage example:
# Increase verbosity level php app.php -v -v -v # Or with long options php app.php --verbose --verbose --verbose
Accessing values:
$verbosity = $input->get('verbose'); // Returns int: 3 if ($verbosity >= 3) { echo "Debug mode enabled\n"; } elseif ($verbosity >= 2) { echo "Detailed output\n"; } elseif ($verbosity >= 1) { echo "Verbose output\n"; }
Custom Validation Filters
Apply custom logic to any parameter or term:
$optParser->addParam( ['role', 'r'], 'STRING', 'User role', function ($value) { $allowed = ['admin', 'editor', 'viewer']; if (!in_array($value, $allowed, true)) { throw new Exception( "Role must be one of: " . implode(', ', $allowed) ); } return $value; } );
Help Sections
You can add additional sections to the automatically generated help output to provide examples,
document exit codes, environment variables, and links to documentation. The help output
automatically uses the script filename in the usage line (e.g., Usage: script.php [options]).
Each method adds one item to the respective section:
$optParser ->addExample('user-manager add alice alice@example.com -p secret') ->addExample('user-manager delete bob --force') ->addExitCode('0', 'Successful execution') ->addExitCode('1', 'General error') ->addExitCode('2', 'Usage error') ->addEnvironment('NO_COLOR', 'Disable colored output') ->addDocumentation('https://github.com/douglasgreen/opt-parser');
Resulting --help output:
Usage: example.php [options] [command] [args]
Manage system user accounts
Commands:
add, a Add a new user
delete, d Delete an existing user
list, l List all users
Options:
--username Username of the user
--email Email address of the user
--password, -p <value> Password for the user
--role, -r <value> Role of the user
--output, -o <value> Output file path
--verbose, -v Enable verbose output
--quiet, -q Suppress non-error output
--force, -f Force operation without confirmation
Options:
-h, --help Display this help message
--version Display version information
Examples:
user-manager add alice alice@example.com -p secret
user-manager delete bob --force
Exit Codes:
0 Successful execution
1 General error
2 Usage error
Environment:
NO_COLOR Disable colored output
Documentation:
https://github.com/douglasgreen/opt-parser
Signal Handling
For long-running operations, handle interruption gracefully:
declare(ticks=1); pcntl_signal(SIGINT, function () { // Cleanup temporary files, database transactions, etc. error_log("Operation interrupted by user"); exit(130); // 128 + SIGINT(2) }); // Your main logic here
Non-Options (Operands)
Arguments after -- are treated as operands regardless of content:
# Delete a file literally named "--force"
php delete.php -- --force
Access via:
$nonOptions = $input->getNonoptions(); // ['--force']
Best Practices
- Options before operands: While POSIX allows intermixing, place all options before positional arguments for clarity
- Use long options in scripts:
--verboseis more readable than-vin automation - Check exit codes: Always check
$?in shell scripts; don't assume success - Quote operands: Always quote variables that might contain spaces or special characters
- Use
--: When passing arbitrary filenames, always use--to prevent option injection
Comparison with getopt()
| Feature | PHP getopt() | OptParser |
|---|---|---|
| Short options | Yes | Yes |
| Long options | Limited | Yes (GNU style) |
| Option clustering | No | Yes (-abc) |
| Positional arguments | No | Yes (Terms) |
| Subcommands | No | Yes (Commands) |
| Type validation | No | Yes (20+ types) |
| Automatic help | No | Yes |
| Standard exit codes | No | Yes |
-- terminator |
Partial | Full |
License
MIT License - See LICENSE file for details.