PHP PEARパッケージ

PEARとは、 PHP本体に標準で付属している、コードの再利用性を高めるためのコンポーネント・ フレームワークとしての「標準ライブラリ」です。 例えばデータベース・アクセスに関するPHPの元来のAPIは、 接続先のRDBMS製品によって異なるAPIの使い方をするため、 RDBMS製品が変わるとコードが全く変わってしまう他に、 APIを1から覚え直す必要があるなど、非効率であるという問題があります。 PEARは、このような特にコードの再利用性、 標準化という面で高水準のフレームワークを提供し、 誰が書いても統一した記述になるなどコードの保守性向上に寄与します。


Windowsでのインストール手順

PHP5をインストールしたディレクトリにある、「go-pear.bat」を実行するだけです。 表示されるメッセージに従って操作すれば簡単に終わります。
インストールが済んだら、PEARディレクトリのパスを php.iniのinclude_pathに設定して、Apacheを再起動します。


PEAR DBライブラリ

現在PEARが扱っている領域は大きく「データベースアクセス」「キャッシュ」 「Webフォーム表示」「ロギング」の4つがあります。 その中でも活用範囲が広く、 効果が際立ちやすいのはデータベースアクセスのライブラリです。 PEARにおけるDB関係のライブラリの中では、DBとMDB2が有名です。 (MDBというものもありましたが開発が終了してしまい、MDB2に引き継がれました) DBが標準、MDB2がそれの改良版という位置づけになっています。 DBを入手するには、UNIXでもWindowsでも、次のように打てばOKです。

pear install DB

インターネットへの接続がプロキシー経由の環境下では、 事前に以下のコマンドでプロキシーの設定をしておきます。

pear config-set http_proxy http://[プロキシーサーバーのアドレス]:[プロキシーサーバーのポート番号]

この設定値は、次のコマンドで確認できます。

pear config-show

それでもうまくいかなければ、 pear.php.netから別途DB-*.zipをダウンロードし、 展開してから、DB.phpとDBフォルダをPEARディレクトリの中にコピーするのでもokです。

簡単な使用例を下に挙げました。

<?php
require_once 'DB.php';

function execute_query($address_code) {
  $dsn = "mysql://urano:urano398@localhost/udb";
  $con = DB::connect($dsn);
  $q1 = $con->quote($address_code);

  $s = <<<SQL
SELECT ADDRESS_CODE, ADDRESS FROM MAIL_ADDRESS
 WHERE ADDRESS_CODE = $q1
SQL;
  $result = $con->getAll($s);
  if($con->isError($result)){
    print "Error\n" . $result->message;
    $arr = array();
  }
  else{
    $arr = $result;
  }
  $con->disconnect();
  return $arr;
}

function show_result($resultset){
  print <<<RESULT_HEADER
<table border="1">
<tr><th>識別子</th><th>メールアドレス</th></tr>
RESULT_HEADER;
  for($i=0; $i<count($resultset); $i++){
    $a = $resultset[$i];
    $address_code = $a[0];
    $address = $a[1];
    print "<tr><td>$address_code</td><td>$address</td></tr><br>";
  }
  print <<<RESULT_FOOTER
</table>
<a href="pear-search-ma.php">検索条件入力に戻る</a><br>
RESULT_FOOTER;
}

#****** ここからメイン
if(isset($_POST["address_code"])){
  $resultset = execute_query($_POST["address_code"]);
  show_result($resultset);
}
else{
?>
<form action="pear-search-ma.php" method="POST">
アドレス識別子:
<input type="text" name="address_code">
<br>
<input type="submit" value="検索">
</form>
<?
}
?>

データベースへの接続
PEARでは、データベースに対する接続情報を、「DSN」と呼ばれる書式の文字列で管理します。 これは簡単には、下の例のような形になります。

$dsn = "mysql://urano:urano398@localhost/udb";
$con = DB::connect($dsn);
if(DB::isError($con)){
    die("エラー!データベースに接続できません:" . $con->message);
}

つまり、「データベース種別://ユーザ:パスワード@ホスト名/データベース名」です。 上のコードはMySQLの例ですが、PEARにサポートされている他のRDBMSでも、大体同じような指定をします。 例えばOracleの場合も
"oci8://urano:urano398@MYDB"
のようになります。接続がOCI8ということはApache+PHPのサーバには Oracle Clientもインストールされていて、 Apache+PHPのサーバ上のOracle Clientからデータベース(インスタンス) へ接続する際のTNS文字列がMYDBという意味です。

最も簡単な検索
通常のデータベース検索(SELECT文による問合せ)は、データベースの検索結果 (結果集合=ResultSetと呼ばれます)を、配列の配列で持つのが一般的です。 つまり、各カラムに対応するデータを要素としてもつ配列がいわゆる「行データ」 となり、検索結果が複数行の場合はそれらがさらに配列になって表現される、 というわけです。この最も一般的な形で結果集合を持たせたい場合には、 getAllというAPIひとつで全ての処理を行ってくれます。

$sql = "SELECT * FROM FOODS";
$result = $con->getAll($sql);
if($con->isError($result)){
    print "Error\n" . $result->getMessage();
}

この変数resultは、問合せに成功したら先述の通り配列の配列を、 エラーが発生したらエラーオブジェクトがセットされます。 エラーの場合、その内容は $result->getMessage() で取得できます。

接続の終了
データベース接続の終了は、disconnectで行います。
「close」ではありませんので他の言語でのデータベース・プログラミングに慣れている方は要注意。

$con->disconnect();

エラー処理
各APIは、エラーが発生すると、本来返すはずのオブジェクトの代わりに、 DB_ERRORクラスのオブジェクトを返します。 戻り値がDB_ERRORのインスタンスであるかどうかを判定するには、 isErrorメソッドを、 基本的なエラーメッセージを取得するにはgetMessage()メソッドを使います。

if(DB::isError($con)){
  print "接続エラー:" . $con->getMessage() . "<br>";
  print $con->getDebugInfo();
  return "";
}

ただし、このgetMessage()メソッドは問題の解決にはあまり役に立ちません。 RDBMSが返すエラーメッセージを表示するには、 getDebugInfo()またはgetUserInfo()メソッドを使います。 ただし、これらのメソッドが返す値にはしばしばDSNが登場します (つまり、データベースに接続するためのユーザとパスワードが分かります)。 これをエンドユーザに見せてよいのかどうか、使う場所には十分注意する必要があります。

プレースホルダーの使用
PEARライブラリを使う場合、SQL文の中にプレースホルダー というものを含めることができます。 データベース開発に慣れている方は例をご覧になった方が早いですね。 こんなのです。

$sql = <<<SQL
  SELECT * FROM FOODS WHERE NAME = ? AND PRICE < ?
SQL;

この「?」がプレースホルダーです。PEARはこの形でまずSQL文を持っておき、 SQLの実行時には、一緒に渡されるパラメータの配列を上の? と並び順に結び付けて、処理を行います。つまり、上のSQL文はテンプレートみたいなもの、 というと分かりやすいでしょう。
実際のコードは次のようになります。

$sql = "SELECT * FROM FOODS WHERE NAME = ? AND PRICE < ?";
$params = array("りんご", "998");
$result = $con->getAll($sql, $params);
if($con->isError($result)){
    print "Error\n" . $result->message;
}

getAllの引数として、パラメータの配列が追加されています。今度は、 プレースホルダーを含むSQL文とこのパラメータを実行時に結び付けて、 SQL文を生成します。
なお、このプレースホルダーをこれと同じ形か多少違う形でも内部的に処理する機構を持っているデータベース (例えばOracle)の場合は、PEARはSQL文のプレースホルダーに関する解析を (各々のデータベースAPI関数を利用して) データベースに任せます。 その結果、同じ形のSQLを何度も発行する場合には、一般に処理が高速になります。 一方そうでないデータベース(例えばMySQL)の場合は、 PEARが自分で?をパラメータに1個ずつ置き換えています。 この場合は、プレースホルダーを使ったからといって著しく性能が向上する、 というわけではないようです。

データベースを更新するSQLの実行
INSERT文、UPDATE文、DELETE文のいわゆるDML文を実行するのも簡単です。

$stmt = $con->prepare($sql);
$result = $con->execute($stmt, $params);
if($con->isError($result)){
    print "エラー! データベースを更新できませんでした。";
    $con->rollback();
} else {
    print "${result}行のデータを更新しました。";
    $con->commit();
}

DML文を実行する場合は、prepareexecuteをこのように使うのが簡単でしょう。 先述のパラメータ配列を指定する位置に要注意、 OracleのOCI(Oracle Call Interface)などに慣れている方には当然ですが、 まずSQL文を(プレースホルダーを含んでいる場合もそのまま)構文解析して、 実行時にこれにパラメータ配列を代入するわけです。 従って、パラメータ配列を変化させながら同じSQL構文を何度も実行する場合には、 毎回構文解析をせずとも最初の1回だけでよいので、処理が速くなります。

データベースを検索するSQLの実行(その2)
先のgetAll関数以外に、データベースを検索して、 結果集合の行データに1行ずつアクセス(フェッチ)してデータを取り出す方法もあります。 PEARのOCI8を使ってOracleデータベースにアクセスする場合、 getAll関数ではプレースホルダーのあるSQL文を正しく扱えないようで、 ? 文字が「ORA-00911: 文字が無効です」エラーになってしまいます。 そこで、この場合は次のように書きます。

$stmt = $con->prepare($sql);
$result = $con->execute($stmt, $params);
$resultset = array();
if($con->isError($result)){
    print "Error\n" . $result->getMessage() . "(" . $result->getUserInfo() . ")";
} else {
    while($row = $result->fetchRow()){
        array_push($resultset, $row);
    }
}

なお、「fetchRow」を「fetchRow(DB_FETCHMODE_ASSOC)」と書き換えることで、 各行のデータを単なるリストの代わりに、 列名をキーとする連想配列として取得することができます。

もう1つのスクリプト全体の例です。

<html><body>
<?php
require_once 'DB.php';
error_reporting(E_ALL);

/*
 * 各行のデータを連想配列の連想配列で取得する場合
 * (1列目が各行を識別するキーの場合のみ有効)
 * getAssocのDB_FETCHMODE_ASSOCをDB_FETCHMODE_ORDERED(DEFAULT)
 * にすると、各行の各列を識別する添え字が列名ではなく数字になる
 */
function query1($con, $sql, $params){
    $result = $con->getAssoc($sql, false, $params, DB_FETCHMODE_ASSOC);
    //$result = $con->getAssoc($sql, false, $params);
    $n = 0;
    while(list($keyvalue, $row) = each($result)){
        print "====== LINE " . (++$n) . " RECORD KEY=[" .
         $keyvalue . "] ======<br/>\n";
        while(list($key, $value) = each($row)){
            print "[" . $key . "] = [" . $value . "]<br/>\n";
        }
    }
}

/*
 * 各行のデータを連想配列のリストで取得する場合
 * fetchRowのDB_FETCHMODE_ASSOCをDB_FETCHMODE_ORDERED(DEFAULT)
 * にすると、各行の各列を識別する添え字が列名ではなく数字になる
 */
function query2($con, $sql, $params){
    $st = $con->prepare($sql);
    $result = $con->execute($st, $params);
    $n = 0;
    while($row = $result->fetchRow(DB_FETCHMODE_ASSOC)){
        print "====== LINE " . (++$n) . " ======<br/>\n";
        while(list($key, $value) = each($row)){
            print "[" . $key . "] = [" . $value . "]<br/>\n";
        }
    }
}

$dsn = "mysql://urano:urano398@localhost/udb";
$con = DB::connect($dsn);
if(DB::isError($con)){
    die("エラー!データベースに接続できません:" . $con->message);
}
$sql=<<<AAA
SELECT SHITEN_CODE, SHITEN_NAME, AREA_CODE, ADDRESS, EMPLOYEE_NUM
  FROM SHITEN
 WHERE AREA_CODE = ?
 ORDER BY SHITEN_CODE
AAA;

// パラメータが1つの場合どちらでもよい
$params = "3";
//$params = array("3");

// MySQL5で必要
$con->getAll("SET NAMES SJIS");
query2($con, $sql, $params);
$con->disconnect();
?>
</body></html>


その他のメモ

extension not found?
Windows環境で、このエラーメッセージが出る原因は、

  • DSNの書式が正しくない。
  • php.iniで、対応するRDBMSのDLLをextensionとしてロードしていない。
が主なものです。確認してみましょう。 またMySQLの場合、PHPのルートディレクトリに置いてあるlibmysql.dllとlibmysqli.dllを、 Apacheのbin(Apache.exeのあるディレクトリ)にコピーする必要があります。

MySQLでの日本語文字化け対策(文字コードの設定)
MySQLでSQLを発行すると、応答に含まれる日本語が全て?になってしまう場合は、 事前に次のようなSQLを一度発行すればOKです。

$con->query("SET NAMES sjis");

"sjis"はもちろんシフトJISの意味です。日本語EUCの場合は"ujis"と指定します。

Open Source Web Architecture Top

(first uploaded 2002/05/12 last updated 2010/07/29, MISUMI URANO)

テレワークならECナビ Yahoo 楽天 LINEがデータ消費ゼロで月額500円〜!
無料ホームページ 無料のクレジットカード 海外格安航空券 海外旅行保険が無料! 海外ホテル