PHP
PHP MySQLi
サーチ…
前書き
mysqli
インターフェースは、 mysql
インターフェースの改良版(「MySQL改良拡張版」を意味します)です。これはバージョン5.5では廃止され、バージョン7.0では削除されました。 mysqliの拡張機能は、MySQLの改良された拡張機能であり、MySQLシステムバージョン4.1.3以降の新機能を利用するために開発されました。 mysqli拡張機能はPHPバージョン5以降に含まれています。
備考
特徴
mysqliインターフェイスにはいくつかの利点があり、mysql拡張機能よりも重要な機能拡張があります:
- オブジェクト指向インタフェース
- プリペアドステートメントのサポート
- 複数のステートメントのサポート
- トランザクションのサポート
- 強化されたデバッグ機能
- 組み込みサーバーのサポート
古いプロシージャスタイルと新しいオブジェクト指向プログラミング(OOP)スタイルの2つのインターフェイスを備えています。廃止予定のmysql
は手続き型インタフェースしかないため、オブジェクト指向のスタイルが好まれることがよくあります。しかし、新しいスタイルはOOPの力のために有利です。
代替案
データベースにアクセスするためのmysqli
インタフェースの代わりに、より新しいPHP Data Objects(PDO)インタフェースがあります。これは、OOPスタイルのプログラミングだけであり、MySQLタイプのデータベース以外にもアクセスできます。
MySQLi connect
オブジェクト指向スタイル
サーバーに接続する
$conn = new mysqli("localhost","my_user","my_password");
デフォルトのデータベースを設定します。 $conn->select_db("my_db");
データベースに接続する
$conn = new mysqli("localhost","my_user","my_password","my_db");
手続き型スタイル
サーバーに接続する
$conn = mysqli_connect("localhost","my_user","my_password");
デフォルトのデータベースを設定します: mysqli_select_db($conn, "my_db");
データベースに接続する
$conn = mysqli_connect("localhost","my_user","my_password","my_db");
データベース接続の確認
オブジェクト指向スタイル
if ($conn->connect_errno > 0) {
trigger_error($db->connect_error);
} // else: successfully connected
手続き型スタイル
if (!$conn) {
trigger_error(mysqli_connect_error());
} // else: successfully connected
MySQLiクエリー
query
関数は有効なSQL文字列を受け取り、データベース接続$conn
に対して直接実行します
オブジェクト指向スタイル
$result = $conn->query("SELECT * FROM `people`");
手続き型スタイル
$result = mysqli_query($conn, "SELECT * FROM `people`");
注意
一般的な問題は、単にクエリを実行し、それが動作することを期待する(つまり、 mysqli_stmtオブジェクトを返す)ということです。この関数は文字列だけを取るので、まず自分でクエリを作成します。 SQLに何か間違いがあると、MySQLコンパイラは失敗しfalse
。 この時点で、この関数はfalse
を返しfalse
。
$result = $conn->query('SELECT * FROM non_existent_table'); // This query will fail
$row = $result->fetch_assoc();
$result
はfalse
であり、オブジェクトではないため、上記のコードはE_FATAL
エラーを生成します。
PHP致命的なエラー:非オブジェクト上のメンバ関数fetch_assoc()を呼び出します。
手続き上のエラーは類似していますが、致命的ではありません。なぜなら、我々は単に関数の期待に反しているからです。
$row = mysqli_fetch_assoc($result); // same query as previous
PHPから次のメッセージが表示されます
mysqli_fetch_array()は、パラメータ1がmysqli_result、booleanが指定されているとみなします。
あなたは最初にテストをすることでこれを避けることができます
if($result) $row = mysqli_fetch_assoc($result);
ループスルーMySQLiの結果
PHPは、結果からデータを取得し、 while
ステートメントを使用してループすることを容易にします。次の行を取得できなかった場合はfalse
返し、ループが終了します。これらの例は
- mysqli_fetch_assoc - 列名をキーとして持つ連想配列
- mysqli_fetch_object - 列名を変数として持つ
stdClass
オブジェクト - mysqli_fetch_array - 連想配列と数値配列(どちらか一方を取得するために引数を使うことができる)
- mysqli_fetch_row - 数値配列
オブジェクト指向スタイル
while($row = $result->fetch_assoc()) {
var_dump($row);
}
手続き型スタイル
while($row = mysqli_fetch_assoc($result)) {
var_dump($row);
}
結果から正確な情報を得るために、以下を使用できます。
while ($row = $result->fetch_assoc()) {
echo 'Name and surname: '.$row['name'].' '.$row['surname'].'<br>';
echo 'Age: '.$row['age'].'<br>'; // Prints info from 'age' column
}
接続を閉じる
データベースのクエリが終了したら、リソースを解放するために接続を閉じることをお勧めします。
オブジェクト指向スタイル
$conn->close();
手続き型スタイル
mysqli_close($conn);
注意 :サーバーへの接続は、スクリプトの実行が終了すると直ちに閉じられます。ただし、クローズ接続機能を明示的に呼び出すことで以前は閉じられている場合を除きます。
使用例:結果をフェッチした後に実行するスクリプトの処理量が多く、完全な結果セットを取得した場合は、必ず接続を終了する必要があります。もしそうでなければ、Webサーバーが大量に使用されているときにMySQLサーバーが接続制限に達する可能性があります。
MySQLiでの準備文
SQLインジェクション攻撃からSQLステートメントを保護するために準備されたステートメントがなぜ役立つのかの完全な説明は、「 パラメーター化されたクエリによるSQLインジェクションの防止 」を参照してください。
$conn
変数はMySQLiオブジェクトです。詳細は、 MySQLi connectの例を参照してください。
どちらの例でも、 $sql
は
$sql = "SELECT column_1
FROM table
WHERE column_2 = ?
AND column_3 > ?";
?
後で提供する値を表します。タイプに関係なく、プレースホルダの引用符は必要ありません。クエリーのデータ部分には、 SET
、 VALUES
、 WHERE
という意味のプレースホルダーのみを提供することもできます。 SELECT
またはFROM
部分でプレースホルダを使用することはできません。
オブジェクト指向スタイル
if ($stmt = $conn->prepare($sql)) {
$stmt->bind_param("si", $column_2_value, $column_3_value);
$stmt->execute();
$stmt->bind_result($column_1);
$stmt->fetch();
//Now use variable $column_1 one as if it were any other PHP variable
$stmt->close();
}
手続き型スタイル
if ($stmt = mysqli_prepare($conn, $sql)) {
mysqli_stmt_bind_param($stmt, "si", $column_2_value, $column_3_value);
mysqli_stmt_execute($stmt);
// Fetch data here
mysqli_stmt_close($stmt);
}
最初のパラメータ$stmt->bind_param
かの2番目のパラメータmysqli_stmt_bind_param
SQLクエリ内の対応するパラメータのデータ型によって決定されます。
パラメータ | バインドされたパラメータのデータ型 |
---|---|
i | 整数 |
d | ダブル |
s | 文字列 |
b | ブロブ |
パラメータのリストは、クエリで指定された順序にする必要があります。この例では、 si
は最初のパラメータ( column_2 = ?
)が文字列であり、2番目のパラメータ( column_3 > ?
)が整数であることを意味します。
データを取得する方法については、準備された文からデータを取得する方法を参照してください。
エスケープ文字列
エスケープ文字列は、クエリに挿入するためにデータを保護する古い( そして安全性の低い )方法です。これは、 MySQLの関数mysql_real_escape_string()を使って処理し、データを消毒します(つまり、PHPはエスケープしていません)。 MySQLi APIはこの関数への直接アクセスを提供します
$escaped = $conn->real_escape_string($_GET['var']);
// OR
$escaped = mysqli_real_escape_string($conn, $_GET['var']);
この時点では、MySQLがダイレクトクエリで安全に使用できると考える文字列があります
$sql = 'SELECT * FROM users WHERE username = "' . $escaped . '"';
$result = $conn->query($sql);
それでなぜこれは準備された声明ほど安全ではないのですか?安全だと思われる文字列を生成するためにMySQLを騙す方法があります。次の例を考えてみましょう
$id = mysqli_real_escape_string("1 OR 1=1");
$sql = 'SELECT * FROM table WHERE id = ' . $id;
1 OR 1=1
はMySQLがエスケープするデータを表していませんが、まだSQLインジェクションを表しています。安全でないデータを返す場所を表す他の例もあります。問題は、MySQLのエスケープ機能がデータをSQL構文に準拠させるように設計されていることです。 MySQLがSQL命令のユーザデータを混乱させないようにするためのものではありません。
MySQLi Insert ID
AUTO_INCREMENT列を持つ表のINSERT
問合せによって生成された最後のIDを戻します。
オブジェクト指向スタイル
$id = $conn->insert_id;
手続き型スタイル
$id = mysqli_insert_id($conn);
接続に前のクエリがなかった場合、またはクエリがAUTO_INCREMENT値を更新しなかった場合はゼロを返します。
行を更新するときにIDを挿入する
AUTO_INCREMENT
idは、新しい行が保存(または挿入)されたときにのみ返されるため、通常、 UPDATE
文は挿入IDを返しません。新しいIDを更新する1つの方法は、更新のためにINSERT ... ON DUPLICATE KEY UPDATE
構文を使用することです。
以下の例のセットアップ:
CREATE TABLE iodku (
id INT AUTO_INCREMENT NOT NULL,
name VARCHAR(99) NOT NULL,
misc INT NOT NULL,
PRIMARY KEY(id),
UNIQUE(name)
) ENGINE=InnoDB;
INSERT INTO iodku (name, misc)
VALUES
('Leslie', 123),
('Sally', 456);
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
+----+--------+------+
| id | name | misc |
+----+--------+------+
| 1 | Leslie | 123 |
| 2 | Sally | 456 |
+----+--------+------+
IODKUが「更新」をLAST_INSERT_ID()
、関連するid
取得するLAST_INSERT_ID()
$sql = "INSERT INTO iodku (name, misc)
VALUES
('Sally', 3333) -- should update
ON DUPLICATE KEY UPDATE -- `name` will trigger "duplicate key"
id = LAST_INSERT_ID(id),
misc = VALUES(misc)";
$conn->query($sql);
$id = $conn->insert_id; -- picking up existing value (2)
IODKUが「挿入」を実行し、 LAST_INSERT_ID()
が新しいid
取得する場合:
$sql = "INSERT INTO iodku (name, misc)
VALUES
('Dana', 789) -- Should insert
ON DUPLICATE KEY UPDATE
id = LAST_INSERT_ID(id),
misc = VALUES(misc);
$conn->query($sql);
$id = $conn->insert_id; -- picking up new value (3)
結果のテーブルの内容:
SELECT * FROM iodku;
+----+--------+------+
| id | name | misc |
+----+--------+------+
| 1 | Leslie | 123 |
| 2 | Sally | 3333 | -- IODKU changed this
| 3 | Dana | 789 | -- IODKU added this
+----+--------+------+
MySQLiでのSQLのデバッグ
したがって、あなたのクエリは失敗しました( MySQLiが$conn
をどのように作成したかを参照してください)
$result = $conn->query('SELECT * FROM non_existent_table'); // This query will fail
何が起こったのかをどうやって見つけますか? $result
はfalse
なので、助けにはならない。ありがたいことに、connect $conn
は、MySQLが私たちに失敗のことを教えてくれたことを教えてくれます
trigger_error($conn->error);
手続き型
trigger_error(mysqli_error($conn));
あなたは次のようなエラーが発生するはずです
テーブル 'my_db.non_existent_table'は存在しません
準備されたステートメントからデータを取得する方法
準備文
クエリの準備と実行の方法については、MySQLiのPrepared statementsを参照してください。
結果のバインディング
オブジェクト指向スタイル
$stmt->bind_result($forename);
手続き型スタイル
mysqli_stmt_bind_result($stmt, $forename);
bind_result
を使用するbind_result
の問題は、使用される列を指定するためにステートメントが必要であることです。つまり、上記のようにクエリを実行するには、 SELECT forename FROM users
ようになっている必要がありSELECT forename FROM users
。より多くの列を含めるには、単にそれらをパラメータとしてbind_result
関数に追加します(SQLクエリに追加することを確認してください)。
どちらの場合も、 forename
カラムを$forename
変数に代入しています。これらの関数は、割り当てたい列数だけ引数をとります。関数は参照によって束縛されるため、代入は1回だけ行われます。
次のようにループすることができます:
オブジェクト指向スタイル
while ($stmt->fetch())
echo "$forename<br />";
手続き型スタイル
while (mysqli_stmt_fetch($stmt))
echo "$forename<br />";
これの欠点は、一度に多くの変数を割り当てる必要があることです。これにより、大規模なクエリを追跡することが困難になります。 MySQLネイティブドライバ( mysqlnd
)がインストールされている場合は、 get_resultを使用するだけです。
オブジェクト指向スタイル
$result = $stmt->get_result();
手続き型スタイル
$result = mysqli_stmt_get_result($stmt);
mysqli_resultオブジェクトを取得しているので、作業がはるかに簡単です。これは、 mysqli_queryが返すオブジェクトと同じです。つまり、 通常の結果ループを使用してデータを取得できます。
mysqlnd
場合はどうすればいいですか?
そうであれば@Sophivorusはあなたにこのすばらしい答えを取り上げました。
この関数は、サーバーにインストールされていないget_result
タスクを実行できます。結果をループして連想配列を作成するだけです
function get_result(\mysqli_stmt $statement)
{
$result = array();
$statement->store_result();
for ($i = 0; $i < $statement->num_rows; $i++)
{
$metadata = $statement->result_metadata();
$params = array();
while ($field = $metadata->fetch_field())
{
$params[] = &$result[$i][$field->name];
}
call_user_func_array(array($statement, 'bind_result'), $params);
$statement->fetch();
}
return $result;
}
mysqli_fetch_assoc()
を使用しているかのように、関数を使用してこのような結果を得ることができます。
<?php
$query = $mysqli->prepare("SELECT * FROM users WHERE forename LIKE ?");
$condition = "J%";
$query->bind_param("s", $condition);
$query->execute();
$result = get_result($query);
while ($row = array_shift($result)) {
echo $row["id"] . ' - ' . $row["forename"] . ' ' . $row["surname"] . '<br>';
}
mysqlnd
ドライバを使用していた場合と同じ出力が得られますが、 mysqlnd
する必要はありません。これは、システムに上記のドライバをインストールできない場合に非常に便利です。このソリューションを実装するだけです。