pearコマンドの中身

でびあんだとsudo apt-get install pearで入ってくれるPEARですが、いろんなパッケージをダウンロードしたりインストールしたりしてくれるpearコマンド(/usr/bin/pear)の実体は、/usr/share/php/pearcmd.phpを経由してPEAR_Commandクラスの対応する関数を実行しているだけです。

なので、シェルから

 % pear discover-channel pear.example.jp
 % pear install example/Example

とかやっていたのは、phpから直接PEARパッケージのクラスを使って実行することもできるんですよ!

必要なもの

まず、pearコマンドを構成する要素として

  • PEAR_Command, PEAR_Command_*
    • コマンドの実体
  • PEAR_Config
    • 設定オブジェクト(いつのまにかできてる.pearrcの中身)
  • PEAR_Frontend
    • CLI, Web, Gtk, Gtk2とかがあるらしい

がたぶん重要です。ぜんぶ http://pear.php.net/package/PEAR/docs/latest/ のクラス一覧にあるやつです。

今はphpスクリプトから直接呼び出したいだけなので、PEAR_FrontendについてはCLIだけ考えればよさそうです。Webとかは、たぶん/usr/bin/pearコマンド(CLI)に相当するCGIとかあるんでしょうね。

pearcmd.phpを眺める

重要そうなところをつまみぐいしていくと、

PEAR_Command::setFrontendType('CLI');
$ui = &PEAR_Command::getFrontendObject();
$config = &PEAR_Config::singleton($pear_user_config, $pear_system_config);
$cmd = PEAR_Command::factory($command, $config);
$ui->outputData("ERROR: $_file is not a valid config file or is corrupted.");
$ok = $cmd->run($command, $opts, $params);

というかんじですね。

それぞれの役割を考えると、

  • $ui: エラーメッセージの表示とかをおまかせするオブジェクト
  • $config: 登録したチャンネルとか、どこにインストールするかなどの設定情報
  • $command: 'install' とか 'remote-list' とか、いわゆるコマンド名
  • $cmd: コマンドオブジェクト, $cmd->run() で実行する

ということのようです。

ちょっと面倒そうなのは、run()の引数の$opts, $paramsってとこですね。

run()の中身は?

 % pear help

とやると、ずらずらといろんなコマンドが出てくるわけですが、PEAR_Command_*の数と比べるとずいぶん少ないかんじです。

どうやら$commandとして指定していたものは、/usr/share/php/PEAR/Command/*.phpに直接対応するのではなく、その中でいくつかまとめて定義されているコマンドの1つを指定していたのでした。

たとえばPEAR/Command/Install.php(PEAR_Command_Installクラス)では、

  • install
  • upgrade
  • upgrade-all
  • uninstall
  • bundle (コレなに?)
  • run-scripts

という$commandがまとめて定義されています。ファイルの先頭に var $commands = array( ... ); という長いのがありますよね。

思えばPEAR_Command::factory()に$command渡しておきながら、run()でもう一度$command渡さなきゃいけなかったのは、こういう2段構えだったからなんですね。

さっきのvar $commandsをよく眺めると、

        'install' => array(
            'summary' => 'Install Package',
            'function' => 'doInstall',
            'shortcut' => 'i',
            'options' => array(
                'force' => array(
                    'shortopt' => 'f',
                    'doc' => 'will overwrite newer installed packages',
                    ),

とか書いてるので、run()で呼ばれるのは doInstall() とか(後ろのほうで定義されてます)で、$optsはこれを見ながら決めればよさそうです。

あとpearcmd.phpで$paramsを作っている様子を見るに、ふだんpearコマンドで引数に与えていたのをそのまま(普通の)配列にしてあげれば良いと。

やってみよう

ということでものは試し。$optsと$paramsがちゃんとあるやつ、ってことで、

 % pear remote-info -c pear PEAR

をやってみます。

<?php

require_once 'PEAR.php';
require_once 'PEAR/Command.php';
require_once 'PEAR/Config.php';

$command = 'remote-info';
$opts = array('channel' => 'pear');
$params = array('DB');


PEAR_Command::setFrontendType('CLI');
$config = PEAR_Config::singleton();
$cmd = PEAR_Command::factory($command, $config);
$cmd->run($command, $opts, $params);

?>

pearは反応が遅いのですげー待たされますが、タバコ1本吸って戻ってみると、ちゃんと結果が表示されました! わーい!

Package details:
================
Latest      1.7.6
Installed   1.7.6
Package     DB
License     PHP License
Category    Database
Summary     Database Abstraction Layer
Description DB is a database abstraction layer providing:
            * an OO-style query API
            ...

$ui(Frontend)が出てきてないですが、とくに自分でメッセージを出す気がなければPEAR_Command::factory()のなかで勝手に作っておいてくれます。上のメッセージはそこから出てきたんだと思う。

明日はinstallをもうちょっと追ってみようと思います。