PHP :: コマンドラインプログラムで重宝する関数「getopt」

コマンドラインプログラムを作る上で重宝する関数だけど、挙動にくせがあるので注意が必要。


getopt -- コマンドラインの引数リストからオプションを取得する

array getopt ( string options [, array longopts] )

注意: この関数は Windows 環境にはまだ実装されていません。

スイッチの有無だけを判定したい場合

例えば、-x というスイッチが指定されているかどうかを判定したい場合。

$options = getopt('x');

※これ以降すべて、結果を var_dump($options); したものと見なして説明します。

実行)
% php /path/to/script.php -x
結果)
array(1) {
  ["x"]=>
  bool(false)
}
複数のスイッチを判定したい場合
$options = getopt('xyz');
% 実行)
php /path/to/script.php -x -y -z
結果)
array(3) {
  ["x"]=>
  bool(false)
  ["y"]=>
  bool(false)
  ["z"]=>
  bool(false)
}

なぜ、false がセットされるのかは謎。(意味的には true の方がいいのでは?)
なので存在判定は、指定したスイッチのキー(添え字)を持つ配列が存在しているかどうかで判定する。
(値が false であるかどうかは見なくても良い)


念のため、以下のようにスイッチを連続した場合の挙動も把握しておいた方がよい。
zスイッチが連続している。

$options = getopt('xyz');
実行)
% php /path/to/script.php -x -y -zz
結果)
array(3) {
  ["x"]=>
  bool(false)
  ["y"]=>
  bool(false)
  ["z"]=>
  array(2) {
    [0]=>
    bool(false)
    [1]=>
    bool(false)
  }
}

連続したスイッチは二次元配列になる。

定義していないスイッチを指定した場合
$options = getopt('x');
実行)
% php /path/to/script.php -a
結果)
array(0) {
}

空の配列。


注意点として、スイッチの文字数は - を除いた 1文字 に限定される。
-output や --quiet と指定しても内部では o と q で認識される
オプションと引数を組で取得したい場合
$options = getopt('a:');

上記のように、オプション文字に コロン を付加することでオプションと引数を組で取得できる。
例えば、-a オプションに対する引数 hoge を取得したい場合は次のように指定する。

実行)
% php /path/to/script.php -a hoge
結果)
array(1) {
  ["a"]=>
  string(4) "hoge"
}

結果は連想配列になり、指定したオプションがキー(添え字)に、そのキーの値に引数がセットされる。


複数の組み合わせを取得した時は以下のように書く。

$options = getopt('a:b:');
実行)
% php /path/to/script.php -a hoge -b fuga
結果)
array(2) {
  ["a"]=>
  string(4) "hoge"
  ["b"]=>
  string(4) "fuga"
}

なお、同名のオプションを複数指定した場合、二次元配列になる。

$options = getopt('a:b:');
実行)
% php getopt.php -a hoge -a jojo -b fuga
結果)
array(2) {
  ["a"]=>
  array(2) {
    [0]=>
    string(4) "hoge"
    [1]=>
    string(4) "jojo"
  }
  ["b"]=>
  string(4) "fuga"
}

ちなみに、オプションと引数の間のスペースは何個あってもいいし、無くてもよい。
下の例を参照。

$options = getopt('a:b:');
実行)
% php getopt.php -a     hoge -bfuga 
結果)
array(2) {
  ["a"]=>
  string(4) "hoge"
  ["b"]=>
  string(4) "fuga"
}
スイッチとオプション引数を混在させて使う場合
$options = getopt('a:b:z');
実行)
% php getopt.php -a hoge -b fuga -z
結果)
array(3) {
  ["a"]=>
  string(4) "hoge"
  ["b"]=>
  string(4) "fuga"
  ["z"]=>
  bool(false)
}
以上を踏まえ、getopt関数で はまったケースのメモと次回に備えた備忘録

ある日、下記のようにスイッチを3つ指定できるコマンドラインプログラムを作ろうとしていた。

php /path/to/script.php --on-local --on-remote --on-virtual

▼script.php から getopt関数でスイッチの判定をしているブロックの抜粋

$opts = getopt('o:');
$local_mode   = (in_array('n-local',   $opts['o'])) ? true : false;
$remote_mode  = (in_array('n-remote',  $opts['o'])) ? true : false;
$virtual_mode = (in_array('n-virtual', $opts['o'])) ? true : false;

このコードの場合、--on-local --on-remote --on-virtual のスイッチを同時に2つ、あるいは3つ指定した場合、正しく動作するがスイッチを1つしか指定しなかった場合は正しく動作しない。これでは使い物にならない。


また、それはなぜか。原因は下記のデバックを見れば一目瞭然。

$opts = getopt('o:');
var_dump($opts);

として出力している。

% php script.php --on-local --on-remote --on-virtual
array(1) {
  ["o"]=>
  array(3) {
    [0]=>
    string(7) "n-local"
    [1]=>
    string(8) "n-remote"
    [2]=>
    string(9) "n-virtual"
  }
}
% php script.php --on-local --on-remote
array(1) {
  ["o"]=>
  array(2) {
    [0]=>
    string(7) "n-local"
    [1]=>
    string(8) "n-remote"
  }
}
% php script.php --on-local
array(1) {
  ["o"]=>
  string(7) "n-local"
}

スイッチを同時に2つ、あるいは3つ指定した場合の戻り値は二次元配列。
しかし、1つの場合はただの配列。これがはまった最大の原因。

スイッチ1つの場合、前述のコードのままだと、$local_mode $remote_mode $virtual_mode はすべて false 。
回避策として以下のように1行加えて、正常に動作するようになった。

$opts = getopt('o:');
if (isset($opts['o']) && is_string($opts['o'])) $opts['o'] = array($opts['o']);
$local_mode   = (in_array('n-local',   $opts['o'])) ? true : false;
$remote_mode  = (in_array('n-remote',  $opts['o'])) ? true : false;
$virtual_mode = (in_array('n-virtual', $opts['o'])) ? true : false;

配列が一次元だった場合、二次元配列に書き換える。