PHPとデータベース - PDO
概要
PDO(PHP Data Objects)拡張モジュールは、PHPからデータベースにアクセスするための軽量で高性能なインターフェイスである。
PDOインターフェイスを実装する各データベースドライバは、正規表現のようなデータベース固有の機能を提供する。
ただし、PDO拡張モジュールは、データベースのあらゆる関数を実行できるわけではない。
データベースサーバにアクセスするには、データベース固有のPDOドライバを使用する必要がある。
PDOは、データアクセスの抽象化レイヤを提供する。
つまり、使用しているデータベースの種類に関わらず、同じ関数を使用してクエリの発行やデータの取得が行うことができる。
PDOは、PHP 5.1以降にバンドルされており、PHP 5.0ではPECL拡張モジュールとして使用可能である。
PHP 5の新機能であるオブジェクト指向で設計されているため、それより前のPHPでは動作しない。
PDOのインストール
Linux
PDOおよびPDO_SQLITEドライバは、PHP 5.1.0以降では標準で有効となっている。
必要に応じて、使用するデータベース用のPDOドライバを有効にすることができる。
データベースごとのPDOドライバについての詳細は、こちらのWebサイトを参照すること。
※注意
PDOを共有モジュールとしてビルドする場合(非推奨)、全てのPDOドライバはPDO自体の後にロードしなければならない。
PDOを共有モジュールとしてインストールする場合、php.iniファイルを編集して、PHPの実行時にPDOが自動的に読み込むように設定する。
データベースごとのドライバについても同様、ドライバはpdo.soの後に記述する。
これは、ドライバを読み込む前にPDOの初期化を済ませておく必要があるからである。
もし、PDOおよびデータベースドライバを静的にビルドした場合、この部分は読み飛ばして構わない。
extension=pdo.so
Windows
- PDOおよび主要なドライバは、共有モジュールとしてPHPに同梱されているため、php.iniファイルを編集するだけで使用できる。
- extension=php_pdo.dll
- ※注意
- PHP 5.3以降では、上記の設定は不要である。これは、PDOのDLLは必須ではなくなったためである。
- 次に、その他のデータベース固有のDLLを読み込む方法は、以下の2つがある。
- 実行時に
dl
関数で読み込む。 - php.iniファイルで、php_pdo.dllに続いて指定する。
例えば、以下のように追記する。
以下のDLLは、php.iniファイルにあるextension_dir
項目で指定したディレクトリに配置しなければならない。- 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
- ※注意
- php.iniファイルの変更後は、設定を有効にするためにPHPを再起動する必要がある。
sudo systemctl restart apache2
- 実行時に
データベース処理のアンチパターン
以下にどれか1つでも当てはまるソースコードは見直す必要がある。
太字にしてあるものは、脆弱性に直結する危険度の高いものである。
mysql_query
等の非推奨関数を使用している。mysql_
で始まる関数は、PHP5.5で非推奨となっている。
SET NAMES
あるいはSET CHARACTER SET
等で文字コードを指定している。
または、データベースで使用する文字コードが未指定である。SET NAMES
等の使用は避けるべきである。- これは、データベース側の文字セットを変更するだけであり、PDOドライバの文字セットは無関係なため、
- 入力側と出力側で文字セットが異なる場合は脆弱性が発生する原因になる。
- 文字コードの性質上、
SET NAMES sjis
やSET CHARACTER SET sjis
と設定する場合、顕著に脆弱性が現れる。 SET NAMES utf8
やSET CHARACTER SET utf8
は基本的には安全だが、例外もあるため注意すること。
MySQLにおいては、libmysqlclientのコンパイルオプションに--with-charset=cp932
や--with-charset=sjis
を指定している場合が該当する。
mysqlndを使用している場合は問題無い。
SELECT * FROM users WHERE id = '$id'
のように変数展開を使用してSQL文を組み立てている。$_POST['id']
等の外部入力の変数が定義されているか確認していない。
外部入力の変数が文字列であるか確認していない。
また、外部入力の変数を出力する時、htmlspecialchars
関数を使用していない。- 以下のようなPHPスクリプトを記述してはならない。
// レコードを挿入する前にhtmlspecialchars関数を使用している間違った例
$stmt = $pdo->prepare('INSERT INTO users(name) VALUES(?)');
$stmt->bindValue(1, htmlspecialchars($name), PDO::PARAM_STR);
- SQLの
LIKE
演算子を使用しているにも関わらず、%
、_
、\
にエスケープ文字を付加していない。 - HTMLの
body
タグの中に、データベースの接続処理を記述している。echo
関数やprint
関数を、そのまま記述している。
Content-Typeがtext/plain
ではなく、exit
関数やdie
関数を使用して強制終了の処理を記述している。- ロジックとテンプレートの分離を実施する。
- これは、ファイルの先頭で全てのロジックを記述して、その後の処理でHTMLタグを出力する。(ファイルも分割すべきである)
- ロジックとテンプレートの分離を実施した後、HTMLタグの中にPHPスクリプトを記述する手法を用いる。
- 以下の例では、ロジックとテンプレートを分離して、データベースにアクセスしてレコードを取得している。
<?php
try
{
$pdo = new PDO('mysql: ... ', 'root', 'password', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,]);
$rows = $pdo->query('SELECT * FROM users')->fetchAll(PDO::FETCH_ASSOC);
}
catch (PDOException $e)
{
exit($e->getMessage());
}
function h($str)
{
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example</title>
</head>
<body>
<ul>
<?php foreach ($rows as $row): ?>
<li><?=h($row)?></li>
<?php endforeach; ?>
</ul>
</body>
</html>
- もし、exit関数やdie関数を使用する場合は、その直前において、以下のように記述する。
- 500は
500 Internal Server Error
の意味で、サーバ側が原因でエラーになったことを表す。
header('Content-Type: text/plain; charset=UTF-8', true, 500);
データベースの接続と管理
PDOクラスのインスタンスを作成することにより、接続が確立される。
コンストラクタには、データソース(DSN)、ユーザ名、パスワードを指定する。
データベースへの接続
データベース接続時になんらかのエラーが発生した場合、PDOException
クラスがスローされる。
エラー処理を行う場合はこの例外をキャッチする。
または、エラーを無視して、set_exception_handler
関数で設定したグローバル例外ハンドラに処理を任せることもできる。
※注意
PDOコンストラクタからの例外をキャッチしない場合、zendエンジンはPHPスクリプトを終了して、バックトレースを表示する。
このバックトレースは、データベースへの接続の詳細(ユーザ名やパスワード等)を誰でも閲覧することができるため、
必ず、例外処理を記述すること。
データベースへの接続に成功する場合、PDOクラスのインスタンスが返る。このインスタンスが存在する間、接続がアクティブであり続ける。
データベースへの接続を閉じる時、インスタンスにnull
を代入して、インスタンスを破棄する。
null
を代入しない場合、PHPスクリプトの終了時に自動的に接続が閉じられる。
※注意
PDOのインスタンスへの参照(PDOStatement
クラスのインスタンスからの参照や別のPDOのインスタンスからの参照等)が残っている場合、
それらも併せて削除する必要がある。(PDOStatement
クラスのインスタンスにnull
を代入する等)
以下に、データベースへの接続を行う時のPDOのインスタンスの生成手順を記載する。
$<PDOクラスのインスタンス名> = new PDO(<データソース名>, <データベースユーザ名>, <パスワード>, <ドライバオプション>);
- データソース名
- データベースに接続するために必要な情報です. (Data Source Name)
- PHP Manualに各データベースに応じたDSNの記述方法が掲載されている。
- 以下の例は、MySQLにおけるデータソース名の記述方法である。
mysql:dbname=<データベース名>;host=<ホスト名またはIPアドレス>;charset=<例: utf8mb4等>
- 先頭にデータベースの種類を指定して、
:
(コロン)で区切る。 - 各項目は、
項目名=値
として、;
(セミコロン)で区切る。
- dbname
- データベース名を指定する。(基本的には必須であるが、後で
USE <データベース名>;
とクエリを実行する場合は省略できる)
- データベース名を指定する。(基本的には必須であるが、後で
- host
- ホスト名またはIPアドレスを指定する。
- (ローカル環境のみで実行する場合、省略しても問題ない場合がある)
- Linuxでは、ホスト名を指定することが推奨される。
- http://qiita.com/mpyw/items/b00b72c5c95aac573b71#comment-e9db50fff9bffa1dd6f8
- Windowsでは、IPアドレスを指定することが推奨される。
- http://www.ah-2.com/2012/01/28/win_localhost_slow.html
- charset
- 文字コードを指定する。
- 文字コードは、ここで指定する。(
SET NAMES
は使用しないこと) - UTF-8を指定する場合、
utf8
であることに注意すること。 - MySQL5.5.3以降を使用する場合、4バイトからなる絵文字等も取り扱える
utf8mb4
を使用することを強く推奨する。
- データベースユーザ名
- データベースのユーザ名を指定する。
- パスワード
- データベースユーザのパスワードを指定する。
- ドライバオプション
- 接続時のオプションを連想配列で渡す。
- キーは予め用意されている定数を使用する。
- 値は予め用意されている定数以外に、論理値や文字列等の一般的な値でもよい。
以下に、よく使用されるドライバオプションとその値を示す。
- PDO::ATTR_ERRMODE
- クエリの実行でエラーが起こった場合、どのように処理をするかを指定する。
- 初期値は、
PDO::ERRMODE_SILENT
である。
- PDO::ERRMODE_EXCEPTION
- 例外をスローする。
- PDO::ERRMODE_WARNING
- クエリの実行Lで発生したエラーをPHPのWarningとして報告する。
PDOStatement::execute
メソッドの戻り値がfalse
かどうかを毎回確認する必要がある。
- PDO::ERRMODE_SILENT
- 何も報告しない。
PDOStatement::execute
メソッドの戻り値がfalse
かどうかを毎回確認する必要がある。
- PDO::ATTR_DEFAULT_FETCH_MODE
PDOStatement::fetch
メソッドやPDOStatement::fetchAll
メソッドで引数が省略された場合、または、- ステートメントが
foreach
文に直接かけられた場合のフェッチスタイルを設定する。 - 初期値は、
PDO::FETCH_BOTH
である。
- PDO::FETCH_BOTH
- カラム番号とカラム名の両方をキーとする連想配列で取得する。
- PDO::FETCH_NUM
- カラム番号をキーとする配列で取得する。
- PDO::FETCH_ASSOC
- カラム名をキーとする連想配列で取得する。この設定が最も使用される。
- PDO::FETCH_OBJ
- カラム名をプロパティとする基本オブジェクトで取得する。
- PDO::ATTR_EMULATE_PREPARES
- データベース側が持つプリペアドステートメント機能のエミュレーションをPDO側で行うかどうかを設定する。
- PHP 5.2以降の初期値は
true
である。 - この設定は、いくつかPDOの挙動に違いが現れる。
- プリペアドステートメントのためにデータベースと通信する必要が無くなるため、エミュレーションを行う方がパフォーマンスは向上する。
- 存在しないテーブル名やカラム名をクエリに持つプリペアドステートメントを発行する場合、
エミュレーションを行わない場合はすぐにエラーが発生するが、エミュレーションを行う場合はクエリを実行するまでエラーが発生するかどうかわからない。 - エミュレーションを行う場合のみ、
;
(セミコロン)区切りで複数のクエリを1つのクエリで実行することができる。
- PDO::ATTR_PERSISTENT(コンストラクタでの指定のみ)
true
の場合、PHPスクリプトが終了してもデータベースへの接続を維持して、次回に再利用する。- 特に、大規模システムでは恩恵が大きい。
- PDO::MYSQL_ATTR_USE_BUFFERED_QUERY(MySQL専用)
true
の場合、バッファクエリを使用する。初期値はMySQLの各バージョンによって異なる。
- バッファクエリ
- 全ての情報をデータベースから取得して、PHPスクリプトにより1件ずつ取得する。
- 非バッファクエリ:
- 1件ごとにデータベースサーバと通信を行って,PHPに取り出させる
- 取得してくる情報がメモリに収まりきらない莫大なデータ量といった特殊なケースを除けば、バッファクエリを選択した方がよい。
- バッファクエリを設定すると、サーバ負荷も軽減されて、途中までフェッチしたところで突然例外が発生するような事態も避けられる。
- 非バッファクエリは、データベースから取得したデータを、コマンドラインからバッチ処理を実行する用途で使用されるが、
- 複数同時にクエリを実行できない等の大きな欠点もある。
- PDO::MYSQL_ATTR_INIT_COMMAND(MySQL専用、コンストラクタでの指定のみ)
- データベースに接続した直後に実行されるクエリをここに記述する。
<?php
try
{
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
foreach($dbh->query('SELECT * from FOO') as $row)
{
print_r($row);
}
$dbh = null;
}
catch(PDOException $e)
{
// エラーが発生した場合、"500 Internal Server Error"を表示して終了する
// もし、エラー画面を表示する場合、HTMLの表示を継続する
// ここではエラー内容を表示しているが、実際の商用環境ではログファイルに記録してWebブラウザには表示しない方が望ましい
header('Content-Type: text/plain; charset=UTF-8', true, 500);
print "エラー!: " . $e->getMessage() . "<br/>";
die();
}
?>
// Webブラウザにこれから表示するものがUTF-8で書かれたHTMLであることを伝える
// (これか <meta charset="utf-8"> の最低限どちらか1つがあればいい. 両方あっても良い.)
header('Content-Type: text/html; charset=utf-8');
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example</title>
</head>
<body>
<!-- ここではHTMLタグ以外を記述しない -->
</body>
</html>
PDOクラスのコンストラクタは、PDO::ERRMODE_EXCEPTION
の有無に関わらずPDOException
をスローするが、
データベースに接続できない場合等は、Warningが発生する。
もし、データベースを動的に指定する場合、set_error_handler
関数を使用してErrorException
に変換した後、例外処理を行う。
データベースの接続を閉じる
データベースの接続は、PHPスクリプトが終了しても閉じられずにキャッシュされ、他のPHPスクリプトが同じ内容の接続を要求する時に再利用される。
このキャッシュにより、新しい接続を確立するオーバーヘッドを避けることができるため、Webアプリケーションを高速化できるようになる。
<?php
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
$sth = $dbh->query('SELECT * FROM foo');
// 使用を終了したので、閉じます
$sth = null;
$dbh = null;
?>
持続的な接続
PDO::ATTR_PERSISTENT
オプションの値は、string型の値が設定されない限り、bool型(持続的な接続の有無を示す)に変換される。
string型の値を設定する場合、複数の接続プールを使用することができる。
これは、互換性の無い異なる接続を使用する場合に便利である。
例えば、異なるPDO::MYSQL_ATTR_USE_BUFFERED_QUERY
の値を設定する場合が挙げられる。
※注意1
持続的な接続をする場合、ドライバのオプションにPDO::ATTR_PERSISTENT
を設定して、PDOクラスのコンストラクタに渡す必要がある。
この属性を、インスタンスの生成後にPDO::setAttribute
メソッドを使用して設定する場合、ドライバは持続的な接続を使用しない。
※注意2
PDO ODBCドライバを使用しており、ODBCライブラリがODBC接続プーリングをサポートしている場合は、
PDOの持続的な接続を使用せずに、ODBCの接続プーリングに接続キャッシュ処理を任せることを推奨する。
ODBCの接続プールは、プロセス内で他のモジュールと共有されている。
PDOが接続をキャッシュすると、その接続はODBCの接続プールに返されなくなり、他のモジュールによって新たな接続が作成されてしまう。
<?php
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(PDO::ATTR_PERSISTENT => true));
?>
クエリの実行
PDO::queryメソッド
ユーザ入力を伴わないクエリは、PDO::queryメソッドを実行する。
戻り値は、PDOStatementクラスである。
$stmt = $pdo->query('SELECT * FROM users');
PDO::execメソッド
ユーザ入力を伴わないクエリにおいて、INSERTやUPDATE等を使用してレコードを直接取得する場合、PDO::execメソッドを使用する。
また、結果を必要としない場合においても、PDO::execメソッドを使用すべきである。
以下の例では、テーブルに存在する全てのレコードを更新して、更新した全てのレコード数を取得している。
$count = $pdo->exec('UPDATE users SET age = age + 1');
PDO::executeメソッド
PDO::prepareメソッド → PDOStatement::bindValueメソッド → PDOStatement::executeメソッドの3ステップでクエリを実行する。
ユーザ入力からクエリを動的に作成する場合、プリペアドステートメントとプレースホルダを使用する。
- プレースホルダ
- ユーザ入力を代入する場所として、予め確保するためのもの。
- プレースホルダには2種類あり、疑問符プレースホルダを使用する方法と名前付きプレースホルダ使用する方法がある。
- これらを混ぜて使用する場合はエラーとなる。
- プリペアドステートメント:
- プレースホルダを使用するために作成する。
- 疑問符プレースホルダ
- ?の要素は、1から始まる。
PDO::PARAM_STR
は省略することができる。- エミュレーションが有効の場合、正しくキャストできない場合があるため、文字列以外を扱う時は明示的なキャストが必要である。
- NULL値に関しては、
PDO::PARAM_NULL
が暗黙的に使用される。
- ?の要素は、1から始まる。
// エミュレーションが有効の場合
$stmt = $pdo->prepare('SELECT * FROM users WHERE gender = ? AND age = ?');
$stmt->bindValue(1, $gender);
$stmt->bindValue(2, (int)$age, PDO::PARAM_INT);
$stmt->execute();
// エミュレーションが無効の場合、上記または下記のいずれの記述も可能
<syntaxhighlight lang="php">
$stmt = $pdo->prepare('SELECT * FROM users WHERE gender = ? AND age = ?');
$stmt->bindValue(1, $gender);
$stmt->bindValue(2, $age, PDO::PARAM_INT);
$stmt->execute();
トランザクションおよび自動コミット
トランザクション内で実行された作業は、データベースに安全に反映されることが保証されている。
トランザクションのコミット時は、他の接続の干渉を受けることはない。
また、未コミット時では、トランザクション内での作業はいつでも取り消すことができる。
PDOクラスを使用してデータベースへ接続する場合、自動コミットモードで動作する。
自動コミットモードとは、データベースがトランザクションをサポートしている場合、クエリが暗黙的にトランザクションのもとで実行される。
データベースがトランザクションをサポートしていない場合、トランザクションを使用せずに実行される。
トランザクションを使用する場合は、PDO::beginTransaction
メソッドを使用して、トランザクションを初期化する必要がある。
使用しているドライバがトランザクションをサポートしていない場合、必ず、PDOException
クラスがスローされる。
トランザクションを終了する場合は、PDO::commit
メソッドあるいはPDO::rollBack
メソッドを使用する。
以下の例では、データベースのテーブルに対して、新しい2つのレコードを追加している。
トランザクションがアクティブな間は、作業中のデータについては、他から一切変更が加えられないことが保証されている。
もし何か問題が発生すれば、catchブロック内でトランザクション開始以降の全ての変更がロールバックされる。
<?php
try
{
$dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2', array(PDO::ATTR_PERSISTENT => true));
echo "接続しました\n";
}
catch (Exception $e)
{
die("接続できません: " . $e->getMessage());
}
try
{
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->beginTransaction();
$dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')");
$dbh->exec("insert into salarychange (id, amount, changedate) values (23, 50000, NOW())");
$dbh->commit();
}
catch(Exception $e)
{
$dbh->rollBack();
echo "失敗しました。" . $e->getMessage();
}
?>
データベースの接続を閉じる場合またはPHPスクリプトが終了する場合、
コミットが完了していないトランザクションがあるならば、自動的にロールバックされる。
これは、明示的にコミットしていない場合、予期せぬ状態で終了したと推測されるため、データの不整合が発生するのを避けるための機能である。
※注意
自動的にロールバックが行われる時は、PDO::beginTransaction
メソッドを実行した場合のみである。
トランザクションを開始するクエリを手動で発行した場合、PDOはそれを知ることができないため、問題が発生してもロールバックできない。
プリペアドステートメントおよびストアドプロシージャ
プリペアドステートメントとは、実行するSQLをコンパイルしたテンプレートのようなものである。
パラメータ変数を使用することで、SQLをカスタマイズすることができる。
プリペアドステートメントには、以下に示す2つの大きな利点がある。
- 一般的に、クエリを実行する時、クエリの解析、コンパイル、実行プランの最適化が行われる。
プリペアドステートメントを使用する場合、これらの解析、コンパイル、最適化の繰り返しを避けることができる。
端的に言うと、プリペアドステートメントは使用するリソースが少ないため、高速に動作するということである。 - プリペアドステートメントに渡すパラメータは、引用符で括る必要は無く、ドライバが自動的に行う。
プリペアドステートメントでは、SQLインジェクションは発生しない。
プリペアドステートメントは、データベースの種類や機能に関わらず同じ仕組みで データベースへのアクセスができる。
Insert (1)
以下の例では、nameおよびvalueを名前付きプレースホルダで置き換えて、INSERT句を実行する。
<?php
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
// 1レコード目の挿入
$name = 'one';
$value = 1;
$stmt->execute();
// パラメータを変更して、2レコード目の挿入
$name = 'two';
$value = 2;
$stmt->execute();
?>
Insert (2)
以下の例では、nameおよびvalueをプレースホルダ?
で置き換えて、INSERT句を実行する。
<?php
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $value);
// 1レコード目の挿入
$name = 'one';
$value = 1;
$stmt->execute();
// パラメータを変更して、2レコード目の挿入
$name = 'two';
$value = 2;
$stmt->execute();
?>
SELECT
以下の例では、フォームで入力したキーの値に応じたデータを取得している。
ユーザの入力内容は自動的に引用符で括られるため、SQLインジェクション攻撃の恐れは無い。
<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
if($stmt->execute(array($_GET['name'])))
{
while($row = $stmt->fetch())
{
print_r($row);
}
}
?>
ストアドプロシージャの呼び出し : 出力パラメータの指定
データベースドライバがサポートしている時、入力パラメータだけでなく、出力パラメータもバインドすることが可能である。
出力パラメータは、ストアドプロシージャから値を取得するために使用する。
この場合、返される値の大きさがどの程度になるのかをバインド時に知る必要がある。
指定した大きさよりも大きな値が返される場合は、エラーが発生する。
<?php
$stmt = $dbh->prepare("CALL sp_returns_string(?)");
$stmt->bindParam(1, $return_value, PDO::PARAM_STR, 4000);
// ストアドプロシージャを呼び出す
$stmt->execute();
print "プロシージャが返した値は $return_value です\n";
?>
ストアドプロシージャの呼び出し : 入出力パラメータの指定
入出力の両方に使用するパラメータを指定することもできる。
入出力パラメータの書式は、出力パラメータと同じである。
以下の例では、ストアドプロシージャに文字列'hello'を渡している。
プロシージャの結果が返ってくると、文字列'hello'はプロシージャの返す値に置き換えられる。
<?php
$stmt = $dbh->prepare("CALL sp_takes_string_returns_string(?)");
$value = 'hello';
$stmt->bindParam(1, $value, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 4000);
// ストアドプロシージャの呼び出し
$stmt->execute();
print "プロシージャが返した値は $value です\n";
?>
プレースホルダの間違った使用例
<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE '%?%'");
$stmt->execute(array($_GET['name']));
// プレースホルダは、値全体に対して使用しなければなりません
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE ?");
$stmt->execute(array("%$_GET[name]%"));
?>