PDO のプレースホルダ(名前付き・疑問符)の使い分け
PDO のプリペアドステートメントでは、SQL に値を埋め込む際にプレースホルダを使う。プレースホルダには「名前付き」と「疑問符」の 2 種類があり、それぞれ書き方と向いている場面が異なる。
名前付きプレースホルダ
名前付きプレースホルダはコロン(:)に続けて任意の名前を書く。SQL のどの位置にどの値が入るのかが一目でわかるのが利点だ。
$stmt = $pdo->prepare(
'SELECT * FROM users WHERE name = :name AND age = :age'
);
$stmt->execute( [
':name' => 'Alice',
':age' => 30,
] );execute に渡す連想配列のキーがプレースホルダ名と対応する。キーにコロンを付けても付けなくても動作するが、付けるほうが SQL との対応が明確になる。
疑問符プレースホルダ
疑問符プレースホルダは ? を使い、値は配列のインデックス順に対応する。
$stmt = $pdo->prepare(
'SELECT * FROM users WHERE name = ? AND age = ?'
);
$stmt->execute( [ 'Alice', 30 ] );1 つ目の ? に配列の 0 番目、2 つ目の ? に 1 番目の値がバインドされる。プレースホルダが少なければ簡潔に書けるが、数が増えると順番の管理が面倒になる。
2 つの使い分け
SQL が長い場合や、バインドする値が多い場合に向いている。名前で対応関係がわかるため、可読性が高くバグも起きにくい。
バインドする値が 1〜2 個程度で、SQL が短い場合に向いている。記述量が少なく済むが、値の順番を間違えやすい。
実務では名前付きプレースホルダを使うケースが多い。特にチーム開発では可読性の高さが重視されるためだ。
bindParam / bindValue との組み合わせ
execute に配列を渡す方法のほかに、bindParam や bindValue で 1 つずつバインドする方法もある。名前付き・疑問符のどちらでも使える。
名前付きプレースホルダの場合はプレースホルダ名を指定する。
$stmt = $pdo->prepare(
'INSERT INTO users ( name, age ) VALUES ( :name, :age )'
);
$stmt->bindValue( ':name', 'Bob', PDO::PARAM_STR );
$stmt->bindValue( ':age', 25, PDO::PARAM_INT );
$stmt->execute();疑問符プレースホルダの場合は 1 始まりのインデックスを指定する。
$stmt = $pdo->prepare(
'INSERT INTO users ( name, age ) VALUES ( ?, ? )'
);
$stmt->bindValue( 1, 'Bob', PDO::PARAM_STR );
$stmt->bindValue( 2, 25, PDO::PARAM_INT );
$stmt->execute();bindValue / bindParam を使うと PDO::PARAM_INT や PDO::PARAM_STR で型を明示できる。execute に配列を渡す方法ではすべて文字列として扱われるため、型を厳密に管理したい場合は bindValue や bindParam を使うほうが安全だ。
混在は禁止
1 つの SQL 文の中で名前付きと疑問符を混ぜることはできない。
// これはエラーになる
$stmt = $pdo->prepare(
'SELECT * FROM users WHERE name = :name AND age = ?'
);どちらか一方に統一する必要がある。プロジェクト全体でもスタイルを統一しておくと、コードの一貫性が保たれる。
IN 句での注意点
プレースホルダは 1 つにつき 1 つの値しかバインドできない。IN 句で複数の値を渡したい場合は、値の数だけプレースホルダを生成する必要がある。
$ids = [ 1, 2, 3 ];
$placeholders = implode( ',', array_fill( 0, count( $ids ), '?' ) );
$stmt = $pdo->prepare( "SELECT * FROM users WHERE id IN ( $placeholders )" );
$stmt->execute( $ids );名前付きプレースホルダでも同様の工夫が必要になる。この手の処理が頻出するなら、ヘルパー関数を用意しておくとコードがすっきりする。