diff options
Diffstat (limited to 'wiki/inc/cli.php')
-rw-r--r-- | wiki/inc/cli.php | 655 |
1 files changed, 0 insertions, 655 deletions
diff --git a/wiki/inc/cli.php b/wiki/inc/cli.php deleted file mode 100644 index cb4fc58..0000000 --- a/wiki/inc/cli.php +++ /dev/null @@ -1,655 +0,0 @@ -<?php - -/** - * Class DokuCLI - * - * All DokuWiki commandline scripts should inherit from this class and implement the abstract methods. - * - * @author Andreas Gohr <andi@splitbrain.org> - */ -abstract class DokuCLI { - /** @var string the executed script itself */ - protected $bin; - /** @var DokuCLI_Options the option parser */ - protected $options; - /** @var DokuCLI_Colors */ - public $colors; - - /** - * constructor - * - * Initialize the arguments, set up helper classes and set up the CLI environment - */ - public function __construct() { - set_exception_handler(array($this, 'fatal')); - - $this->options = new DokuCLI_Options(); - $this->colors = new DokuCLI_Colors(); - - dbg_deprecated('use \splitbrain\phpcli\CLI instead'); - $this->error('DokuCLI is deprecated, use \splitbrain\phpcli\CLI instead.'); - } - - /** - * Register options and arguments on the given $options object - * - * @param DokuCLI_Options $options - * @return void - */ - abstract protected function setup(DokuCLI_Options $options); - - /** - * Your main program - * - * Arguments and options have been parsed when this is run - * - * @param DokuCLI_Options $options - * @return void - */ - abstract protected function main(DokuCLI_Options $options); - - /** - * Execute the CLI program - * - * Executes the setup() routine, adds default options, initiate the options parsing and argument checking - * and finally executes main() - */ - public function run() { - if('cli' != php_sapi_name()) throw new DokuCLI_Exception('This has to be run from the command line'); - - // setup - $this->setup($this->options); - $this->options->registerOption( - 'no-colors', - 'Do not use any colors in output. Useful when piping output to other tools or files.' - ); - $this->options->registerOption( - 'help', - 'Display this help screen and exit immediately.', - 'h' - ); - - // parse - $this->options->parseOptions(); - - // handle defaults - if($this->options->getOpt('no-colors')) { - $this->colors->disable(); - } - if($this->options->getOpt('help')) { - echo $this->options->help(); - exit(0); - } - - // check arguments - $this->options->checkArguments(); - - // execute - $this->main($this->options); - - exit(0); - } - - /** - * Exits the program on a fatal error - * - * @param Exception|string $error either an exception or an error message - */ - public function fatal($error) { - $code = 0; - if(is_object($error) && is_a($error, 'Exception')) { - /** @var Exception $error */ - $code = $error->getCode(); - $error = $error->getMessage(); - } - if(!$code) $code = DokuCLI_Exception::E_ANY; - - $this->error($error); - exit($code); - } - - /** - * Print an error message - * - * @param string $string - */ - public function error($string) { - $this->colors->ptln("E: $string", 'red', STDERR); - } - - /** - * Print a success message - * - * @param string $string - */ - public function success($string) { - $this->colors->ptln("S: $string", 'green', STDERR); - } - - /** - * Print an info message - * - * @param string $string - */ - public function info($string) { - $this->colors->ptln("I: $string", 'cyan', STDERR); - } - -} - -/** - * Class DokuCLI_Colors - * - * Handles color output on (Linux) terminals - * - * @author Andreas Gohr <andi@splitbrain.org> - */ -class DokuCLI_Colors { - /** @var array known color names */ - protected $colors = array( - 'reset' => "\33[0m", - 'black' => "\33[0;30m", - 'darkgray' => "\33[1;30m", - 'blue' => "\33[0;34m", - 'lightblue' => "\33[1;34m", - 'green' => "\33[0;32m", - 'lightgreen' => "\33[1;32m", - 'cyan' => "\33[0;36m", - 'lightcyan' => "\33[1;36m", - 'red' => "\33[0;31m", - 'lightred' => "\33[1;31m", - 'purple' => "\33[0;35m", - 'lightpurple' => "\33[1;35m", - 'brown' => "\33[0;33m", - 'yellow' => "\33[1;33m", - 'lightgray' => "\33[0;37m", - 'white' => "\33[1;37m", - ); - - /** @var bool should colors be used? */ - protected $enabled = true; - - /** - * Constructor - * - * Tries to disable colors for non-terminals - */ - public function __construct() { - if(function_exists('posix_isatty') && !posix_isatty(STDOUT)) { - $this->enabled = false; - return; - } - if(!getenv('TERM')) { - $this->enabled = false; - return; - } - } - - /** - * enable color output - */ - public function enable() { - $this->enabled = true; - } - - /** - * disable color output - */ - public function disable() { - $this->enabled = false; - } - - /** - * Convenience function to print a line in a given color - * - * @param string $line - * @param string $color - * @param resource $channel - */ - public function ptln($line, $color, $channel = STDOUT) { - $this->set($color); - fwrite($channel, rtrim($line)."\n"); - $this->reset(); - } - - /** - * Set the given color for consecutive output - * - * @param string $color one of the supported color names - * @throws DokuCLI_Exception - */ - public function set($color) { - if(!$this->enabled) return; - if(!isset($this->colors[$color])) throw new DokuCLI_Exception("No such color $color"); - echo $this->colors[$color]; - } - - /** - * reset the terminal color - */ - public function reset() { - $this->set('reset'); - } -} - -/** - * Class DokuCLI_Options - * - * Parses command line options passed to the CLI script. Allows CLI scripts to easily register all accepted options and - * commands and even generates a help text from this setup. - * - * @author Andreas Gohr <andi@splitbrain.org> - */ -class DokuCLI_Options { - /** @var array keeps the list of options to parse */ - protected $setup; - - /** @var array store parsed options */ - protected $options = array(); - - /** @var string current parsed command if any */ - protected $command = ''; - - /** @var array passed non-option arguments */ - public $args = array(); - - /** @var string the executed script */ - protected $bin; - - /** - * Constructor - */ - public function __construct() { - $this->setup = array( - '' => array( - 'opts' => array(), - 'args' => array(), - 'help' => '' - ) - ); // default command - - $this->args = $this->readPHPArgv(); - $this->bin = basename(array_shift($this->args)); - - $this->options = array(); - } - - /** - * Sets the help text for the tool itself - * - * @param string $help - */ - public function setHelp($help) { - $this->setup['']['help'] = $help; - } - - /** - * Register the names of arguments for help generation and number checking - * - * This has to be called in the order arguments are expected - * - * @param string $arg argument name (just for help) - * @param string $help help text - * @param bool $required is this a required argument - * @param string $command if theses apply to a sub command only - * @throws DokuCLI_Exception - */ - public function registerArgument($arg, $help, $required = true, $command = '') { - if(!isset($this->setup[$command])) throw new DokuCLI_Exception("Command $command not registered"); - - $this->setup[$command]['args'][] = array( - 'name' => $arg, - 'help' => $help, - 'required' => $required - ); - } - - /** - * This registers a sub command - * - * Sub commands have their own options and use their own function (not main()). - * - * @param string $command - * @param string $help - * @throws DokuCLI_Exception - */ - public function registerCommand($command, $help) { - if(isset($this->setup[$command])) throw new DokuCLI_Exception("Command $command already registered"); - - $this->setup[$command] = array( - 'opts' => array(), - 'args' => array(), - 'help' => $help - ); - - } - - /** - * Register an option for option parsing and help generation - * - * @param string $long multi character option (specified with --) - * @param string $help help text for this option - * @param string|null $short one character option (specified with -) - * @param bool|string $needsarg does this option require an argument? give it a name here - * @param string $command what command does this option apply to - * @throws DokuCLI_Exception - */ - public function registerOption($long, $help, $short = null, $needsarg = false, $command = '') { - if(!isset($this->setup[$command])) throw new DokuCLI_Exception("Command $command not registered"); - - $this->setup[$command]['opts'][$long] = array( - 'needsarg' => $needsarg, - 'help' => $help, - 'short' => $short - ); - - if($short) { - if(strlen($short) > 1) throw new DokuCLI_Exception("Short options should be exactly one ASCII character"); - - $this->setup[$command]['short'][$short] = $long; - } - } - - /** - * Checks the actual number of arguments against the required number - * - * Throws an exception if arguments are missing. Called from parseOptions() - * - * @throws DokuCLI_Exception - */ - public function checkArguments() { - $argc = count($this->args); - - $req = 0; - foreach($this->setup[$this->command]['args'] as $arg) { - if(!$arg['required']) break; // last required arguments seen - $req++; - } - - if($req > $argc) throw new DokuCLI_Exception("Not enough arguments", DokuCLI_Exception::E_OPT_ARG_REQUIRED); - } - - /** - * Parses the given arguments for known options and command - * - * The given $args array should NOT contain the executed file as first item anymore! The $args - * array is stripped from any options and possible command. All found otions can be accessed via the - * getOpt() function - * - * Note that command options will overwrite any global options with the same name - * - * @throws DokuCLI_Exception - */ - public function parseOptions() { - $non_opts = array(); - - $argc = count($this->args); - for($i = 0; $i < $argc; $i++) { - $arg = $this->args[$i]; - - // The special element '--' means explicit end of options. Treat the rest of the arguments as non-options - // and end the loop. - if($arg == '--') { - $non_opts = array_merge($non_opts, array_slice($this->args, $i + 1)); - break; - } - - // '-' is stdin - a normal argument - if($arg == '-') { - $non_opts = array_merge($non_opts, array_slice($this->args, $i)); - break; - } - - // first non-option - if($arg{0} != '-') { - $non_opts = array_merge($non_opts, array_slice($this->args, $i)); - break; - } - - // long option - if(strlen($arg) > 1 && $arg{1} == '-') { - list($opt, $val) = explode('=', substr($arg, 2), 2); - - if(!isset($this->setup[$this->command]['opts'][$opt])) { - throw new DokuCLI_Exception("No such option $arg", DokuCLI_Exception::E_UNKNOWN_OPT); - } - - // argument required? - if($this->setup[$this->command]['opts'][$opt]['needsarg']) { - if(is_null($val) && $i + 1 < $argc && !preg_match('/^--?[\w]/', $this->args[$i + 1])) { - $val = $this->args[++$i]; - } - if(is_null($val)) { - throw new DokuCLI_Exception("Option $arg requires an argument", DokuCLI_Exception::E_OPT_ARG_REQUIRED); - } - $this->options[$opt] = $val; - } else { - $this->options[$opt] = true; - } - - continue; - } - - // short option - $opt = substr($arg, 1); - if(!isset($this->setup[$this->command]['short'][$opt])) { - throw new DokuCLI_Exception("No such option $arg", DokuCLI_Exception::E_UNKNOWN_OPT); - } else { - $opt = $this->setup[$this->command]['short'][$opt]; // store it under long name - } - - // argument required? - if($this->setup[$this->command]['opts'][$opt]['needsarg']) { - $val = null; - if($i + 1 < $argc && !preg_match('/^--?[\w]/', $this->args[$i + 1])) { - $val = $this->args[++$i]; - } - if(is_null($val)) { - throw new DokuCLI_Exception("Option $arg requires an argument", DokuCLI_Exception::E_OPT_ARG_REQUIRED); - } - $this->options[$opt] = $val; - } else { - $this->options[$opt] = true; - } - } - - // parsing is now done, update args array - $this->args = $non_opts; - - // if not done yet, check if first argument is a command and reexecute argument parsing if it is - if(!$this->command && $this->args && isset($this->setup[$this->args[0]])) { - // it is a command! - $this->command = array_shift($this->args); - $this->parseOptions(); // second pass - } - } - - /** - * Get the value of the given option - * - * Please note that all options are accessed by their long option names regardless of how they were - * specified on commandline. - * - * Can only be used after parseOptions() has been run - * - * @param string $option - * @param bool|string $default what to return if the option was not set - * @return bool|string - */ - public function getOpt($option, $default = false) { - if(isset($this->options[$option])) return $this->options[$option]; - return $default; - } - - /** - * Return the found command if any - * - * @return string - */ - public function getCmd() { - return $this->command; - } - - /** - * Builds a help screen from the available options. You may want to call it from -h or on error - * - * @return string - */ - public function help() { - $text = ''; - - $hascommands = (count($this->setup) > 1); - foreach($this->setup as $command => $config) { - $hasopts = (bool) $this->setup[$command]['opts']; - $hasargs = (bool) $this->setup[$command]['args']; - - if(!$command) { - $text .= 'USAGE: '.$this->bin; - } else { - $text .= "\n$command"; - } - - if($hasopts) $text .= ' <OPTIONS>'; - - foreach($this->setup[$command]['args'] as $arg) { - if($arg['required']) { - $text .= ' <'.$arg['name'].'>'; - } else { - $text .= ' [<'.$arg['name'].'>]'; - } - } - $text .= "\n"; - - if($this->setup[$command]['help']) { - $text .= "\n"; - $text .= $this->tableFormat( - array(2, 72), - array('', $this->setup[$command]['help']."\n") - ); - } - - if($hasopts) { - $text .= "\n OPTIONS\n\n"; - foreach($this->setup[$command]['opts'] as $long => $opt) { - - $name = ''; - if($opt['short']) { - $name .= '-'.$opt['short']; - if($opt['needsarg']) $name .= ' <'.$opt['needsarg'].'>'; - $name .= ', '; - } - $name .= "--$long"; - if($opt['needsarg']) $name .= ' <'.$opt['needsarg'].'>'; - - $text .= $this->tableFormat( - array(2, 20, 52), - array('', $name, $opt['help']) - ); - $text .= "\n"; - } - } - - if($hasargs) { - $text .= "\n"; - foreach($this->setup[$command]['args'] as $arg) { - $name = '<'.$arg['name'].'>'; - - $text .= $this->tableFormat( - array(2, 20, 52), - array('', $name, $arg['help']) - ); - } - } - - if($command == '' && $hascommands) { - $text .= "\nThis tool accepts a command as first parameter as outlined below:\n"; - } - } - - return $text; - } - - /** - * Safely read the $argv PHP array across different PHP configurations. - * Will take care on register_globals and register_argc_argv ini directives - * - * @throws DokuCLI_Exception - * @return array the $argv PHP array or PEAR error if not registered - */ - private function readPHPArgv() { - global $argv; - if(!is_array($argv)) { - if(!@is_array($_SERVER['argv'])) { - if(!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) { - throw new DokuCLI_Exception( - "Could not read cmd args (register_argc_argv=Off?)", - DOKU_CLI_OPTS_ARG_READ - ); - } - return $GLOBALS['HTTP_SERVER_VARS']['argv']; - } - return $_SERVER['argv']; - } - return $argv; - } - - /** - * Displays text in multiple word wrapped columns - * - * @param int[] $widths list of column widths (in characters) - * @param string[] $texts list of texts for each column - * @return string - */ - private function tableFormat($widths, $texts) { - $wrapped = array(); - $maxlen = 0; - - foreach($widths as $col => $width) { - $wrapped[$col] = explode("\n", wordwrap($texts[$col], $width - 1, "\n", true)); // -1 char border - $len = count($wrapped[$col]); - if($len > $maxlen) $maxlen = $len; - - } - - $out = ''; - for($i = 0; $i < $maxlen; $i++) { - foreach($widths as $col => $width) { - if(isset($wrapped[$col][$i])) { - $val = $wrapped[$col][$i]; - } else { - $val = ''; - } - $out .= sprintf('%-'.$width.'s', $val); - } - $out .= "\n"; - } - return $out; - } -} - -/** - * Class DokuCLI_Exception - * - * The code is used as exit code for the CLI tool. This should probably be extended. Many cases just fall back to the - * E_ANY code. - * - * @author Andreas Gohr <andi@splitbrain.org> - */ -class DokuCLI_Exception extends Exception { - const E_ANY = -1; // no error code specified - const E_UNKNOWN_OPT = 1; //Unrecognized option - const E_OPT_ARG_REQUIRED = 2; //Option requires argument - const E_OPT_ARG_DENIED = 3; //Option not allowed argument - const E_OPT_ABIGUOUS = 4; //Option abiguous - const E_ARG_READ = 5; //Could not read argv - - /** - * @param string $message The Exception message to throw. - * @param int $code The Exception code - * @param Exception $previous The previous exception used for the exception chaining. - */ - public function __construct($message = "", $code = 0, Exception $previous = null) { - if(!$code) $code = DokuCLI_Exception::E_ANY; - parent::__construct($message, $code, $previous); - } -} |