Source for file Getopt.php

Documentation is available at Getopt.php

  1. <?php
  2. // vim: foldmethod=marker
  3. /**
  4.  *  Getopt.php
  5.  *
  6.  *  @author     Yoshinari Takaoka <takaoka@beatcraft.com>
  7.  *  @license    Public Domain
  8.  *  @package    Ethna
  9.  *  @version    $Id: adb71f2ff53da8464732977145d8d6d2e7da9d28 $
  10.  */
  11.  
  12. if (!defined('ETHNA_OPTVALUE_IS_DISABLED')) {
  13.     define('ETHNA_OPTVALUE_IS_DISABLED'1);
  14. }
  15. if (!defined('ETHNA_OPTVALUE_IS_REQUIRED')) {
  16.     define('ETHNA_OPTVALUE_IS_REQUIRED'2);
  17. }
  18. if (!defined('ETHNA_OPTVALUE_IS_OPTIONAL')) {
  19.     define('ETHNA_OPTVALUE_IS_OPTIONAL'3);
  20. }
  21.  
  22. // {{{ Ethna_Getopt
  23. /**
  24.  *  コマンドラインオプション解釈クラス
  25.  *  PEAR への依存を排除するため、 Console_Getopt クラスを最実装したもの
  26.  *
  27.  *  @author     Yoshinari Takaoka <takaoka@beatcraft.com>
  28.  *  @access     public
  29.  *  @package    Ethna
  30.  *  @see        http://pear.php.net/manual/en/package.console.console-getopt.php
  31.  */
  32. {
  33.     /**
  34.      *  PHP 設定を考慮して、$argv 配列を読みます。
  35.      *  ini ディレクティブ中の register_argc_argv を考慮します。
  36.      *
  37.      *  注意: PHP 4.2.0 以前では、$argv を読むためには
  38.      *         register_globals が ON になっている必要が
  39.      *         ありました。Ethna は この設定がoffであるこ
  40.      *         とを前提にして書かれているため、ここでは考
  41.      *         慮していません。
  42.      *
  43.      *  @return array - オプションとパラメータを含む配列、
  44.      *                   もしくは Ethna_Error
  45.      */
  46.     public function readPHPArgv()
  47.     {
  48.         global $argv;
  49.  
  50.         if (ini_get('register_argc_argv'== false{
  51.             return Ethna::raiseError(
  52.                        'Could not read cmd args (register_argc_argv=Off?'
  53.                    );
  54.         }
  55.         return $argv;
  56.     }
  57.  
  58.     /**
  59.      *  コマンドラインオプションをパースし、結果を返します。
  60.      *
  61.      *  @param array  $args - コマンドライン引数の配列
  62.      *  @param string $shortoptions - 使用できる短いオプション目録を指定します。
  63.      *  @param array  $longoptions - 使用できる長いオプション目録を指定します。
  64.      *
  65.      *  @return array - パースされたオプションと非オプションのコマンドライン引数
  66.      *                   の 2つの要素からなる配列、もしくは Ethna_Error 。
  67.      */
  68.     public function getopt($args$shortoptions$longoptions NULL)
  69.     {
  70.         $shortopts $this->_parseShortOption($shortoptions);
  71.         if (Ethna::isError($shortopts)) {
  72.             return $shortopts;
  73.         }
  74.         $longopts $this->_parseLongOption($longoptions);
  75.         if (Ethna::isError($longopts)) {
  76.             return $longopts;
  77.         }
  78.  
  79.         $parsed_arguments array();
  80.         $nonparsed_arguments array();
  81.  
  82.         for ($pos 0$pos count($args)$pos++{
  83.  
  84.              $arg $args[$pos];
  85.              $next_arg = isset($args[$pos 1]$args[$pos 1NULL;
  86.              $is_nextarg_is_value false;
  87.              $required false;
  88.  
  89.              if (strpos($arg'--'=== 0//  long option
  90.  
  91.                  //
  92.                  // GNU getopt(3) の場合は、長いオプションは他と重なら
  93.                  // ない限りにおいて短縮できる。たとえば --foo, --fuji
  94.                  // というオプションが定義された場合、 --fo や --fu と
  95.                  // いう短縮指定も可能というものである。
  96.                  //
  97.                  // PEAR の Console_Getopt はこの短縮指定に対応していな
  98.                  // い。よって、それを使用してきた Ethna でもそこまでは
  99.                  // 頑張らないことにする。
  100.                  //
  101.  
  102.                  //    オプションの値を処理する
  103.                  $lopt str_replace('--'''$arg);
  104.                  $opt_and_value explode('='$lopt);
  105.                  $opt $opt_and_value[0];
  106.                  if (!array_key_exists($opt$longopts)) {
  107.                      return Ethna::raiseError("unrecognized option --$opt");
  108.                  }
  109.  
  110.                  //  オプションの値を取り出す
  111.                  $required $longopts[$opt];
  112.                  $value NULL;
  113.                  if (count($opt_and_value== 2{
  114.                      $value $opt_and_value[1];   // --foo=bar
  115.                  elseif (strpos('-'$next_arg!== 0
  116.                         && $required == ETHNA_OPTVALUE_IS_REQUIRED{
  117.                      if (!empty($next_arg)) {      // --foo bar
  118.                          // 次の $argv を値として解釈
  119.                          // == が設定されていた場合は値として解釈「しない」
  120.                          $value $next_arg;
  121.                          $pos++;
  122.                      }
  123.                  }
  124.  
  125.                  //  オプション設定チェック
  126.                  switch ($required{
  127.                      case ETHNA_OPTVALUE_IS_REQUIRED:
  128.                          if ($value === NULL{
  129.                              return Ethna::raiseError(
  130.                                         "option --$opt requires an argument"
  131.                                     );
  132.                          }
  133.                          break;
  134.                      case ETHNA_OPTVALUE_IS_DISABLED:
  135.                          if ($value !== NULL{
  136.                              return Ethna::raiseError(
  137.                                         "option --$opt doesn't allow an argument"
  138.                                     );
  139.                          }
  140.                          break;
  141.                  }
  142.  
  143.                  //  長いオプションの場合は、-- 付きでオプション名を記録する
  144.                  //  Console_Getopt 互換にするため。
  145.                  $parsed_arguments[array("--$opt"$value);
  146.  
  147.              elseif (strpos($arg'-'=== 0{  // short option
  148.  
  149.                  //
  150.                  // -abcd のように、オプションと値が続けて
  151.                  // 入力される場合がある。この場合どうオプションを解釈
  152.                  // するかの仕様は、GNU getopt(3) の仕様に従う
  153.                  //
  154.                  // 1. abcd を1文字ずつに分解し、a, b, c, d にする
  155.                  //
  156.                  // 2. ':' (値必須) として設定されていた場合は、次の文字以降は
  157.                  //    全て値として解釈する。この場合は次のargvは値として解釈し
  158.                  //    ない。また、次の文字がなく、次の argv が値だった場合は、
  159.                  //    それを値として解釈する
  160.                  // 3. '::'(値が任意) として設定されていた場合も次の文字以降を
  161.                  //    全て値として解釈するが、次の文字がない場合でも次のargvは
  162.                  //    値として解釈「しない」
  163.                  //
  164.                  // 4. 無設定(値設定禁止)の場合は、次の文字もオプションとして解
  165.                  //    釈する。また、次のargvは値として解釈しない
  166.                  //
  167.                  // @see LANG=C; man 3 getopt (日本語マニュアルは見ない方がいいかも)
  168.                  // @see http://www.gnu.org/software/libtool/manual/libc/Using-Getopt.html
  169.                  //
  170.                  //  TODO: ambiguous なオプションを検出できるようにする
  171.                  //
  172.  
  173.                  $sopt str_replace('-'''$arg);
  174.                  $sopt_len strlen($sopt);
  175.  
  176.                  for ($sopt_pos 0$sopt_pos $sopt_len$sopt_pos++{
  177.  
  178.                      //  オプションを取り出す
  179.                      $opt $sopt[$sopt_pos];
  180.  
  181.                      $value NULL;
  182.                      $do_next_arg false;
  183.                      $required = isset($shortopts[$opt]$shortopts[$optNULL;
  184.                      switch ($required{
  185.                          case ETHNA_OPTVALUE_IS_REQUIRED:
  186.                          case ETHNA_OPTVALUE_IS_OPTIONAL:
  187.                             if ($sopt_len == 1
  188.                              && $required == ETHNA_OPTVALUE_IS_REQUIRED{
  189.                                 if ($next_arg[0!= '-'// -a hoge
  190.                                     // 次の $argv を値として解釈
  191.                                     // 但し、:: の場合は解釈しない
  192.                                     $value $next_arg;
  193.                                     $pos++;
  194.                                 }
  195.                             else {
  196.                                 //  残りの文字を値として解釈
  197.                                 $value substr($sopt$sopt_pos 1);
  198.                                 $value (empty($value)) NULL $value;
  199.                             }
  200.                             if ($required == ETHNA_OPTVALUE_IS_REQUIRED
  201.                               && empty($value)) {
  202.                                  return Ethna::raiseError(
  203.                                             "option -$opt requires an argument"
  204.                                         );
  205.                              }
  206.                              // ':' または '::' が設定された場合は、次の文字
  207.                              // 以降を全て値として解釈するため、次のargv要素に
  208.                              // 解釈を移す
  209.                              $do_next_arg true;
  210.                              break;
  211.                          case ETHNA_OPTVALUE_IS_DISABLED:
  212.                              //   値を設定禁止にした場合は、値が解釈されなく
  213.                              //   なるので、値設定のチェックは不要
  214.                              break;
  215.                          default:
  216.                              return Ethna::raiseError("unrecognized option -$opt");
  217.                              break;
  218.                      }
  219.  
  220.                      //  短いオプションの場合は、- を付けないでオプション名を記録する
  221.                      //  Console_Getopt 互換にするため。
  222.                      $parsed_arguments[array($opt$value);
  223.  
  224.                      if ($do_next_arg === true{
  225.                          break;
  226.                      }
  227.                  }
  228.  
  229.              else {  // オプションとして解釈されない
  230.  
  231.                  //   non-parsed なオプションに辿り着いた
  232.                  //   ら、それ以降の解釈を停止する
  233.                  //   つまり、それ以降は全て値として解釈する
  234.                  //
  235.                  //   これは POSIX_CORRECT な実装であって
  236.                  //   GNU Getopt な実装ではないが、実際に
  237.                  //   Console_Getopt で行われている以上、
  238.                  //   それに従った実装
  239.                  $nonparsed_arguments array_slice($args$pos);
  240.                  break;
  241.              }
  242.         }
  243.  
  244.         return array($parsed_arguments$nonparsed_arguments);
  245.     }
  246.  
  247.     /**
  248.      *  短いオプション目録を解析します。
  249.      *
  250.      *  @param  string $sopts 短いオプション目録
  251.      *  @return array  オプションと引数指定種別の配列
  252.      *                  エラーの場合は Ethna_Error
  253.      *  @access protected
  254.      */
  255.     protected function _parseShortOption($sopts)
  256.     {
  257.         if (empty($sopts)) {
  258.             return array();
  259.         }
  260.  
  261.         if (!preg_match('/^[A-Za-z:]+$/'$sopts)) {
  262.             return Ethna::raiseError('invalid short options.');
  263.         }
  264.  
  265.         $analyze_result array();
  266.  
  267.         for ($pos 0$pos strlen($sopts)$pos++{
  268.             $char $sopts[$pos];
  269.             $next_char (isset($sopts[$pos 1]))
  270.                        ? $sopts[$pos 1]
  271.                        : NULL;
  272.             $next2_char (isset($sopts[$pos 2]))
  273.                         ? $sopts[$pos 2]
  274.                         : NULL;
  275.  
  276.             if ($char == ':'{
  277.                 continue;
  278.             }
  279.  
  280.             //   $sopts[$pos] is character.
  281.             if ($next_char == ':' && $next2_char == ':'{
  282.                 $analyze_result[$charETHNA_OPTVALUE_IS_OPTIONAL// 値は任意
  283.             elseif ($next_char == ':' && $next2_char != ':'{
  284.                 $analyze_result[$charETHNA_OPTVALUE_IS_REQUIRED// 値は必須
  285.             else {
  286.                 $analyze_result[$charETHNA_OPTVALUE_IS_DISABLED// 値は不要
  287.             }
  288.         }
  289.  
  290.         return $analyze_result;
  291.     }
  292.  
  293.     /**
  294.      *  長いオプション目録を解析します。
  295.      *
  296.      *  @param  array $lopts 長いオプション目録
  297.      *  @return array オプションと引数指定種別の配列
  298.      *                 エラーの場合は Ethna_Error
  299.      *  @access protected
  300.      */
  301.     protected function _parseLongOption($lopts)
  302.     {
  303.         if (empty($lopts)) {
  304.             return array();
  305.         }
  306.  
  307.         if (!is_array($lopts)) {
  308.             return Ethna::raiseError('invalid long options.');
  309.         }
  310.  
  311.         $analyze_result array();
  312.  
  313.         foreach ($lopts as $opt{
  314.             if (preg_match('/==$/'$opt0{
  315.                 $opt substr($opt0-2);
  316.                 $analyze_result[$optETHNA_OPTVALUE_IS_OPTIONAL// 値は任意
  317.             elseif (preg_match('/=$/'$opt0{
  318.                 $opt substr($opt0-1);
  319.                 $analyze_result[$optETHNA_OPTVALUE_IS_REQUIRED// 値は必須
  320.             else {
  321.                 $analyze_result[$optETHNA_OPTVALUE_IS_DISABLED// 値は不要
  322.             }
  323.         }
  324.  
  325.         return $analyze_result;
  326.     }
  327. }
  328.  
  329. // }}}

Documentation generated on Fri, 11 Nov 2011 03:58:13 +0900 by phpDocumentor 1.4.3