ホームページ  >  記事  >  バックエンド開発  >  PDO 対 MySQLi: PHP データベース API の戦い

PDO 対 MySQLi: PHP データベース API の戦い

藏色散人
藏色散人オリジナル
2019-03-22 10:56:303004ブラウズ

PDO 対 MySQLi: PHP データベース API の戦い

はじめに

PHP 5.5 以降、mysql__ 拡張機能を使用する時代は終わりました。メソッドは PHP 7 以降では非推奨となり、削除されました。しかし、インターネットには依然として、初心者がコピー&ペーストして、共有ホスティング プラットフォーム上の古いバージョンの PHP で使用できる古いチュートリアルがたくさんあります。

PHP で MySQL または MariaDB を使用している場合は、MySQLi または PDO を選択できるようになりました。前者は単なる改良版であり、手続き型と OOP をサポートし、プリペアド ステートメントが追加されています。一方、後者は、サポートする 12 個のデータベース ドライバーすべてに対して統合 API を使用できるようにする抽象化レイヤーです。 MySQL は PHP の世界で最も人気のあるデータベースですが。

理論的には、存在するすべてのデータベース タイプに対してベンダー固有の API を用意する必要はありません。これは、API を使用する方がはるかに簡単であるためです。確かにこれには多くの真実がありますが、問題は、PDO_MYSQL には、MySQLi が備えている最新かつ最高の機能がすべて備わっているわけではないということです。正直なところ、なぜこれが当てはまるのか理解できません。これでは、ベンダー固有の API を使用する理由が完全になくなってしまいます。それでも、ほとんどの人はこれらの追加機能を必要としないと思いますが、必要とする人も確かにいます。

PDOの利点

1. 便利な取得方法

2. 変数や値を直接渡して実行できる

3. 変数の型を自動的に検出する機能 (実際には、サーバーに送信されると、すべてが文字列として扱われますが、正しい型に変換されます。これは準備されたステートメントでは 100% 機能しますが、一部のステートメントでは効果がありません)

4. 準備されたステートメントを使用して結果を自動的にバッファリングするオプションを提供します

5. 名前付きパラメーター (ただし、PDO でシミュレーション モードをオフにすることは役に立ちません。同じ名前は 1 回しか使用できません)

MySQLi の利点

1. 非同期クエリ

2. 影響を受ける行に関する情報を取得する機能同じ値を持つ行の更新などの詳細情報を提供します (PDO のコンストラクター設定として実行でき、後で変更することはできません)

3. 正しいデータベース シャットダウン方法

4. 複数のクエリ(PDO でシミュレーション モードがオンになっている場合は問題ありません)

5. 永続的な接続を使用して自動的にクリーンアップします

コードの相違点

PDOと MySQLi は非常に似ていますが、構文がわずかに異なります。 MySQLi は PHP の古いスネークケース規則に従いますが、PDO はキャメルケースを使用します。さらに、MySQLi のメソッドはオブジェクト プロパティとして使用されますが、PDO は関数に従来の構文を使用します。

PDO と MySQLi は両方とも、準備されたステートメントを使用するために 2 つの別個の方法を使用する必要があるため、状況が複雑になります。ただし、PDO を使用すると、専用のバインディング関数を使用する必要がなくなります。

たとえば、ベンダー固有の PostgreSQL API では、これを行うことができます。

参考のために、「準備されていない」クエリを実行して MySQLi と PDO を含む連想配列を取得する方法を示す例を次に示します。

$arr = $mysqli->query("SELECT * FROM myTable")->fetch_all(MYSQLI_ASSOC);
$arr = $pdo->query("SELECT * FROM myTable")->fetchAll(PDO::FETCH_ASSOC);

実際には、ラッパー、クエリ ビルダー、または ORM を使用するのが最善の方法です。 PDO は値を直接バインドして実行できますが、それでも理想的ではありません。私が作成したクラスでは、パラメータのバインディングとして値を渡しながら、すべての呼び出しを連鎖させることができます。

$arr = $mysqli->query("SELECT * FROM myTable WHERE id > ?", [12])->fetchAll('assoc');

連想配列全体が、より簡潔な方法で変数に格納されるようになりました。

新しいデータベース接続の作成

PDO

$dsn = "mysql:host=localhost;dbname=myDatabase;charset=utf8mb4";$options = [
  PDO::ATTR_EMULATE_PREPARES   => false, // turn off emulation mode for "real" prepared statements
  PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, //turn on errors in the form of exceptions
  PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, //make the default fetch be an associative array];try {
  $pdo = new PDO($dsn, "username", "password", $options);} catch (Exception $e) {
  error_log($e->getMessage());
  exit('Something weird happened'); //something a user can understand}

MySQLi

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);try {
  $mysqli = new mysqli("localhost", "username", "password", "databaseName");
  $mysqli->set_charset("utf8mb4");} catch(Exception $e) {
  error_log($e->getMessage());
  exit('Error connecting to database'); //Should be a message a typical user could understand}
挿入、更新、削除

PDO

$stmt = $pdo->prepare("INSERT INTO myTable (name, age) VALUES (?, ?)");
$stmt->execute([$_POST['name'], 29]);
$stmt = null;

MySQLi

$stmt = $mysqli->prepare("UPDATE myTable SET name = ? WHERE id = ?");
$stmt->bind_param("si", $_POST['name'], $_SESSION['id']);
$stmt->execute();
$stmt->close();

prepare() とexecute() は PDO を使用してリンクできることに注意してください。

影響を受ける行の数を取得します

PDO

$stmt->rowCount();

MySQLi

$stmt->affected_rows;
最新の主キーを挿入

どちらのメソッドも $stmt ではなく接続変数を使用することに注意してください。

PDO

$pdo->lastInsertId();

MySQLi

$mysqli->insert_id;
一致する行の取得

PDO

PDO では、唯一のこれを行うには、これを接続オプションとして設定し、rowCount() の動作を変更します。つまり、 rowCount() はデータベース接続全体で一致する行または変更された行を返しますが、両方を返すことはありません。

$options = [
  PDO::MYSQL_ATTR_FOUND_ROWS => true];

MySQLi

$mysqli->info;

これにより、次のような情報の文字列全体が出力されます:

Rows matched: 1 Changed: 0 Warnings: 0

これを行うことができます

preg_match_all('/(\S[^:]+): (\d+)/', $mysqli->info, $matches); 
$infoArr = array_combine ($matches[1], $matches[2]);
var_export($infoArr);
これらの値に簡単にアクセスできるようになりました。値は文字列であるため、すべての値を int にキャストすることも、==== が機能することも、==

を厳密にチェックすることもできることに注意してください。

['Rows matched' => '1', 'Changed' => '0', 'Warnings' => '0']
Fetch

連想配列の取得

PDO

$stmt = $pdo->prepare("SELECT * FROM myTable WHERE id <= ?");
$stmt->execute([5]);
$arr = $stmt->fetchAll(PDO::FETCH_ASSOC);
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt = null;

MySQLi

$stmt = $mysqli->prepare("SELECT id, name, age FROM myTable WHERE name = ?");
$stmt->bind_param("s", $_POST[&#39;name&#39;]);
$stmt->execute();
$arr = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt->close();
単一行の取得

PDO

$stmt = $pdo->prepare("SELECT id, name, age FROM myTable WHERE name = ?");
$stmt->execute([$_POST[&#39;name&#39;]]);
$arr = $stmt->fetch(PDO::FETCH_ASSOC);
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt = null;

MySQLi

$stmt = $mysqli->prepare("SELECT id, name, age FROM myTable WHERE name = ?");
$stmt->bind_param("s", $_POST[&#39;name&#39;]);
$stmt->execute();
$arr = $stmt->get_result()->fetch_assoc();
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt->close();
単一値 (スカラー) の取得

PDO

$stmt = $pdo->prepare("SELECT id, name, age FROM myTable WHERE name = ?");
$stmt->execute([$_POST[&#39;name&#39;]]);
$arr = $stmt->fetch(PDO::FETCH_COLUMN);
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt = null;

MySQLi

$stmt = $mysqli->prepare("SELECT id, name, age FROM myTable WHERE name = ?");
$stmt->bind_param("s", $_POST[&#39;name&#39;]);
$stmt->execute();
$arr = $stmt->get_result()->fetch_row()[0];
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt->close();
オブジェクト配列の取得

PDO

class myClass {}
$stmt = $pdo->prepare("SELECT name, age, weight FROM myTable WHERE name = ?");
$stmt->execute([&#39;Joe&#39;]);
$arr = $stmt->fetchAll(PDO::FETCH_CLASS, &#39;myClass&#39;);
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt = null;

MySQLi###
class myClass {}
$arr = [];
$stmt = $mysqli->prepare("SELECT id, name, age FROM myTable WHERE id = ?");
$stmt->bind_param("s", $_SESSION[&#39;id&#39;]);
$stmt->execute();
$result = $stmt->get_result();
while($row = $result->fetch_object(&#39;myClass&#39;)) {
  $arr[] = $row;
}
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt->close();

正如你所看到的,PDO在这里非常出色。MySQLi没有像$mysqli_result->fetch_all(MYSQLI_OBJ)这样的东西。PDO甚至更进一步,通过使用fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'myClass')对它进行位元化,以处理在类构造函数之后调用它的默认行为。可以在MySQLi中复制这种行为,但是它依赖于省略构造函数,和魔术方法 _set(),或者只在构造函数中设置它(如果它不等于默认值)。

PDO

$search = "%{$_POST[&#39;search&#39;]}%";
$stmt = $pdo->prepare("SELECT id, name, age FROM myTable WHERE name LIKE ?");
$stmt->execute([$search]);
$arr = $stmt->fetchAll();
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt = null;
Copy

MySQLi

$search = "%{$_POST[&#39;search&#39;]}%";
$stmt = $mysqli->prepare("SELECT id, name, age FROM myTable WHERE name LIKE ?"); 
$stmt->bind_param("s", $search);
$stmt->execute();
$arr = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt->close();

获取模式

到目前为止,这是我最喜欢的PDO特性。PDO中的获取模式非常有用,而MySQLi还没有添加它们。

获取键/值对

PDO

$stmt = $pdo->prepare("SELECT event_name, location FROM events WHERE id < ?");
$stmt->execute([25]);
$arr = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt = null;
Copy

MySQLi

$arr = [];
$id = 25;
$stmt = $con->prepare("SELECT event_name, location FROM events WHERE id < ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
while($row = $result->fetch_row()) {
  $arr[$row[0]] = $row[1];
}
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt->close();

输出:

[&#39;Cool Event&#39; => &#39;Seattle&#39;, &#39;Fun Event&#39; => &#39;Dallas&#39;, &#39;Boring Event&#39; => &#39;Chicago&#39;]

获取组列

PDO

$stmt = $pdo->prepare("SELECT hair_color, name FROM myTable WHERE id < ?");
$stmt->execute([10]);
$arr = $stmt->fetchAll(PDO::FETCH_GROUP | PDO::FETCH_COLUMN);
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt = null;
Copy

MySQLi

$arr = [];
$id = 10;
$stmt = $con->prepare("SELECT hair_color, name FROM myTable WHERE id < ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
while($row = $result->fetch_row()) {
  $arr[$row[0]][] = $row[1];
}
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt->close();
Copy

输出:

[
  &#39;blonde&#39; => [&#39;Patrick&#39;, &#39;Olivia&#39;],
  &#39;brunette&#39; => [&#39;Kyle&#39;, &#39;Ricky&#39;],
  &#39;red&#39; => [&#39;Jordan&#39;, &#39;Eric&#39;]
]

获取键/值对数组

PDO

$stmt = $pdo->prepare("SELECT id, max_bench, max_squat FROM myTable WHERE weight < ?");
$stmt->execute([200]);
$arr = $stmt->fetchAll(PDO::FETCH_UNIQUE);
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt = null;
Copy

MySQLi

$arr = [];
$weight = 200;
$stmt = $con->prepare("SELECT id, max_bench, max_squat FROM myTable WHERE weight < ?");
$stmt->bind_param("i", $weight);
$stmt->execute();
$result = $stmt->get_result();
$firstColName = $result->fetch_field_direct(0)->name;
while($row = $stmtResult->fetch_assoc()) {
  $firstColVal = $row[$firstColName];
  unset($row[$firstColName]);
  $arr[$firstColVal] = $row;
}
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt->close();
Copy

输出:

[
  17 => [&#39;max_bench&#39; => 230, &#39;max_squat&#39; => 175],
  84 => [&#39;max_bench&#39; => 195, &#39;max_squat&#39; => 235],
  136 => [&#39;max_bench&#39; => 135, &#39;max_squat&#39; => 285]
]

获取组

PDO

$stmt = $pdo->prepare("SELECT hair_color, name, age FROM myTable WHERE id < ?");
$stmt->execute([12]);
$arr = $stmt->fetchAll(PDO::FETCH_GROUP);
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt = null;
Copy

MySQLi

$arr = [];
$id = 12;
$stmt = $con->prepare("SELECT hair_color, name, age FROM myTable WHERE id < ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
$firstColName = $result->fetch_field_direct(0)->name;
while($row = $stmtResult->fetch_assoc()) {
  $firstColVal = $row[$firstColName];
  unset($row[$firstColName]);
  $arr[$firstColVal][] = $row;
}
if(!$arr) exit(&#39;No rows&#39;);
var_export($arr);
$stmt->close();
Copy

输出:

[
  &#39;blonde&#39; => [
    [&#39;name&#39; => &#39;Patrick&#39;, &#39;age&#39; => 22],
    [&#39;name&#39; => &#39;Olivia&#39;, &#39;age&#39; => 18]
  ],
  &#39;brunette&#39;  => [
    [&#39;name&#39; => &#39;Kyle&#39;, &#39;age&#39;=> 25],
    [&#39;name&#39; => &#39;Ricky&#39;, &#39;age&#39; => 34]
  ],
   &#39;red&#39;  => [
    [&#39;name&#39; => &#39;Jordan&#39;, &#39;age&#39; => 17],
    [&#39;name&#39; => &#39;Eric&#39;, &#39;age&#39; => 52]
  ]
]

在数组中的位置

PDO

$inArr = [1, 3, 5];
$clause = implode(&#39;,&#39;, array_fill(0, count($inArr), &#39;?&#39;)); 
$stmt = $pdo->prepare("SELECT * FROM myTable WHERE id IN ($clause)");
$stmt->execute($inArr);
$resArr = $stmt->fetchAll();
if(!$resArr) exit(&#39;No rows&#39;);
var_export($resArr);
$stmt = null;
Copy

MySQLi

$inArr = [12, 23, 44];
$clause = implode(&#39;,&#39;, array_fill(0, count($inArr), &#39;?&#39;)); /
$types = str_repeat(&#39;i&#39;, count($inArr)); /
$stmt = $mysqli->prepare("SELECT id, name FROM myTable WHERE id IN ($clause)");
$stmt->bind_param($types, ...$inArr);
$stmt->execute();
$resArr = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
if(!$resArr) exit(&#39;No rows&#39;);
var_export($resArr);
$stmt->close();

与其他占位符一起排列的位置

PDO

$inArr = [1, 3, 5];
$clause = implode(&#39;,&#39;, array_fill(0, count($inArr), &#39;?&#39;)); 
$stmt = $pdo->prepare("SELECT * FROM myTable WHERE id IN ($clause) AND id < ?");
$fullArr = array_merge($inArr, [5]); 
$stmt->execute($fullArr);
$resArr = $stmt->fetchAll();
if(!$resArr) exit(&#39;No rows&#39;);
var_export($resArr);
$stmt = null;
Copy

MySQLi

$inArr = [12, 23, 44];
$clause = implode(&#39;,&#39;, array_fill(0, count($inArr), &#39;?&#39;));
$types = str_repeat(&#39;i&#39;, count($inArr));
$types .= &#39;i&#39;; //add 1 more int type
$fullArr = array_merge($inArr, [26]); 
$stmt = $mysqli->prepare("SELECT id, name FROM myTable WHERE id IN ($clause) AND age > ?");
$stmt->bind_param($types, ...$fullArr); 
$stmt->execute();
$resArr = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
if(!$resArr) exit(&#39;No rows&#39;);
var_export($resArr);
$stmt->close();

交易

PDO

try {
  $pdo->beginTransaction();
  $stmt1 = $pdo->prepare("INSERT INTO myTable (name, state) VALUES (?, ?)");
  $stmt2 = $pdo->prepare("UPDATE myTable SET age = ? WHERE id = ?");
  if(!$stmt1->execute([&#39;Rick&#39;, &#39;NY&#39;])) throw new Exception(&#39;Stmt 1 Failed&#39;);
  else if(!$stmt2->execute([27, 139])) throw new Exception(&#39;Stmt 2 Failed&#39;);
  $stmt1 = null;
  $stmt2 = null;
  $pdo->commit();
} catch(Exception $e) {
  $pdo->rollback();
  throw $e;
}

MySQLi

try {
  $mysqli->autocommit(FALSE);
  $stmt1 = $mysqli->prepare("INSERT INTO myTable (name, age) VALUES (?, ?)");
  $stmt2 = $mysqli->prepare("UPDATE myTable SET name = ? WHERE id = ?");
  $stmt1->bind_param("si", $_POST[&#39;name&#39;], $_POST[&#39;age&#39;]);
  $stmt2->bind_param("si", $_POST[&#39;name&#39;], $_SESSION[&#39;id&#39;]);
  $stmt1->execute();
  $stmt2->execute();
  $stmt1->close();
  $stmt2->close();
  $mysqli->autocommit(TRUE);
} catch(Exception $e) {
  $mysqli->rollback(); 
  throw $e;
}

MySQLi有一个问题,但是解决方案是使用全局处理程序将错误转换为异常。

命名为Paramters

$stmt = $pdo->prepare("UPDATE myTable SET name = :name WHERE id = :id");
$stmt->execute([&#39;:name&#39; => &#39;David&#39;, &#39;:id&#39; => 3]);
$stmt = null;

相关推荐:《mysql教程》《PHP教程

以上がPDO 対 MySQLi: PHP データベース API の戦いの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。