PHPでMySQLiの変数の数を可変(動的)にする方法
2015-11-16
PHPからMySQLを使用する際には、MySQLiの使用が本家により推奨されています。
今回はそのMySQLiを使用したときの、プリペアドステートメントの数が動的に変化する場合のbind_paramメソッドの実装方法について解説します。
プリペアドステートメントの数が固定の場合
まず、通常のステートメントの数が固定の場合は、下記のようなコードにより、SQLを実行します。
<?php
// データベースに接続
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'world');
// プリペアドステートメント
$stmt = $mysqli->prepare("INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
// 変数のバインド
$stmt->bind_param('sssd', $code, $language, $official, $percent);
$code = 'DEU';
$language = 'Bavarian';
$official = "F";
$percent = 11.2;
// プリペアドステートメントの実行
$stmt->execute();
// ステートメントの終了
$stmt->close();
// 接続の終了
$mysqli->close();
SQLインジェクションの防止と、ソースコードの可読性向上の観点から、mysqli_stmt::bind_param
メソッドを使用して、SQLに変数を埋め込むのが一般的です。
しかし、mysqli_stmt::bind_param
メソッドを利用すると、そのままでは引数の数を可変にすることができません。
call_user_func_array
関数を利用し、実装することで引数の数を可変にすることが可能です。
プリペアドステートメントの数が可変の場合<?php
$sqlParams = [];
$sqlParams[0] = "";
// WHERE句の組み立て
$sqlWhere = [];
foreach ($whereCond as $value) { //引数などからWHERE句を組み立てる
if (//条件) {
$sqlWhere[] = "name = ?";
$sqlParams[0] .= "s";
$sqlParams[] = $value;
} elseif (//条件) {
$sqlWhere[] = "status = ?";
$sqlParams[0] .= "i";
$sqlParams[] = $value;
} elseif (//条件) {
$sqlWhere[] = "modified = ?";
$sqlParams[0] .= "s";
$sqlParams[] = $value;
}
}
// ORDER BY句の組み立て
$sqlOrderBy = [];
foreach ($orderByCond as $value) {
// 省略。WHERE句と同様に組み立てる
}
// LIMIT句の組み立て
$sqlLimit = [];
foreach ($limitCond as $value) {
// 省略。WHERE句と同様に組み立てる
}
// SQL文の組み立て
$sql = 'SELECT name, status, modified FROM users';
if (count($sqlWhere) > 0) {
$sql .= ' WHERE ' . implode(",", $sqlWhere);
}
if (count($sqlOrderBy) > 0) {
$sql .= ' ORDER BY ' . implode(",", $sqlOrderBy);
}
if (count($sqlLimit) > 0) {
$sql .= ' LIMIT ' . implode(",", $sqlLimit);
}
// bind_paramに渡す引数を参照渡しに変更
$params = [];
foreach ($sqlParams as $key => $value) {
$params[$key] = &$sqlParams[$key];
}
// データベースに接続
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'world');
// プリペアドステートメント
if ($stmt = $mysqli->prepare($sql)) {
// 変数のバインド
call_user_func_array(array($stmt, 'bind_param'), $params);
// プリペアドステートメントの実行
if ($stmt->execute()) {
// 結果の取得
$result = $stmt->get_result();
// 結果を出力
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
// 結果を処理
}
// 結果セットを開放
$result->free();
}
// ステートメントの終了
$stmt->close();
// 接続の終了
$mysqli->close();
このようにすることで、可変のステートメントを安全に実装することができます。
なお、SQL文組み立てのところで変数を文字列に組み込んでしまうと、SQLインジェクションの元になってしまうので、そこだけは注意して実装してください。
まとめ
MySQLたのしい。