TOP書籍連動> PDO(PHP Database Object)の特徴(2)
まるごと PHP!
まるごと PHP!

Part4:PDO(PHP Data Object)データベース抽象化レイヤクラス(2)

著者:岩切洋一(IWAKIRI, Yohichi)   2005/3/30
前のページ  1  2
PDO(PHP Database Object)の特徴(2)

   次に、プリペアドステートメント(コラム「プリペアドステートメント」を参照)を使ったレコード追加の例を示します(リスト2)。
リスト2:レコードの追加
try {
// PDOのエラーモードを例外モードに構成する
$dbh->setAttribute (PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION);

// トランザクションの開始
$dbh->exec('BEGIN');
// プリペアドステートメントの作成とパラメータのバインド
$stmt = $dbh->prepare ('INSERT INTO stock VALUES (:item_id, :number)');
$stmt->bindParam (':item_id', $item_id, PDO_PARAM_INT);
$stmt->bindParam (':number', $number, PDO_PARAM_INT);
$records[] = array(1, 100);
$records[] = array(2, 200);
$records[] = array(3, 50);
$records[] = array(4, 10);
$records[] = array(2, 20);

foreach ($records as $record) {
$item_id = $record[0];
$number = $record[1];
$stmt->execute();
}
$dbh->exec('COMMIT');
echo "レコード追加処理が正常に終了しました。\n";
}
catch (PDOException $e) {
// 更新処理中に例外が起きた場合、ロールバックする
$dbh->exec('ROLLBACK');
echo "レコードの追加に失敗しました。\n";
die($e->getMessage() . "\n");
}

   3行目のsetAttribute()メソッドで、PDOのエラーモードを例外モードに設定します。この設定を行わない場合は、try〜catch構文ではなく、errorCode()メソッドを逐次呼び出し、エラーかどうかを確認する必要があります。 8行目のprepare()メソッドでSQLの構文解析が行われ、PDOStatementオブジェクトが返されるので、続くbindParam()メソッドでこれを変数と関連付けます。item_idはプライマリキーで、追加レコードでは意図的にキーが重複するようにしています。結果をmysqlコマンドで見ると、ロールバックが行われたために1件も登録されていないことが確認できます。

データベース操作のための共通APIの提供

   ライトウェイトとも関連しますが、ユーザーが注意しなければならない点は、DBMS接続のためのDSN(Data Source Name)の構成だけです。それ以外は、まったく共通のAPIでデータベースを操作できるように設計されています。MySQL、PostgreSQL、ODBCでのDSNの書き方の違いを次に示します。

MySQL
'mysql:dbname=データベース名;host=ホスト名;port=ポート番号;charset=文字セット;unix_socket=ソケット名'
PostgreSQL【注1】
'mysql:dbname=データベース名 host=ホスト名 port=ポート番号 user=ユーザー名 password=パスワード'
【注1】
PostgreSQLクライアントライブラリのPQconnectdb関数に渡すことが可能な文字列を指定します。詳細については、http://www.postgresql.jp/document
/pg743doc/html/libpq.html#LIBPQ-CONNECT
を参照してください。
odbc(unixODBCを使用)
'odbc:odbcデータソース名'

   また、ユーザーDSNファイルを用意します(リスト3)。

リスト3:~/.odbc.ini
[ODBC Data Sources]
pgsql = Posrgre SQL ODBC Data Source
mysql = MySQL ODBC Data Source

[pgsql]
Driver = /usr/local/lib/libodbcpsql.so
Servername = localhost
Port = 5432
Database = データベース名
Protocol = 7.4
ReadOnly = No
;ConnSettings = set client_encoding to euc_jp

[mysql]
Driver = /usr/local/lib/libodbcmyS.so
Servername = localhost
ReadOnly = No

   この部分はDBMSクライアントライブラリの仕様に依存するため、なかなか統一の取れない部分であり、注意が必要です。そのほか、一部のメソッドはドライバによっては実装が追いついていないものも見受けられますが、正式版が出る際には対応されることが期待できます。たとえばMySQLドライバでトランザクションを行う場合、次のメソッドは利用できません。

$dbh->beginTransaction();
$dbh->commit();
$dbh->rollBack();

   代わりに次のメソッドを使います。

$dbh->exec('BEGIN');
$dbh->exec('COMMIT');
$dbh->exec('ROLLBACK');
プリペアドステートメント

   通常のSQL文の実行は、問い合わせのたびに、構文解析が行われ、実行計画が決まります。プリペアドステートメントを利用すると、事前にSQLの構文解析を行い、実行計画を決めることができます。そのため、同一SQLを繰り返す処理では、実行計画を決める処理を省略できることになり、結果的に実行時間の短縮が可能となります。また、プレースホルダの役割もあります。プレースホルダは、ダイレクトSQLコマンドインジェクション対策が行えるなどの利点があります。ユーザー入力をもとに、不注意にSQL文を組み立てることは、非常に危険です(詳細については、IPAのセキュアプログラミング講座をご覧になることをお勧めします)。たとえば、次のコードを見てください。

$query = "UPDATE user_info
SET passwd = '$new_passwd'
WHERE user = '$user'
AND passwd = '$old_passwd'";

   $old_passwdに「"古いパスワード' or user = '別ユーザー"」が入っていると、どうなるか想像がつくでしょうか。こうすると、別ユーザーのpasswdを無条件で変更できることになり、不正アクセスにつながります。PDOは、bindParam()メソッドでプレースホルダと変数を関連付ける際に型を指定する必要がありますが、先ほどの$old_passwdのような値が渡された場合は、「"古いパスワード'' or user = ''別ユーザー"」のように、エスケープ処理が行われます。

   残念ながら、現段階ではPDOのociドライバを除くと、実行計画に要する時間の短縮は実装されていませんが、プレースホルダ機能は利用できるので、積極的にこれを使いましょう。なお、MySQL 4.1以降、およびPostgreSQL 7.3以降では、PREPARE文がサポートされているので、PDOでも対応されることでしょう。

前のページ  1  2



著者プロフィール
著者:岩切洋一
「まるごとPHP!Vol.1(インプレス刊)」にて本記事「PDOデータベース抽象化レイヤクラス」の執筆や、「WEB+DB PRESS Vol.18(技術評論社刊)」にて「PEAR実践入門 第4章 使ってみよう!作ってみようPECL」の執筆などを行う。


INDEX
Part4:PDO(PHP Data Object)データベース抽象化レイヤクラス(2)
  PDO(PHP Database Object)の特徴(1)
PDO(PHP Database Object)の特徴(2)