English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
このチュートリアルでは、PHPを使用してMySQLでプレースホルダーステートメントを使用する方法を学びます。
プレースホルダーステートメント(パラメータ化文とも呼ばれます)は、実際のパラメータの値ではなく占位符を含むSQLクエリテンプレートです。実行文を执行する際に、これらの占位符が実際の値で置き換わります。
MySQLiは匿名位置占位符(?)を使用する方法をサポートしています。
INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?);
PDOは匿名位置占位符(?)と名前付き占位符をサポートしています。名前付き占位符は、冒号(:)で始まり、その後で識別子が続きます。以下のように表示されます:
INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email);
プレースホルダーステートメントの実行は、準備と実行の二つの段階で構成されています。
準備 - 準備段階では、SQL文のテンプレートを作成し、データベースサーバーに送信します。サーバーは文テンプレートを解析し、文法チェックとクエリ最適化を実行し、後で使用するために保存します。
実行 - 実行中に、パラメータの値がサーバーに送信されます。サーバーは文テンプレートとこれらの値を使用して、実行するための文を作成します。
プレースホルダーステートメントは非常に有用であり、特定のINSERT文を多次に実行する際に異なる値(例えば、一連の文)を使用する場合に特に有用です。以下では、それを使用する際の主な利点について説明します。
プレースホルダーステートメントは、文が一度のみ解析され、その後何度も実行できるため、効率的に同じ文を繰り返し実行できます。各実行では、データベースサーバーに占位符の値を転送するだけでなく、完全なSQL文を転送する必要がないため、バンド幅の使用を最大限に減少させることができます。
プレースホルダーステートメントは、以下を防ぐ強力な保護を提供します。SQLインジェクションパラメータの値が直接SQLクエリ文字列に埋め込まれていないためです。異なるプロトコルを使用して、パラメータの値とクエリを分けてデータベースサーバーに送信します。これにより、それを干渉しません。文テンプレートを解析した後、サーバーは実行時にこれらの値を使用して直接実行します。これがプレースホルダーステートメントがエラーが少ない理由であり、データベースのセキュリティにおいて最も重要な要素の一つと考えられています。
以下の例では、プレースホルダーステートメントの実際の動作方法を説明します:
<?php /* MySQLサーバーへの接続を試みます。 MySQLが動作していることを前提とします。 デフォルト設定のサーバー(パスワードなしのユーザー「root」) */ $link = mysqli_connect("localhost", "root", "", "demo"); //接続を確認 if($link === false){ die("エラー:接続できません。 " . mysqli_connect_error()); } //プレースメントステートメントを使用します $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)"; if($stmt = mysqli_prepare($link, $sql)){ //変数をプレースホルダとしてプレパレードされたSQL文にバインド mysqli_stmt_bind_param($stmt, "sss", $first_name, $last_name, $email); /* パラメータの値を設定して実行し、この文で別の行を挿入します。 */ $first_name =「Hermione」; $last_name =「Granger」; $email =「[email protected]」; mysqli_stmt_execute($stmt); /* パラメータの値を設定して行を挿入する文を実行します。 */ $first_name =「Ron」; $last_name =「Weasley」; $email =「[email protected]」; mysqli_stmt_execute($stmt); echo "レコードの挿入に成功しました。"; } else{ echo "エラー:クエリを準備できません:$sql." . mysqli_error($link); } //ステートメントを閉じる mysqli_stmt_close($stmt); //接続を閉じる mysqli_close($link); ?>
<?php /* MySQLサーバーへの接続を試みます。 MySQLが動作していることを前提とします。 デフォルト設定のサーバー(パスワードなしのユーザー「root」) */ $mysqli = new mysqli("localhost", "root", "", "demo"); //接続を確認 if($mysqli === false){ die("エラー:接続できません。 " . $mysqli->connect_error); } // プレースメントステートメントを使用します $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)"; if($stmt = $mysqli->prepare($sql)){ // 変数をプレースホルダとしてプレパレードされたSQL文にバインド $stmt->bind_param("sss", $first_name, $last_name, $email); /* パラメータの値を設定して実行します。 この文を再度実行して別の行を挿入します */ $first_name =「Hermione」; $last_name =「Granger」; $email =「[email protected]」; $stmt->execute(); /* パラメータ値を設定して実行 挿入する行のクエリ */ $first_name =「Ron」; $last_name =「Weasley」; $email =「[email protected]」; $stmt->execute(); echo "レコードが成功に挿入されました。"; } else{ echo「エラー:クエリを準備できません:$sql.「」. $mysqli」;->error; } //ステートメントを閉じる $stmt->close(); //接続を閉じる $mysqli->close(); ?>
<?php /* MySQLサーバーへの接続を試みます。 MySQLが動作していることを前提とします。 デフォルト設定のサーバー(パスワードなしのユーザー「root」) */ try{ $pdo = new PDO("mysql:host=localhost;dbname=demo", "root", ""); // PDOのエラーモードを例外に設定します $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(PDOException $e){ die("エラー:接続ができません。 " . $e->getMessage()); } //インサートクエリの実行を試みます try{ //プレースメントステートメントを使用します $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email)"; $stmt = $pdo->prepare($sql); //パラメータをステートメントにバインドします $stmt-bindParam(':first_name', $first_name, PDO::PARAM_STR); $stmt-bindParam(':last_name', $last_name, PDO::PARAM_STR); $stmt-bindParam(':email', $email, PDO::PARAM_STR); /* パラメータ値を設定して実行, この文を再度実行して別の行を挿入します */ $first_name =「Hermione」; $last_name =「Granger」; $email =「[email protected]」; $stmt->execute(); /* パラメータ値を設定して実行 挿入する行のクエリ */ $first_name =「Ron」; $last_name =「Weasley」; $email =「[email protected]」; $stmt->execute(); echo "レコードの挿入に成功しました。"; } catch(PDOException $e){ die("エラー:クエリの準備ができません。",/クエリを実行します: $sql.「」. $e->getMessage()); } // ステートメントを閉じる unset($stmt); //接続を閉じる unset($pdo); ?>
上記の例で見たように、INSERTは一度だけ準備しましたが、異なるパラメータセットを複数回実行しました。
上記のSQL INSERT文の例では、クエリを使用しますfirst_name、last_nameおよびemailフィールド値のプレースホルダー
mysqli_stmt_bind_param()関数は、プレースホルダー(?)に変数をバインドします。プレースホルダー(?)は、実行時に変数に保存されている実際の値に置き換わります。二つ目の引数として提供される種類の文字列「sss」は、各バインド変数のデータタイプをstring(文字列)に指定します。
バインド変数の種類を指定する文字列は、以下の4種類のデータタイプを指定します
i - integer(整型)
d - double(双精度浮点型)
s - string(文字列)
b - BLOB(binary large object:二進大オブジェクト)
バインド変数の種類と文字数はSQL文テンプレートのプレースホルダーの数と一致する必要があります。
前章を覚えている場合は、以下のようにHTMLフォームを作成しましたデータをデータベースに挿入するここでは、プレミス処理文を実行することでこの例を拡張します。以下のインサートスクリプト例をテストするには、フォームの属性のactionに正しいファイル名を使用することを確認してください。
これはデータを挿入するための更新されたPHPコードです。例を詳しく見ると、mysqli_real_escape_string()を使用してユーザー入力をエスケープするようにしていません。プレパレードされたSQL文を使用するため、ユーザー入力が直接クエリ文字列に置き換わることはありませんので、それらを正しくエスケープする必要はありません。
<?php /* MySQLサーバーへの接続を試みます。 MySQLが動作していることを前提とします。 デフォルト設定のサーバー(パスワードなしのユーザー「root」) */ $link = mysqli_connect("localhost", "root", "", "demo"); //接続を確認 if($link === false){ die("エラー:接続できません。 " . mysqli_connect_error()); } //プレースメントステートメントを使用します $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)"; if($stmt = mysqli_prepare($link, $sql)){ //変数をプレパレードされたSQL文にバインド mysqli_stmt_bind_param($stmt, "sss", $first_name, $last_name, $email); //パラメータを設定 $first_name = $_REQUEST['first_name']; $last_name = $_REQUEST['last_name']; $email = $_REQUEST['email']; //プレースメントステートメントの実行を試みます if(mysqli_stmt_execute($stmt)){ echo "レコードの挿入に成功しました。"; } else{ echo "エラー:クエリを実行できません:$sql." . mysqli_error($link); } } else{ echo "エラー:クエリを実行できません:$sql." . mysqli_error($link); } // ステートメントを閉じる mysqli_stmt_close($stmt); //接続を閉じる mysqli_close($link); ?>
<?php /* MySQLサーバーへの接続を試みます。 MySQLが動作していることを前提とします。 デフォルト設定のサーバー(パスワードなしのユーザー「root」) */ $mysqli = new mysqli("localhost", "root", "", "demo"); //接続を確認 if($mysqli === false){ die("エラー:接続できません。 " . $mysqli->connect_error); } //プレースメントステートメントを使用します $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)"; if($stmt = $mysqli->prepare($sql)){ //変数をプレースホルダとしてプレパレードされたSQL文にバインド $stmt->bind_param("sss", $first_name, $last_name, $email); //パラメータを設定 $first_name = $_REQUEST['first_name']; $last_name = $_REQUEST['last_name']; $email = $_REQUEST['email']; //プレースメントステートメントの実行を試みます if($stmt->execute()){ echo "レコードの挿入に成功しました。"; } else{ echo "エラー:クエリの実行に失敗しました: $sql. " . $mysqli->error; } } else{ echo "エラー:クエリの実行に失敗しました: $sql. " . $mysqli->error; } //ステートメントを閉じる $stmt->close(); //接続を閉じる $mysqli->close(); ?>
<?php /* MySQLサーバーへの接続を試みます。 MySQLが動作していることを前提とします。 デフォルト設定のサーバー(パスワードなしのユーザー「root」) */ try{ $pdo = new PDO("mysql:host=localhost;dbname=demo", "root", ""); //PDOのエラーモードを例外に設定します $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(PDOException $e){ die("エラー:接続ができません。 " . $e->getMessage()); } //インサートクエリの実行を試みます try{ //プレースメントステートメントを使用します $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email)"; $stmt = $pdo->prepare($sql); // パラメータをステートメントにバインドします $stmt->bindParam(':first_name', $_REQUEST['first_name'], PDO::PARAM_STR); $stmt->bindParam(':last_name', $_REQUEST['last_name'], PDO::PARAM_STR); $stmt->bindParam(':email', $_REQUEST['email'], PDO::PARAM_STR); // プレースメントステートメントを実行します $stmt->execute(); echo "レコードの挿入に成功しました。"; } catch(PDOException $e){ die("エラー:クエリの準備ができません。",/クエリを実行します $sql. " . $e->getMessage()); } //ステートメントを閉じる unset($stmt); //接続を閉じる unset($pdo); ?>
注意:プレプロセッシングステートメントではユーザー入力のエスケープは必要ありませんが、外部ソースから受け取ったデータのタイプとサイズを常に確認し、システムリソースの悪用を防ぐために適切な制限を実施する必要があります。