PHP Data Objects (PDO) 拡張モジュールは、 PHP の中からデータベースにアクセスするための軽量で高性能な インターフェイスを定義します。 PDO インターフェイスを実装する各データベースドライバは、 正規表現関数のようなデータベース固有の機能を提供することができます。 PDO 拡張モジュールによりそのデータベースの全てのデータベース関数を 実行できるわけではないことに注意してください。 データベースサーバにアクセスするには、 データベース固有の PDO ドライバ を使用する必要があります。
PDO は、データアクセスの抽象化レイヤを提供します。 つまり、使用しているデータベースが何であるかにかかわらず、同じ 関数を使用してクエリの発行やデータの取得が行えるということです。 PDO は、データベースの抽象化を行うのでは ありません。つまり、SQL を書き直したり 存在しない機能をエミュレートしたりはしないということです。 もしそのような機能が必要なら、全体を網羅する (full-blown) 別の抽象化レイヤを使用すべきです。
PDO は PHP 5.1 以降にバンドルされており、PHP 5.0 では PECL 拡張モジュールとして使用可能です。PDO は PHP 5 の新機能である オブジェクト指向機能を使用しており、それより前のバージョンの PHP では動作しません。
PHP 5.1 を動作させているのならば、PDO および PDO_SQLITE はその配布物の中に含まれています。configure を実行した際に PDO は自動的に有効になります。 PDO は、共有モジュールとしてビルドすることを推奨します。 なぜなら、PECL からアップデート版を導入する際にそのほうが有利だからです。 PHP を PDO サポート込みでビルドする際の推奨設定は、 zlib サポートを有効にする (pear インストーラを使用するため) ことです。 また、あなたが選択したデータベースについての PDO ドライバを有効にする必要もあります。 詳細についてはデータベース固有の PDO ドライバ を参照ください。PDO を共有モジュールとしてビルドした場合は、 PDO ドライバも共有モジュールとしてビルドする必要があることに注意しましょう。 SQLite 拡張モジュールは PDO に依存しています。そのため、PDO を共有モジュールとしてビルドした場合には SQLite も同じようにビルドしなければなりません。
./configure --with-zlib --enable-pdo=shared --with-pdo-sqlite=shared --with-sqlite=shared |
PDO を共有モジュールとしてインストールした後は、PHP の実行時に PDO 拡張モジュールが自動的に読み込まれるよう、php.ini ファイルを編集する必要があります。 また同様に、データベース固有のドライバについてもここで有効にする必要があります。 この設定は pdo.so の行より後で記述するようにしましょう。なぜなら、 データベース固有のドライバが読み込まれるためには、それ以前に PDO が初期化されていなければならないからです。 PDO およびデータベース固有のドライバを静的にビルドしたのならば、 この設定は不要です。
extension=pdo.so |
PDO を共有モジュールとしてビルドすると、新しいバージョンの PDO が公開された際に pear upgrade pdo コマンドで更新することができるようになります。 その際に PHP 自体を再ビルドする必要はありません。注意すべき点として、 PDO をこの方法で更新した際はデータベース固有のドライバも同時に更新するようにしてください。
PDO は、 http://pecl.php.net/package/pdo から PECL 拡張モジュールとして使用可能です。 pear ツールを使用してインストールを行います。 このツールは、PHP の configure 時に自動的に使用可能になります。 pear が圧縮されたパッケージを取り扱えるよう、 PHP が --with-zlib を含めて confiure されていることを 確認しておきましょう。
以下のコマンドにより、最新の安定版の PDO をダウンロードしてビルドを行い、そしてインストールします。
pear install pdo |
pear コマンドは、PDO モジュールを自動的に PHP のエクステンションディレクトリにインストールします。 Linux や Unix 上で PDO を有効にするには、以下の行を php.ini に追加する必要があります。
extension=pdo.so |
PECL パッケージのビルド方法についてのより詳細な情報は、マニュアルの PECL 拡張モジュールのインストール を参照ください。
PDO および主要データベースのドライバは、共有モジュールとして PHP に同梱されています。これを使用するには、単に php.ini ファイルを編集するだけです。
extension=php_pdo.dll |
次に、その他のデータベース固有の DLL ファイルを選択します。 実行時に dl() によりロードするか、または、 php.ini の php_pdo.dll の下で有効にしてください。 例えば、以下のようになります。
extension=php_pdo.dll extension=php_pdo_firebird.dll extension=php_pdo_informix.dll extension=php_pdo_mssql.dll extension=php_pdo_mysql.dll extension=php_pdo_oci.dll extension=php_pdo_oci8.dll extension=php_pdo_odbc.dll extension=php_pdo_pgsql.dll extension=php_pdo_sqlite.dll |
これらの DLL は、システムの extension_dir で指定した場所に存在する必要があります。 PDO_INFORMIX は PECL 拡張モジュールにのみ存在することに注意しましょう。
php.ini の設定により動作が変化します。
PHP_INI_* 定数の詳細および定義については 付録G を参照してください。
以下に設定ディレクティブに関する 簡単な説明を示します。
DSN の別名を定義します。完全な説明については PDO::__construct を参照ください。
以下のドライバが現在 PDO インターフェイスを実装しています。
ドライバ名 | サポートされるデータベース |
---|---|
PDO_DBLIB | FreeTDS / Microsoft SQL Server / Sybase |
PDO_FIREBIRD | Firebird/Interbase 6 |
PDO_INFORMIX | IBM Informix Dynamic Server |
PDO_MYSQL | MySQL 3.x/4.0 |
PDO_OCI | Oracle Call Interface |
PDO_ODBC | ODBC v3 (IBM DB2、unixODBC そして win32 ODBC) |
PDO_PGSQL | PostgreSQL |
PDO_SQLITE | SQLite 3 と SQLite 2 |
PDO 基底クラスのインスタンスを作成することにより、接続が確立されます。 どのドライバを使用するのかにかかわらず、常に PDO クラスを指定します。 コンストラクタに渡す引数により、データソース (いわゆる DSN) の指定や (もしあれば、オプションで) ユーザ名およびパスワードの指定を行います。
接続時になんらかのエラーが発生した場合、PDOException オブジェクトがスローされます。エラー処理を行いたい場合はこの例外を キャッチします。あるいはこれを無視して、 set_exception_handler() で設定した グローバル例外ハンドラに処理を任せることもできます。
警告 |
PDO コンストラクタからの例外をアプリケーション内でキャッチしない場合、 zend エンジンはスクリプトの実行を終了し、バックトレースを表示します。 このバックトレースを見れば、データベースへの接続の詳細がわかってしまいます。 その中にはユーザ名やパスワードも含まれます。 (catch 文を使用して) 明示的に例外をキャッチするか、 あるいは set_exception_handler() を使用して 暗黙的に例外をキャッチするようにしましょう。 |
データベースへの接続に成功すると、PDO クラスのインスタンスが スクリプトに返されます。この PDO オブジェクトが存在する間、 接続がアクティブであり続けます。接続を閉じるには、他から 参照されていないことを保障することでオブジェクトを破棄する 必要があります。それには、オブジェクトを保持している変数に対して NULL を代入します。 明示的にこれを行わなかった場合は、スクリプトの終了時に自動的に 接続が閉じられます。
データベースサーバへの持続的な接続による恩恵をこうむる web アプリケーションは多いでしょう。持続的な接続は、スクリプトが 終了しても閉じられずにキャッシュされ、他のスクリプトが同じ内容の 接続を要求してきた際にそれが再利用されます。持続的接続の キャッシュにより、スクリプトがデータベースを使用するたびに 新しい接続を確立するオーバーヘッドを避けることができます。 それにより、結果として web アプリケーションを高速化できるように なります。
注意: PDO ODBC ドライバを使用しており、ODBC ライブラリが ODBC 接続プーリングをサポートしている場合 (unixODBC および Windows はこれをサポートしています。他にもあるかもしれません) は、 PDO の持続的接続を使用せずに ODBC の接続プーリングに 接続キャッシュ処理を任せることを推奨します。 ODBC の接続プールは、プロセス内で他のモジュールと共有されています。 PDO が接続をキャッシュしてしまうと、その接続は ODBC の 接続プールに返されなくなり、他のモジュールによって新たな接続が 作成されてしまうようになります。
さあ、PDO を使用してデータベースに接続することができました。 クエリを発行する前に、PDO がトランザクションをどのように扱うのかを 理解しなければなりません。トランザクションについてよくわからない方の ために説明すると、これは以下の 4 つの機能、つまり 原子性 - Atomicity、一貫性 - Consistency、独立性 - Isolation および 永続性 - Durability (ACID) を提供するものです。 一般的な言葉で言うと、トランザクション内で実行された作業は たとえ段階的に行われたものであってもデータベースに安全に反映される ことが保証されています。トランザクションのコミット時に他の接続の 干渉を受けることはありません。また、トランザクション内での作業は (まだコミットされていなければ) いつでも自動的に取り消すことが できます。これにより、スクリプト内でのエラー処理がより楽になります。
トランザクションの一般的な実装は、変更内容を一時的に「蓄えて」おき、 それを一気に適用するようになっています。この実装には、更新処理の 性能を劇的に向上させるという効果もあります。つまり、 トランザクションによってあなたの書くスクリプトはより高速になり、 またより堅牢になるということです (これらの恩恵をうけるためには、トランザクションを正しく使用する 必要があります)。
残念ながら、すべてのデータベースがトランザクションをサポートしていると いうわけではありません。そのため、PDO で最初に接続をオープンした際には、 いわゆる「自動コミット」モードで動作します。自動コミットモードとは、 もしデータベースがトランザクションをサポートしていたら個々のクエリが 暗黙的なトランザクションのもとで実行され、サポートしていなかったら トランザクションを使用せずに実行されることを意味します。 トランザクションを使用する場合は、 PDO::beginTransaction() メソッドを使用して トランザクションを初期化する必要があります。使用しているドライバが トランザクションをサポートしていない場合は PDOException が スローされます (これは深刻な状態であるため、エラー処理の設定に かかわらず常にスローされます)。初期化した後でトランザクションを 終了させるには、トランザクション内でのコードが成功したか否かに応じて PDO::commit() あるいは PDO::rollBack() を使用します。
スクリプトが終了したり接続が閉じられようとした際に、もし処理が 完了していないトランザクションがあれば PDO が自動的に ロールバックします。これは、スクリプトが予期せぬ状態で終了した場合に データの不整合が発生するのを避けるための安全装置です。もし 明示的にコミットしていなければ、おそらく何かおかしなことが 起こったのだろうと推測されます。そのため、データを守るために ロールバックが行われるのです。
警告 |
自動的にロールバックが行われるのは、トランザクションを PDO::beginTransaction() で開始した場合のみです。 トランザクションを開始するクエリを手動で発行した場合、 PDO はそれを知ることができません。そのため、何か問題が発生しても ロールバックすることはできないのです。 |
トランザクション内で行える処理は、更新には限りません。たとえば 何か複雑なクエリを発行してデータを抽出し、その情報をもとに データの更新をしたり別のクエリを実行したりすることも可能です。 トランザクションがアクティブな間は、作業中のデータについては 他から一切変更が加えられないことが保証されます。 実際のところこの説明は 100% 正確というわけではありませんが、 もしあなたがいままでトランザクションのことを知らなかったのであれば このように理解しておけば十分でしょう。
より成熟したデータベースの多くは、プリペアドステートメントという 概念をサポートしています。プリペアドステートメントとはいったい何の ことでしょう? これは、実行したい SQL をコンパイルした 一種のテンプレートのようなものです。パラメータ変数を使用することで SQL をカスタマイズすることが可能です。プリペアドステートメントには 2 つの大きな利点があります。
クエリのパース (あるいは準備) が必要なのは最初の一回だけで、 同じパラメータ (あるいは別のパラメータ) を指定して何度でも クエリを実行することができます。クエリを実行するには、準備として クエリの解析やコンパイル、そして実行プランの最適化が行われます。 クエリが複雑になると、この処理には時間がかかるようになります。 同じクエリを異なったパラメータで何度も実行すると、アプリケーションの 動作は目に見えて遅くなるでしょう。 プリペアドステートメントを使用すると、この 解析/コンパイル/最適化 の繰り返しを避けることができます。 端的に言うと、プリペアドステートメントは使用するリソースが少なくいため 高速に動作するということです。
プリペアドステートメントに渡すパラメータは、引用符で括る必要は ありません。それはドライバが自動的に行います。 アプリケーションで明示的にプリペアドステートメントを使用するように すれば、SQL インジェクションは決して発生しません (しかし、もし信頼できない入力をもとにクエリの他の部分を構築している のならば、その部分に対するリスクを負うことになります)。
プリペアドステートメントは非常に有用な機能なので、もしドライバが サポートしていなくても、例外的に PDO がこの機能をエミュレートします。 これにより、データベースの機能にかかわらず同じ仕組みで データベースへのアクセスができることが保証されます。
例 6. プリペアドステートメントを使用して、繰り返し挿入処理を行う この例は、name および value を名前つきプレースホルダで置き換えて INSERT クエリを実行します。
|
例 7. プリペアドステートメントを使用して、繰り返し挿入処理を行う この例は、name および value をプレースホルダ ? で置き換えて INSERT クエリを実行します。
|
データベースドライバがサポートしていれば、入力パラメータだけでなく 出力パラメータもバインドすることが可能です。出力パラメータは、 一般にストアドプロシージャから値を受け取るために使用します。この場合、 返される値の大きさがどの程度になるのかをバインド時に知っておく必要が あります。指定した大きさよりも大きな値が返されると、エラーが発生します。
入出力の両方に使用するパラメータを指定することもできます。 このパラメータの書式は、出力パラメータと同じです。 次の例では、ストアドプロシージャに文字列 'hello' を渡しています。 プロシージャの結果が返ってくると、 この文字列はプロシージャの返す値に置き換えられます。
PDO が提供するエラー処理方法は 3 通り存在し、 アプリケーションの開発形態によって使い分けることができます。
PDO::ERRMODE_SILENT
デフォルトのモードです。ステートメントおよびデータベースオブジェクトの エラーについて、PDO は単にそのエラーコードのみを設定します。 これを取得するには PDO::errorCode() および PDO::errorInfo() メソッドを使用します。 ステートメントオブジェクトへのコールによってエラーが発生した場合は、 そのオブジェクトの PDOStatement::errorCode() あるいは PDOStatement::errorInfo() メソッドを呼び出します。 データベースオブジェクトへのコールによってエラーが発生した場合は、 その代わりにデータベースオブジェクト上の同じメソッドを呼び出します。
PDO::ERRMODE_WARNING
エラーコードを設定することに加え、PDO は 伝統的な E_WARNING メッセージも出力します。この設定はデバッグ/テストの際に有用で、 アプリケーションの動作を妨げることなしに問題点を確認できるように なります。
PDO::ERRMODE_EXCEPTION
エラーコードを設定することに加え、PDO は PDOException をスローします。エラーコードや 関連情報が、クラスのプロパティとして設定されます。 この設定もまたデバッグ時に有用で、エラーが発生した時点で スクリプトの実行を停止させることによりコード内の問題点を 見つけやすくなります (例外によりスクリプトが終了した際には、トランザクションは自動的に ロールバックされることを覚えておきましょう)。
このモードが有用である理由のひとつとして、伝統的な PHP 形式の警告よりも より明確にエラー処理コードが書けることがあります。例外を発生させず、 データベースへのコールのたびに毎回明示的に返り値をチェックすることに 比べると、コードの量やネストを減らすことができます。
PHP の例外についての詳細な情報は、 例外 を参照ください。
PDO のエラーコードは、SQL-92 の SQLSTATE エラーコード文字列に 標準化されています。 ネイティブのコードを適切な SQLSTATE コードに変換するのは、個々の PDO ドライバの仕事となります。 PDO::errorCode() メソッドは SQLSTATE コードを返します。 エラーについての詳細な銃尾法が知りたい場合、PDO では PDO::errorInfo() メソッドも提供しており、 これは SQLSTATE コード、ドライバ固有のエラーコードおよびドライバ固有の エラーメッセージを含む配列を返します。
アプリケーション内で、データベースに「大きな」データを格納する 必要を感じることがあるかもしれません。「大きな」とは、一般的には 「4kb 程度以上」を指しますが、データベースによっては 32kb くらいまでは 「大きい」と判断されずにすむこともあります。ラージオブジェクトは テキストあるいはバイナリの両方の形式をとり得ます。 PDO でこのラージデータ型を扱うには、 PDOStatement::bindParam() や PDOStatement::bindColumn() のコール時に 型コードとして PDO::PARAM_LOB を使用します。 PDO::PARAM_LOB を指定すると、PDO は データをストリームにマップします。これにより、 PHP ストリーム API を使用してデータを扱えるようになります。
例 12. データベース内の画像を表示する この例では $lob という名前の変数に LOB をバインドし、 fpassthru() を使用してそれをブラウザに送信します LOB はストリームで表されるので、 fgets()、fread() および stream_get_contents() といった関数を 使用することができます。
|
例 13. 画像をデータベースに挿入する この例では、ファイルをオープンしてそのハンドルを PDO に渡し、 LOB としてデータベースに挿入します。PDO は、データベースに応じた もっとも適切な方法でデータを取得します。
|
例 14. 画像をデータベースに挿入する: Oracle Oracle は、ファイルから LOB を挿入する方法が他とは少し違います。 また、必ずトランザクション内で挿入しなければなりません。 それ以外の場合、新しく挿入された LOB は長さゼロとなり、クエリの 実行時に暗黙的にコミットされます。
|
PHP とデータベースサーバの間の接続を表します。
beginTransaction - トランザクションを開始する
commit - トランザクションを コミットする
errorCode - エラーが発生した場合に、データベースからエラーコードを取得する
errorInfo - エラーが発生した場合に、データベースからエラー情報の配列を取得する
exec - SQL ステートメントを 発行し、作用された行の数を返す
getAttribute - データベース接続属性を取得する
lastInsertId - テーブルに挿入された直近の行の値を取得する
prepare - SQL ステートメントを実行するために準備する
rollBack - トランザクションをロールバックする
setAttribute - データベース接続属性を設定する
プリペアドステートメントを表します。ステートメント実行後は関連する結果セットを 表します。
bindColumn - PHP 変数を結果セットの出力カラムにバインドする
bindParam - プリペアドステートメントのパラメータに PHP 変数をバインドする
bindValue - プリペアドステートメントのパラメータに値をバインドする
closeCursor - カーソルを閉じてステートメントを再実行できるようにする
columnCount - 結果セットのカラム数を返す
errorCode - エラーが発生した場合、ステートメントからエラーコードを取得する
errorInfo - エラーが発生した場合、ステートメントからエラー情報の配列を取得する
execute - プリペアドステートメントを実行する
fetch - 結果セットから行を取得する
fetchAll - 結果セットからすべての行を含む配列を返す
fetchColumn 結果セットの単一カラムからデータを返す
getAttribute - PDOStatement 属性を取得する
getColumnMeta - 結果セットのカラムのメタデータを取得する
nextRowset - 次の行セット (結果セット) を取得する
rowCount - SQL ステートメントの実行により作用された行の数を返す
setAttribute - PDOStatement 属性を設定する
setFetchMode - PDOStatement の取得モードを設定する
PDO が発するエラーを表します。あなた自身が書いたコードから PDOException をスローしてはいけません。 PHP の例外についての詳細な情報は、 例外 を参照ください。
例 15. PDOException クラス
|
以下の定数が定義されています。 この関数の拡張モジュールが PHP 組み込みでコンパイルされているか、 実行時に動的にロードされている場合のみ使用可能です。
警告 |
PHP 5.1 以降、PDO はクラス定数を使用します。 それ以前のリリースでは、PDO_PARAM_BOOL の形式のグローバル変数を使用します。 |
ブールデータ型を表します。
SQL NULL データ型を表します。
SQL INTEGER データ型を表します。
SQL CHAR, VARCHAR, または他の文字列データ型を表します。
SQL ラージオブジェクト型を表します。
パラメータがストアドプロシージャ用の入力パラメータであることを指定します。 この値は、PDO::PARAM_* データ型とのビットORとして指定する必要があります。
取得する方法として、 結果セットが返すカラム名と同じ名前の変数を有するオブジェクトとして各行を返す方法を 指定します。 PDO::FETCH_LAZY は、アクセスされたものと同じ名前のオブジェクト変数を作成します。
結果セットの対応するカラム名にふられているものと同じキーを付けた 連想配列として各行を返す取得方法を指定します。 もし結果セットが複数のカラムを同名で含む場合、 PDO::FETCH_ASSOC はカラム名毎に 1 つの値のみ返します。
結果セットの対応するカラム名にふられているものと同じキーを付けた 連想配列として各行を返す取得方法を指定します。 もし結果セットが複数のカラムを同名で含む場合、 PDO::FETCH_NAMED はカラム名毎に値の配列を返します。
結果セットの対応するカラム番号にふられているものと同じ添字を付けた 配列として各行を返す取得方法を指定します。番号は0から始まります。
結果セットと同じカラム名と0から始まるカラム番号を付けた配列として各行を返す 方法を指定します。
結果セットが返すカラム名と同じ名前のプロパティを有する オブジェクトとして各行を返す方法を指定します。
結果セットのカラムの値を PDOStatement::bindParam() または PDOStatement::bindColumn() メソッドでバインドされた PHP変数に代入し、TRUEを返すという取得方法を指定します。
結果セットの次の行から指定された一つのカラムのみを返す取得方法を指定します。
カラムをクラスのプロパティにマップしつつ、 指定されたクラスの新規インスタンスを返す取得方法を指定します。
カラムをクラスのプロパティにマップしつつ、 指定されたクラスの既存のインスタンスを更新する取得方法を指定します。
PHP 5.1.0 以降で使用可能です。
この値が FALSE の場合、PDO は接続がトランザクションを開始できるように オートコミットを無効にしようとします。
独自アプリケーションにおけるメモリ使用量に対する速度のバランスを 調整するためのプリフェッチサイズを設定します。 全てのデータベースとドライバの組み合わせでプリフェッチサイズの設定を サポートしているわけではありません。
データベースとの通信に対するタイムアウト値を秒で設定します。
PDO::CASE_* 定数で指定されたケースにカラム名を変更します。
ドライバ名を返します。
空文字を SQL の NULL 値に変換します。
新規接続を生成するよりもむしろ持続的接続を要求します。
結果セット中の各カラム名にカタログ名を追加します。 カタログ名とカラム名は、小数点 (.) で区切られます。
結果セット中の各カラム名にテーブル名を追加します。 テーブル名とカラム名は、小数点 (.) で区切られます。
PHP 5.1.3 以降で使用可能です。
エラー時にエラーもしくは例外を発生しません。 開発者の方は明示的にエラーをチェックするようにしてください。 これはデフォルトのモードです。
エラーが発生した場合、PHP の E_WARNING メッセージを発行します。
エラーが発生した場合、PDOException を投げます。
カラム名をデータベースドライバにより返されたままにします。
カラム名を小文字にします。
カラム名を大文字にします。
結果セットの次の行を取得します。スクローラブルなカーソルでのみ有効です。
結果セットの前の行を取得します。スクローラブルなカーソルでのみ有効です。
結果セットの先頭の行を取得します。スクローラブルなカーソルでのみ有効です。
結果セットの最後の行を取得します。スクローラブルなカーソルでのみ有効です。
結果セットから行番号で指定した行を取得します。スクローラブルなカーソルでのみ有効です。
結果セットのカーソルの現在の位置を基準とする相対位置により指定された行を 取得します。スクローラブルなカーソルでのみ有効です。
前進のみ可能なカーソルを有するPDOStatementオブジェクトを生成します。 これにより、アプリケーションの性能は改善しますが、 PDOStatementオブジェクトは前方にある結果セットから一度に一行を 取得するという制約を受けます。
スクローラブルカーソルを有するPDOStatementオブジェクトを作成しあす。 結果セットから取得した行を制御するためのPDO::FETCH_ORI_*定数を指定して ください。
SQLSTATE '00000' は SQL ステートメントがエラーや警告がなく発行に成功したことを意味します。 この定数はエラーが発生したかどうかを判別するために PDO::errorCode() もしくは PDOStatement::errorCode() をチェックする際に 便利です。この場合、通常はエラー状態を発生したメソッドからの戻りコードを 検査することによって検知します。
割り当てられたときに発生するイベント。
割り当てが解除されたときに発生するイベント。
プリペアドステートメントの実行前に発生するイベント。
プリペアドステートメントの実行後に発生するイベント。
結果セットから結果を取得する前に発生するイベント。
結果セットから結果を取得した後に発生するイベント。
バインドパラメータの登録時に発生するイベント。 これにより、ドライバがパラメータ名を正規化できるようになります。