Back to index

php5  5.3.10
clicommand.inc
Go to the documentation of this file.
00001 <?php
00002 
00017 abstract class CLICommand
00018 {
00019        protected $argc;
00020        protected $argv;
00021        protected $cmds = array();
00022        protected $args = array();
00023        protected $typs = array();
00024 
00025        function __construct($argc, array $argv)
00026        {
00027               $this->argc = $argc;
00028               $this->argv = $argv;
00029               $this->cmds = self::getCommands($this);
00030               $this->typs = self::getArgTyps($this);
00031 
00032               if ($argc < 2) {
00033                      self::error("No command given, check ${argv[0]} help\n");
00034               } elseif (!isset($this->cmds[$argv[1]]['run'])) {
00035                      self::error("Unknown command '${argv[1]}', check ${argv[0]} help\n");
00036               } else {
00037                      $command = $argv[1];
00038               }
00039 
00040               if (isset($this->cmds[$command]['arg'])) {
00041                      $this->args = call_user_func(array($this, $this->cmds[$command]['arg']));
00042                      $i = 1;
00043                      $missing = false;
00044                      while (++$i < $argc) {
00045                             if ($argv[$i][0] == '-') {
00046                                    if (strlen($argv[$i]) == 2 && isset($this->args[$argv[$i][1]])) {
00047                                           $arg = $argv[$i][1];
00048                                           if (++$i >= $argc) {
00049                                                  self::error("Missing argument to parameter '$arg' of command '$command', check ${argv[0]} help\n");
00050                                           } else {
00051                                                  $this->args[$arg]['val'] = $this->checkArgTyp($arg, $i, $argc, $argv);
00052                                           }
00053                                    }  else {
00054                                           self::error("Unknown parameter '${argv[$i]}' to command $command, check ${argv[0]} help\n");
00055                                    }
00056                             } else {
00057                                    break;
00058                             }
00059                      }
00060 
00061                      if (isset($this->args[''])) {
00062                             if ($i >= $argc) {
00063                                    if (isset($this->args['']['require']) && $this->args['']['require']) {
00064                                           self::error("Missing default trailing arguments to command $command, check ${argv[0]} help\n");
00065                                    }
00066                             } else {
00067                                    $this->args['']['val'] = array();
00068                                    while($i < $argc) {
00069                                           $this->args['']['val'][] = $argv[$i++];
00070                                    }
00071                             }
00072                      } else if ($i < $argc) {
00073                             self::error("Unexpected default arguments to command $command, check ${argv[0]} help\n");
00074                      }
00075 
00076                      foreach($this->args as $arg => $inf) {
00077                             if (strlen($arg) && !isset($inf['val']) && isset($inf['required']) && $inf['required']) {
00078                                    $missing .=  "Missing parameter '-$arg' to command $command, check ${argv[0]} help\n";
00079                             }
00080                      }
00081 
00082                      if (strlen($missing)) {
00083                             self::error($missing);
00084                      }
00085               }
00086 
00087               call_user_func(array($this, $this->cmds[$command]['run']), $this->args);
00088        }
00089 
00090        static function notice ($msg)
00091        {
00092               fprintf(STDERR, $msg);
00093        }
00094 
00095        static function error ($msg, $exit_code = 1)
00096        {
00097               self::notice($msg);
00098               exit($exit_code);
00099        }
00100 
00101        function checkArgTyp($arg, $i, $argc, $argv)
00102        {
00103               $typ = $this->args[$arg]['typ'];
00104 
00105               if (isset($this->typs[$typ]['typ'])) {
00106                      return call_user_func(array($this, $this->typs[$typ]['typ']), $argv[$i], $this->args[$arg], $arg);
00107               } else {
00108                      return $argv[$i];
00109               }
00110        }
00111 
00112        static function getSubFuncs(CLICommand $cmdclass, $prefix, array $subs)
00113        {
00114               $a = array();
00115               $r = new ReflectionClass($cmdclass);
00116               $l = strlen($prefix);
00117 
00118               foreach($r->getMethods() as $m) {
00119                      if (substr($m->name, 0, $l) == $prefix) {
00120                             foreach($subs as $sub) {
00121                                    $what = substr($m->name, $l+strlen($sub)+1);
00122                                    $func = $prefix . $sub . '_' . $what;
00123                                    $what = str_replace('_', '-', $what);
00124                                    if ($r->hasMethod($func)) {
00125                                           if (!isset($a[$what])) {
00126                                                  $a[$what] = array();
00127                                           }
00128                                           $a[$what][$sub] = /*$m->class . '::' .*/ $func;
00129                                    }
00130                             }
00131                      }
00132               }
00133               return $a;
00134        }
00135 
00136        static function getCommands(CLICommand $cmdclass)
00137        {
00138               return self::getSubFuncs($cmdclass, 'cli_cmd_', array('arg','inf','run'));
00139        }
00140 
00141        static function getArgTyps(CLICommand $cmdclass)
00142        {
00143               return self::getSubFuncs($cmdclass, 'cli_arg_', array('typ'));
00144        }
00145 
00146        static function cli_arg_typ_bool($arg, $cfg, $key)
00147        {
00148               return (bool)$arg;
00149        }
00150 
00151        static function cli_arg_typ_int($arg, $cfg, $key)
00152        {
00153               if ((int)$arg != $arg) {
00154                      self::error("Argument to -$key must be an integer.\n");
00155               }
00156 
00157               return (int)$arg;
00158        }
00159 
00160        static function cli_arg_typ_regex($arg, $cfg, $key)
00161        {
00162               if (strlen($arg)) {
00163                      if (strlen($arg) > 1 && $arg[0] == $arg[strlen($arg)-1] && strpos('/,', $arg) !== false) {
00164                             return $arg;
00165                      } else {
00166                             return '/' . $arg . '/';
00167                      }
00168               } else {
00169                      return NULL;
00170               }
00171        }
00172 
00173        static function cli_arg_typ_select($arg, $cfg, $key)
00174        {
00175               if (!in_array($arg, array_keys($cfg['select']))) {
00176                      self::error("Parameter value '$arg' not one of '" . join("', '", array_keys($cfg['select'])) . "'.\n");
00177               }
00178               return $arg;
00179        }
00180 
00181        static function cli_arg_typ_dir($arg, $cfg, $key)
00182        {
00183               $f = realpath($arg);
00184 
00185               if ($f===false || !file_exists($f) || !is_dir($f)) {
00186                      self::error("Requested path '$arg' does not exist.\n");
00187               }
00188               return $f;
00189        }
00190 
00191        static function cli_arg_typ_file($arg)
00192        {
00193               $f = new SplFileInfo($arg);
00194               $f = $f->getRealPath();
00195               if ($f===false || !file_exists($f)) {
00196                      echo "Requested file '$arg' does not exist.\n";
00197                      exit(1);
00198               }
00199               return $f;
00200        }
00201 
00202        static function cli_arg_typ_filenew($arg, $cfg, $key)
00203        {
00204               $d = dirname($arg);
00205               $f = realpath($d);
00206 
00207               if ($f === false) {
00208                      self::error("Path for file '$arg' does not exist.\n");
00209               }
00210               return $f . '/' . basename($arg);
00211        }
00212 
00213        static function cli_arg_typ_filecont($arg, $cfg, $key)
00214        {
00215               return file_get_contents(self::cli_arg_typ_file($arg, $cfg, $key));
00216        }
00217 
00218        function cli_get_SP2($l1, $arg_inf)
00219        {
00220               return str_repeat(' ', $l1 + 2 + 4 + 8);
00221        }
00222 
00223        function cli_get_SP3($l1, $l2, $arg_inf)
00224        {
00225               return str_repeat(' ', $l1 + 2 + 4 + 8 + 2 + $l2 + 2);
00226        }
00227 
00228        static function cli_cmd_inf_help()
00229        {
00230               return "This help or help for a selected command.";
00231        }
00232 
00233        private function cli_wordwrap($what, $l, $sp)
00234        {
00235               $p = max(79 - $l, 40);     // minimum length for paragraph
00236               $b = substr($what, 0, $l); // strip out initial $l
00237               $r = substr($what, $l);    // remainder
00238               $r = str_replace("\n", "\n".$sp, $r); // in remainder replace \n's
00239               return $b . wordwrap($r, $p, "\n".$sp);
00240        }
00241 
00242        private function cli_help_get_args($func, $l, $sp, $required)
00243        {
00244               $inf = "";
00245               foreach(call_user_func($func, $l, $sp) as $arg => $conf) {
00246                      if ((isset($conf['required']) && $conf['required']) != $required) {
00247                             continue;
00248                      }
00249 
00250                      if (strlen($arg)) {
00251                             $arg = "-$arg  ";
00252                      } else {
00253                             $arg = "... ";
00254                      }
00255 
00256                      $sp2 = $this->cli_get_SP2($l, $inf);
00257                      $l2  = strlen($sp2);
00258                      $inf .= $this->cli_wordwrap($sp . $arg . $conf['inf'], $l2, $sp2) . "\n";
00259 
00260                      if (isset($conf['select']) && count($conf['select'])) {
00261                             $ls = 0;
00262                             foreach($conf['select'] as $opt => $what) {
00263                                    $ls = max($ls, strlen($opt));
00264                             }
00265                             $sp3 = $this->cli_get_SP3($l, $ls, $inf);
00266                             $l3  = strlen($sp3);
00267                             foreach($conf['select'] as $opt => $what) {
00268                                    $inf .= $this->cli_wordwrap($sp2 . "  " . sprintf("%-${ls}s  ", $opt) . $what, $l3, $sp3) . "\n";
00269                             }
00270                      }
00271               }
00272               if (strlen($inf)) {
00273                      if ($required) {
00274                             return $sp . "Required arguments:\n\n" . $inf;
00275                      } else {
00276                             return $sp . "Optional arguments:\n\n". $inf;
00277                      }
00278               }
00279        }
00280 
00281        function cli_cmd_arg_help()
00282        {
00283               return array('' => array('typ'=>'any','val'=>NULL,'inf'=>'Optional command to retrieve help for.'));
00284        }
00285 
00286        function cli_cmd_run_help()
00287        {
00288               $argv  = $this->argv;
00289               $which = $this->args['']['val'];
00290               if (isset($which)) {
00291                      if (count($which) != 1) {
00292                             self::error("More than one command given.\n");
00293                      }
00294 
00295                      $which = $which[0];
00296                      if (!array_key_exists($which, $this->cmds)) {
00297                             if (strtolower($which) == 'commands') {
00298                                    self::cli_cmd_run_help_list();
00299                                    exit(0);
00300                             }
00301                             self::error("Unknown command, cannot retrieve help.\n");
00302                      }
00303 
00304                      $l = strlen($which);
00305                      $cmds = array($which => $this->cmds[$which]);
00306               } else {
00307                      echo "\n$argv[0] <command> [options]\n\n";
00308                      $l = 0;
00309                      ksort($this->cmds);
00310                      foreach($this->cmds as $name => $funcs) {
00311                             $l = max($l, strlen($name));
00312                      }
00313                      $inf = "Commands:";
00314                      $lst = "";
00315                      $ind = strlen($inf) + 1;
00316                      foreach($this->cmds as $name => $funcs) {
00317                             $lst .= ' ' . $name;
00318                      }
00319                      echo $this->cli_wordwrap($inf.$lst, $ind, str_repeat(' ', $ind)) . "\n\n";
00320                      $cmds = $this->cmds;
00321               }
00322               $sp = str_repeat(' ', $l + 2);
00323               foreach($cmds as $name => $funcs) {
00324                      $inf = $name . substr($sp, strlen($name));
00325                      if (isset($funcs['inf'])) {
00326                             $inf .= $this->cli_wordwrap(call_user_func(array($this, $funcs['inf'])), $l, $sp) . "\n";
00327                             if (isset($funcs['arg'])) {
00328                                    $inf .= "\n";
00329                                    $inf .= $this->cli_help_get_args(array($this, $funcs['arg']), $l, $sp, true);
00330                                    $inf .= "\n";
00331                                    $inf .= $this->cli_help_get_args(array($this, $funcs['arg']), $l, $sp, false);
00332                             }
00333                      }
00334                      echo "$inf\n\n";
00335               }
00336               exit(0);
00337        }
00338 
00339        static function cli_cmd_inf_help_list()
00340        {
00341               return "Lists available commands.";
00342        }
00343 
00344        function cli_cmd_run_help_list()
00345        {
00346               ksort($this->cmds);
00347               echo join(' ', array_keys($this->cmds)) . "\n";
00348        }
00349 }
00350 
00351 ?>