Page MenuHomeWickedGov Phorge

OptsProcessor.php
No OneTemporary

Size
14 KB
Referenced Files
None
Subscribers
None

OptsProcessor.php

<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*/
namespace Wikimedia\Parsoid\Tools;
/**
* Class for processing CLI args
*
* This code has been extracted from Core's Maintenance.php class
* by removing all non-CLI arg processing state and code.
*/
class OptsProcessor {
/**
* Array of desired params
* @var array
*/
public $params = [];
/**
* Array of mapping short parameters to long ones
* @var array
*/
public $shortParamsMap = [];
/**
* Array of desired args
* @var array
*/
public $argList = [];
/**
* This is the array of options that were actually passed
* @var array
*/
protected $options = [];
/**
* This is the array of arguments that were actually passed
* @var array
*/
public $args = [];
/**
* Allow arbitrary options to be passed, or only specified ones?
* @var bool
*/
public $allowUnregisteredOptions = false;
/**
* Name of the script currently running
* @var string
*/
private $self;
/**
* Is the script running in quiet mode?
* @var bool
*/
private $quiet = false;
/**
* Add a description of the script
* @var string
*/
private $description = '';
/**
* Generic options added by addDefaultParams()
* Generic options which might or not be supported by the script
* @var array
*/
private $genericParameters = [];
/**
* Generic options which might or not be supported by the script
* @var array
*/
private $dependentParameters = [];
/**
* Used to read the options in the order they were passed.
* Useful for option chaining (Ex. dumpBackup.php). It will
* be an empty array if the options are passed in through
* loadParamsAndArgs( $self, $opts, $args ).
*
* This is an array of arrays where
* 0 => the option and 1 => parameter value.
*
* @var array
*/
public $orderedOptions = [];
/**
* Adds default args (--help, --quiet right now)
*/
public function __construct() {
$this->addDefaultParams();
global $argv;
$this->self = $argv[0];
}
/**
* Set the description text.
* @param string $text The text of the description
*/
public function addDescription( string $text ): void {
$this->description = $text;
}
/**
* Get the script's name
* @return string
*/
public function getName(): string {
return $this->self;
}
/**
* Checks to see if a particular option is supported. Normally this means
* it has been registered by the script via addOption.
* @param string $name The name of the option
* @return bool true if the option exists, false otherwise
*/
public function supportsOption( string $name ): bool {
return isset( $this->params[$name] );
}
/**
* Add a parameter to the script. Will be displayed on --help
* with the associated description
*
* @param string $name The name of the param (help, version, etc)
* @param string $description The description of the param to show on --help
* @param bool $required Is the param required?
* @param bool $withArg Is an argument required with this option?
* @param string|bool $shortName Character to use as short name
* @param bool $multiOccurrence Can this option be passed multiple times?
*/
public function addOption(
string $name, string $description, bool $required = false,
bool $withArg = false, $shortName = false,
bool $multiOccurrence = false
): void {
$this->params[$name] = [
'desc' => $description,
'require' => $required,
'withArg' => $withArg,
'shortName' => $shortName,
'multiOccurrence' => $multiOccurrence,
];
if ( $shortName !== false ) {
$this->shortParamsMap[$shortName] = $name;
}
}
/**
* Checks to see if a particular option exists.
* @param string $name The name of the option
* @return bool
*/
public function hasOption( string $name ): bool {
return isset( $this->options[$name] );
}
/**
* Get an option, or return the default.
*
* If the option was added to support multiple occurrences,
* this will return an array.
*
* @param string $name The name of the param
* @param mixed $default Anything you want, default null
* @return mixed
*/
public function getOption( $name, $default = null ) {
if ( $this->hasOption( $name ) ) {
return $this->options[$name];
} else {
// Set it so we don't have to provide the default again
$this->options[$name] = $default;
return $this->options[$name];
}
}
/**
* Add some args that are needed
* @param string $arg Name of the arg, like 'start'
* @param string $description Short description of the arg
* @param bool $required Is this required?
*/
public function addArg( string $arg, string $description, bool $required = true ) {
$this->argList[] = [
'name' => $arg,
'desc' => $description,
'require' => $required
];
}
/**
* Remove an option. Useful for removing options that won't be used in your script.
* @param string $name The option to remove.
*/
public function deleteOption( string $name ): void {
unset( $this->params[$name] );
}
/**
* Sets whether to allow unregistered options, which are options passed to
* a script that do not match an expected parameter.
* @param bool $allow Should we allow?
*/
public function setAllowUnregisteredOptions( bool $allow ): void {
$this->allowUnregisteredOptions = $allow;
}
/**
* Does a given argument exist?
* @param int $argId The integer value (from zero) for the arg
* @return bool
*/
public function hasArg( int $argId = 0 ): bool {
return isset( $this->args[$argId] );
}
/**
* Get an argument.
* @param int $argId The integer value (from zero) for the arg
* @param mixed $default The default if it doesn't exist
* @return mixed
*/
public function getArg( int $argId = 0, $default = null ) {
return $this->hasArg( $argId ) ? $this->args[$argId] : $default;
}
public function isQuiet(): bool {
return $this->quiet;
}
/**
* Add the default parameters to the scripts
*/
public function addDefaultParams(): void {
# Generic (non script dependent) options:
$this->addOption( 'help', 'Display this help message', false, false, 'h' );
$this->addOption( 'quiet', 'Whether to suppress non-error output', false, false, 'q' );
# Save generic options to display them separately in help
$this->genericParameters = $this->params;
# Save additional script dependent options to display
# them separately in help
$this->dependentParameters = array_diff_key( $this->params, $this->genericParameters );
}
/**
* Clear all params and arguments.
*/
public function clearParamsAndArgs(): void {
$this->options = [];
$this->args = [];
}
/**
* Load params and arguments from a given array
* of command-line arguments
*
* @param array $argv
*/
public function loadWithArgv( array $argv ): void {
$options = [];
$args = [];
$this->orderedOptions = [];
# Parse arguments
for ( $arg = reset( $argv ); $arg !== false; $arg = next( $argv ) ) {
if ( $arg == '--' ) {
# End of options, remainder should be considered arguments
$arg = next( $argv );
while ( $arg !== false ) {
$args[] = $arg;
$arg = next( $argv );
}
break;
} elseif ( substr( $arg, 0, 2 ) == '--' ) {
# Long options
$option = substr( $arg, 2 );
if ( isset( $this->params[$option] ) && $this->params[$option]['withArg'] ) {
$param = next( $argv );
if ( $param === false ) {
$this->error( "\nERROR: $option parameter needs a value after it\n" );
$this->maybeHelp( true );
}
$this->setParam( $options, $option, $param );
} else {
$bits = explode( '=', $option, 2 );
$this->setParam( $options, $bits[0], $bits[1] ?? 1 );
}
} elseif ( $arg == '-' ) {
# Lonely "-", often used to indicate stdin or stdout.
$args[] = $arg;
} elseif ( substr( $arg, 0, 1 ) == '-' ) {
# Short options
$argLength = strlen( $arg );
for ( $p = 1; $p < $argLength; $p++ ) {
$option = $arg[$p];
if ( !isset( $this->params[$option] ) && isset( $this->shortParamsMap[$option] ) ) {
$option = $this->shortParamsMap[$option];
}
if ( isset( $this->params[$option]['withArg'] ) && $this->params[$option]['withArg'] ) {
$param = next( $argv );
if ( $param === false ) {
$this->error( "\nERROR: $option parameter needs a value after it\n" );
$this->maybeHelp( true );
}
$this->setParam( $options, $option, $param );
} else {
$this->setParam( $options, $option, 1 );
}
}
} else {
$args[] = $arg;
}
}
$this->options = $options;
$this->args = $args;
}
/**
* Helper function used solely by loadParamsAndArgs
* to prevent code duplication
*
* This sets the param in the options array based on
* whether or not it can be specified multiple times.
*
* @param array &$options
* @param string $option
* @param mixed $value
*/
private function setParam( array &$options, string $option, $value ): void {
$this->orderedOptions[] = [ $option, $value ];
if ( isset( $this->params[$option] ) ) {
$multi = $this->params[$option]['multiOccurrence'];
} else {
$multi = false;
}
$exists = array_key_exists( $option, $options );
if ( $multi && $exists ) {
$options[$option][] = $value;
} elseif ( $multi ) {
$options[$option] = [ $value ];
} elseif ( !$exists ) {
$options[$option] = $value;
} else {
$this->error( "\nERROR: $option parameter given twice\n" );
$this->maybeHelp( true );
}
}
/**
* Process command line arguments
* $options becomes an array with keys set to the option names
* $args becomes a zero-based array containing the non-option arguments
*
* @param ?string $self The name of the script, if any
* @param ?array $opts An array of options, in form of key=>value
* @param ?array $args An array of command line arguments
*/
public function loadParamsAndArgs(
?string $self = null, ?array $opts = null, ?array $args = null
): void {
# If we were given opts or args, set those and return early
if ( $self ) {
$this->self = $self;
}
if ( $opts ) {
$this->options = $opts;
}
if ( $args ) {
$this->args = $args;
}
global $argv;
$this->self = $argv[0];
$this->loadWithArgv( array_slice( $argv, 1 ) );
}
/**
* Run some validation checks on the params, etc
*/
public function validateParamsAndArgs(): void {
$die = false;
# Check to make sure we've got all the required options
foreach ( $this->params as $opt => $info ) {
if ( $info['require'] && !$this->hasOption( $opt ) ) {
$this->error( "Param $opt required!" );
$die = true;
}
}
# Check arg list too
foreach ( $this->argList as $k => $info ) {
if ( $info['require'] && !$this->hasArg( $k ) ) {
$this->error( 'Argument <' . $info['name'] . '> required!' );
$die = true;
}
}
if ( !$this->allowUnregisteredOptions ) {
# Check for unexpected options
foreach ( $this->options as $opt => $val ) {
if ( !$this->supportsOption( $opt ) ) {
$this->error( "Unexpected option $opt!" );
$die = true;
}
}
}
if ( $die ) {
$this->maybeHelp( true );
}
}
/**
* Maybe show the help.
* @param bool $force Whether to force the help to show, default false
*/
public function maybeHelp( bool $force = false ): void {
if ( !$force && !$this->hasOption( 'help' ) ) {
return;
}
$screenWidth = 80; // TODO: Calculate this!
$tab = " ";
$descWidth = $screenWidth - ( 2 * strlen( $tab ) );
ksort( $this->params );
// Description ...
if ( $this->description ) {
print "\n" . wordwrap( $this->description, $screenWidth ) . "\n";
}
$output = "\nUsage: php " . basename( $this->self );
// ... append parameters ...
if ( $this->params ) {
$output .= " [--" . implode( "|--", array_keys( $this->params ) ) . "]";
}
// ... and append arguments.
if ( $this->argList ) {
$output .= ' ';
foreach ( $this->argList as $k => $arg ) {
if ( $arg['require'] ) {
$output .= '<' . $arg['name'] . '>';
} else {
$output .= '[' . $arg['name'] . ']';
}
if ( $k < count( $this->argList ) - 1 ) {
$output .= ' ';
}
}
}
print "$output\n\n";
# TODO abstract some repetitive code below
// Generic parameters
foreach ( $this->genericParameters as $par => $info ) {
if ( $info['shortName'] !== false ) {
$par .= " (-{$info['shortName']})";
}
$this->output(
wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
"\n$tab$tab" ) . "\n"
);
}
print "\n";
$scriptDependantParams = $this->dependentParameters;
if ( count( $scriptDependantParams ) > 0 ) {
print "Script dependent parameters:\n";
// Parameters description
foreach ( $scriptDependantParams as $par => $info ) {
if ( $info['shortName'] !== false ) {
$par .= " (-{$info['shortName']})";
}
$this->output(
wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
"\n$tab$tab" ) . "\n"
);
}
print "\n";
}
// Script specific parameters not defined on construction by
// addDefaultParams()
$scriptSpecificParams = array_diff_key(
# all script parameters:
$this->params,
# remove the default parameters:
$this->genericParameters,
$this->dependentParameters
);
if ( count( $scriptSpecificParams ) > 0 ) {
print "Script specific parameters:\n";
// Parameters description
foreach ( $scriptSpecificParams as $par => $info ) {
if ( $info['shortName'] !== false ) {
$par .= " (-{$info['shortName']})";
}
$this->output(
wordwrap( "$tab--$par: " . $info['desc'], $descWidth,
"\n$tab$tab" ) . "\n"
);
}
print "\n";
}
// Print arguments
if ( count( $this->argList ) > 0 ) {
print "Arguments:\n";
// Arguments description
foreach ( $this->argList as $info ) {
$openChar = $info['require'] ? '<' : '[';
$closeChar = $info['require'] ? '>' : ']';
$this->output(
wordwrap( "$tab$openChar" . $info['name'] . "$closeChar: " .
$info['desc'], $descWidth, "\n$tab$tab" ) . "\n"
);
}
print "\n";
}
die( 1 );
}
/**
* Override to redirect opts processor error messages.
*
* @param string $err
*/
protected function error( string $err ): void {
error_log( $err );
}
/**
* Override to redirect opts processor output.
*
* @param string $out
*/
protected function output( string $out ): void {
print $out;
}
}

File Metadata

Mime Type
text/x-php
Expires
Sat, Oct 3, 06:28 (2 w, 3 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
02/8f/528d1470ca7ba204b2dc53bbaf79
Default Alt Text
OptsProcessor.php (14 KB)

Event Timeline