jdwx / args
A simple PHP library for parsing command line arguments.
Requires
- php: >=8.2.0
- ext-mbstring: *
- jdwx/param: ^1.1
Requires (Dev)
- jetbrains/phpstorm-attributes: ^1.0
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2025-02-03 21:25:00 UTC
README
PHP library for handling command-line arguments
It's designed to provide methods for safely retrieving most common argument types, including strings, integers, floating point values, boolean flags, sets of keywords, filenames, hostnames, IP addresses, and email addresses.
It's useful for handling arguments presented to a PHP script on the shell command line, e.g.:
YourPrompt$ ./example.php Hello, world.
Or it could refer to the arguments on a command entered into a PHP-based REPL, such as a custom management tool.
Installation
You can require it directly with Composer:
composer require jdwx/args
Or download the source from GitHub: https://github.com/jdwx/args.git
Requirements
This library requires PHP 8.2 or later. It might work with earlier versions of PHP 8, but it has not been tested with them.
Usage
General Argument Handling
<?php declare( strict_types = 1 ); require_once __DIR__ . '/../vendor/autoload.php'; use JDWX\Args\Arguments; # Remove the script name from the arguments for our example. $argv = array_slice( $argv, 1 ); $args = new Arguments( $argv ); echo $args->shiftString(), "\n"; # Echoes "Hello," in the given example.
All "shift" methods throw an exception if an argument exists but is invalid:
$args = new JDWX\Args\Arguments( [ 'not-an-email-address' ] ); try { $email = $args->shiftEmailAddress(); } catch ( JDWX\Args\BadArgumentException $e) { echo "Not a valid email address: ", $e->getValue(), "\n"; }
The default form of such methods returns null if no more arguments are present, which is useful for iterating in a while loop:
$args = new Arguments( [ '1', '2', '3', '4', '5' ] ); while ( $i = $args->shiftInteger() ) { echo "Got integer: $i\n"; }
The shift methods also provide a variant that throws an exception if no more arguments are present. This is useful for ensuring that a required argument is present:
while ( $st = $args->shiftString() ) { $i = $args->shiftIntegerEx(); assert( is_int( $i ) ); echo "Got: $st => $i\n"; }
It is also possible to "peek" at the next argument without necessarily consuming it. This is supported for strings that match a certain prefix:
# If the next argument is "prefix_example," $st will be set to "example." # In this example, the i_bConsume flag is not set, so the argument will not # be consumed. $args = new JDWX\Args\Arguments( [ 'prefix_example' ] ); if ( $st = $args->peekString( 'prefix_' ) ) { echo "Got: {$st}\n"; $st2 = $args->shiftString(); echo "Shifted: {$st2}\n"; # Argument was not consumed. } else { echo "Nope!\n"; }
or for strings in set of keywords:
$rKeywords = [ 'example', 'demo', 'test' ]; # If the next argument is "example," $st will be set to "example." In this example, # the consume flag is set so the argument is consumed if (and only if) it matches. # The default is kept as false for consistency with peekString(), but usually # you do want to consume keyword-matching arguments. if ( $st = $args->peekKeywords( $rKeywords, i_bConsume: true ) ) { echo "Keyword: {$st}\n"; } else { echo "Nope!\n"; }
Parsing Arguments From Strings
While the command line is the most common place where arguments are encountered, the library also provides functionality for processing arbitrary strings into lists of arguments, including robust handling of quoting and escaped characters:
$args = new Arguments( 'Hello, "world!"' ); echo 'parsed arg 1 = ', $args->shiftString(), "\n"; # Echoes "Hello," echo 'parsed arg 2 = ', $args->shiftString(), "\n"; # Echoes "world!" (no quotes)
The ParsedString class provides a lower-level interface for interacting with parsed strings, including $variable substitution and implementing `backtick` replacement through callbacks. See the parser.php file in the examples directory for a simple example.
Handling Options
The library also provides handling for optional Gnu-style arguments that begin with two hyphens. It supports both boolean flags (e.g., --flag and --no-flag) and options that require a value (e.g., --key=value). It does not support short options (e.g, "-h") or options that require a value to be specified in the next argument (e.g., "--key value").
$options = new Options( [ new Option( 'foo' ), new Option( 'bar', i_bstValue: true ), new Option( 'baz', i_bFlagOnly: false ), new Option( 'qux', '1', '0' ), ] ); $args = new JDWX\Args\Arguments( [ '--foo', '--no-bar', '--baz=quux', '--qux=3' ] ); $options->fromArguments( $args ); echo 'foo = ', $options[ 'foo' ]->asBool() ? 'true' : 'false', "\n"; echo 'bar = ', $options[ 'bar' ]->asBool() ? 'true' : 'false', "\n"; echo 'baz = ', $options[ 'baz' ]->asString(), "\n"; for ( $ii = 0 ; $ii < $options[ 'qux' ]->asInt() ; ++$ii ) { echo "qux\n"; }
If you only have one flag, you can use the Option class directly.
$args = new Arguments( [ "--foo=bar" ] ); $option = new Option( 'foo', i_bFlagOnly: false ); $option->set( $args ); echo 'foo as bool = ', $option->asBool() ? 'true' : 'Nope!', "\n"; # Echoes "true" because flag was present. echo 'foo as str = ', $option->asString(), "\n"; # Echoes "bar" in the given example.
If you prefer to do it in one line of code, there are static methods:
$args = new Arguments( [ '--foo=bar', '--baz' ] ); echo "1-line foo = ", Option::simpleString( 'foo', $args ), "\n"; # Echoes "bar" echo "1-line baz = ", Option::simpleBool( 'baz', $args ) ? 'true' : 'Nope!', "\n"; # Echoes "true"
If you don't want to bother predefining options, you can also extract everything that looks like one from the arguments:
$args = new Arguments( [ '--foo=bar', '--baz', '--no-qux', 'leftover' ] ); $rOptions = $args->handleOptions(); echo $rOptions[ 'foo' ], "\n"; # Echoes "bar" echo ( $rOptions[ 'baz' ] === true ) ? 'true' : 'Nope!', "\n"; # Echoes "true" echo ( $rOptions[ 'qux' ] === false ) ? 'false' : 'Nope!', "\n"; # Echoes "false" echo $args->shiftString(), "\n"; # Echoes "leftover"
Handling options through any of these interfaces removes them from the argument list, allowing remaining arguments to be processed without further interference.
Stability
This library is considered stable and is used in production code. Additional parsing methods may be added in the future, but existing methods should not be removed or changed in a way that breaks backwards compatibility. Exceptions to address security issues or bugs may occur, but are expected to be rare.
History
This library has been in use for many years. It was refactored out of a larger codebase in 2023 and was first released as a standalone library in 2024.