フリーPHPスクリプト配布サイト。
PEAR(The PHP Extension and Application Repository)とは、PHP標準のクラスライブラリ群のことです。世界中のPHPユーザーが作成した便利なクラスが登録されています。
PEARはオープンソースで開発が進められているため、誰でも利用することができます。
PEARの詳細な解説は、以下の公式ページに記載されています。
以下では、PEARに登録されている、PEAR::DBの基本的な使用方法を紹介します。
PEAR::DBは、色々な種類のデータベースに統一した記述でアクセスできるクラスライブラリです。
PHPは標準でMySQLやPostgreSQLやSQLiteなど、色々なデータベースに接続するための命令が用意されています。データベースの種類によって条件分岐させて命令を呼び出せば、プログラムを複数のデータベースに対応させることもできます。
ですがPEAR::DBを使用していれば、同じ命令で複数のデータベースに接続ができるようになるので、さらに開発が容易になります。(PEAR::DB内部が差異を吸収してくれます。)
PEAR::DBをインストールするには、コマンドプロンプトから pear install DB を実行します。(あらかじめPEARをインストールしておく必要があります。)インストールができれば、PHPプログラムから require_once('DB.php'); とすれば呼び出すことができるようになります。
以下はPEAR::DBでMySQLを使用したサンプルプログラムです。アドレス帳のデータを保存したテーブルの内容を、順に表示しています。(テーブルはMySQLの基本的な操作で作成した address テーブルです。)
<?php
require_once 'DB.php';
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<title>サンプル</title>
</head>
<body>
<?php
$dbh = DB::connect('mysql://root:1234@localhost/phpdb');
if (DB::isError($dbh)) {
exit($dbh->getMessage());
}
$sth = $dbh->query('SELECT * FROM address WHERE no > 10 and no <= 20');
if (DB::isError($sth)) {
exit($sth->getMessage());
}
while ($data = $sth->fetchRow(DB_FETCHMODE_ASSOC)) {
echo $data['no'] . ':' . $data['name'] . "<br>\n";
}
$dbh->disconnect();
?>
</body>
</html>
次から、それぞれの処理を詳しく見ていきます。
PHPからMySQLを操作する際には mysql_connect や mysql_query といった関数が用意されていましたが、これらはMySQL専用の関数です。PostgreSQLやSQLiteを操作するためには、別途専用の関数を使用します。
ですがPEAR::DBなら、同じ命令で色々なデータベースに接続することができます。
以下はMySQL用の関数との比較です。
| 処理内容 | MySQL操作関数 | PEAR::DB |
|---|---|---|
| MySQLに接続&データベース選択 | mysql_connect ~ mysql_select_db |
DB::connect |
| SQL実行 | mysql_query ~ mysql_fetch_array |
query ~ fetchRow |
| 接続を切断 | mysql_close |
disconnect |
PEAR::DBでデータベースに接続するには、DB::connect メソッドを使用します。接続に成功するとオブジェクトが返されます。
オブジェクト = DB::connect(
'データベースの種類://ユーザー名:パスワード@接続先アドレス/データベース名'
);
MySQLに接続する場合、データベースの種類は mysql を指定します。もしPostgreSQLに接続したければ pgsql、SQLiteに接続したければ sqlite を指定します。他にも、色々な種類のデータベースに接続することができます。
DB::connect を実行後、実際に接続ができたかどうかチェックします。チェックは「返されたオブジェクトがエラーオブジェクトかどうか?」で判断しています。これは DB::isError を使用すれば調べることができます。
また、エラーの内容は $dbh->getMessage() で取得することができます。
PEAR::DBで実際にデータベースにコマンドを送るには query メソッドを使用します。引数には実行したいコマンドを指定します。
オブジェクト = $dbh->query('実行するSQL文');
レコードを取得するコマンドを指定した場合、fetchRow メソッドを使用すれば、レコードを一件ずつ取得する事ができます。引数に DB_FETCHMODE_ASSOC を指定すると、連想配列の形式で取得します
オブジェクト = $sth->fetchRow(DB_FETCHMODE_ASSOC);
取得したレコードすべてを順に取得して表示する場合、while と組み合わせて以下のように記述します。
while ($data = $sth->fetchRow(DB_FETCHMODE_ASSOC)) {
echo $data['no'] . ':' . $data['name'] . "<br>\n";
}
これで「データを1件取り出すことができれば」という条件で繰り返し処理を行うため、データが存在する間はずっと echo が実行されます。
PEAR::DBでデータベースとの接続を切断するには、disconnect メソッドを使用します。
query メソッドを実行すると、与えられたSQL文を解析し、その後実行します。ですが検索条件のみ変化するSQL文を何度も実行するような場合、毎回SQL文全体を解析するのは無駄が多いです。
しかしプレースホルダを利用すれば、SQL文の解析は最初に一度だけ行い、その後は変化する部分のみを解析&実行することができます。
以下はプレースホルダを使用したサンプルプログラムです。
<?php
require_once 'DB.php';
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<title>サンプル</title>
</head>
<body>
<?php
$dbh = DB::connect('mysql://root:1234@localhost/phpdb');
if (DB::isError($dbh)) {
exit($dbh->getMessage());
}
$sth = $dbh->prepare('SELECT * FROM address WHERE no > ? and no <= ?');
$sth = $dbh->execute($sth, array(10, 20));
if (DB::isError($sth)) {
exit($sth->getMessage());
}
while ($data = $sth->fetchRow(DB_FETCHMODE_ASSOC)) {
echo $data['no'] . ':' . $data['name'] . "<br>\n";
}
$dbh->disconnect();
?>
</body>
</html>
プレースホルダを利用しているのは以下の部分です。
$sth = $dbh->prepare('SELECT * FROM address WHERE no > ? and no <= ?');
$sth = $dbh->execute($sth, array(10, 20));
プレースホルダを利用する場合、SQL文の指定は query ではなく prepare で行います。また、検索条件など変化する部分は ? と記述しておきます。
その後 execute($sth, array(10, 20)) を実行していますが、このようにすると「最初の ? は 10」となり「次の ? は 20」という指定になります。つまり SELECT * FROM address WHERE no > 10 and no <= 20 を指定したときと同じ結果が得られます。
今回の例はもともと検索を1度実行するだけだったので、あまりプレースホルダを使用する意味はありません。ですが大量にデータを登録・更新したりする場合はプレースホルダが有効に働きます。
以上のように、プレースホルダはSQLを効率よく実行するための仕組みなのですが、プログラムに対する攻撃の防止にもなります。例えば
$sth = $dbh->query('DELETE FROM address WHERE no = ' . $_POST['no']);
このようなプログラムで、ユーザーに指定された番号でデータを削除するとします。($_POST['no'] は、フォームから入力した値が格納されます。)もしユーザーが 10 を指定した場合、
DELETE FROM address WHERE no = 10
が実行され、番号が10のデータのみ削除されます。ですがもしユーザーが 1 OR 1 = 1 という値を指定した場合、
DELETE FROM address WHERE no = 1 OR 1 = 1
が実行され、address テーブル内のデータがすべて削除されてしまいます。また、
$sth = $dbh->query('DELETE FROM address WHERE name = "' . $_POST['name'] . '"');
このようなプログラムで、ユーザーに指定された名前でデータを削除するとします。($_POST['name'] は、フォームから入力した値が格納されます。)もしユーザーが 山田太郎 を指定した場合、
DELETE FROM address WHERE name = "山田太郎"
が実行され、名前が山田太郎のデータのみ削除されます。ですがもしユーザーが 山田太郎" OR "1" = "1 という値を指定した場合、
DELETE FROM address WHERE name = "山田太郎" OR "1" = "1"
が実行され、やはり address テーブル内のデータがすべて削除されてしまいます。
これを防ぐには addslashes で ' や " をエスケープしたり、intval 関数で強制的に数値に変換すれば防ぐことができます。ですが沢山のデータベース操作処理を書いていると、どこかで書き忘れのミスが発生する可能性があります。
この対策に、プレースホルダを使用していれば、
$sth = $dbh->prepare('SELECT * FROM address WHERE no > ? and no <= ?');
$sth = $dbh->execute($sth, array(10, 20));
と、何も考えずに指定するだけで対策を行ってくれます。テーブルの設定から文字列を入力するべきか数値を入力するべきかを自動判別し、適切なエスケープ処理を自動で行います。
以上の理由から、ユーザーからの入力をもとにSQL文を作成する場合、常にプレースホルダを利用した方が良いでしょう。