Maison  >  Article  >  développement back-end  >  PDO vs MySQLi : la bataille des API de base de données PHP

PDO vs MySQLi : la bataille des API de base de données PHP

藏色散人
藏色散人original
2019-03-22 10:56:303004parcourir

PDO vs MySQLi : la bataille des API de base de données PHP

Introduction

L'époque de l'utilisation de l'extension mysql_ est révolue, depuis PHP 5.5 Ses méthodes ont été obsolètes et supprimées à partir de PHP 7. Mais Internet regorge encore d’anciens tutoriels que les débutants peuvent simplement copier/coller et utiliser avec les anciennes versions de PHP sur les plateformes d’hébergement mutualisé.

Si vous utilisez MySQL ou MariaDB en PHP, vous avez désormais la possibilité de MySQLi ou PDO. La première n'est qu'une version améliorée, prend en charge la procédure et la POO et ajoute des instructions préparées, tandis que la seconde est une couche d'abstraction qui vous permet d'utiliser une API unifiée pour les 12 pilotes de base de données qu'elle prend en charge. Bien que MySQL soit la base de données la plus populaire dans le monde PHP.

En théorie, nous n'avons pas besoin d'avoir une API spécifique au fournisseur pour chaque type de base de données existant, car il est beaucoup plus simple d'en utiliser une seule. Bien qu'il y ait certainement beaucoup de vérité là-dedans, le problème est que PDO_MYSQL ne possède pas toutes les fonctionnalités les plus récentes et les plus performantes de MySQLi. Honnêtement, je ne comprends pas pourquoi c'est le cas, car cela éliminerait complètement toute raison d'utiliser une API spécifique au fournisseur. Pourtant, j’imagine que la plupart des gens n’ont pas besoin de ces fonctionnalités supplémentaires, mais certains en ont certainement besoin.

Avantages du PDO

1. Méthodes d'acquisition utiles

2. Permet de transmettre directement des variables et des valeurs à exécuter

3. Possibilité de détecter automatiquement les types de variables (ce qui se passe réellement, c'est que lorsqu'ils sont envoyés au serveur, tout est traité comme une chaîne, mais converti au type correct. Cela fonctionne à 100 % dans les instructions préparées, mais dans certaines n'a aucun effet dans les cas extrêmes, comme en mode simulation)

4. Fournit une option pour mettre automatiquement en mémoire tampon les résultats à l'aide d'instructions préparées

5. Paramètres nommés (bien que désactiver le mode simulation dans PDO soit inutile car vous ne peut utiliser le même nom qu'une seule fois)

Avantages MySQL

1 Requête asynchrone

2. informations, telles que la mise à jour des lignes avec la même valeur (peut être définie dans PDO en tant que constructeur et ne peut pas être modifiée ultérieurement)

3. Méthode d'arrêt de la base de données correcte

4. ok si le mode simulation est activé dans PDO)

5. Utilisez des connexions persistantes pour effacer automatiquement

Les différences de code

PDO et MySQLi sont très similaires, mais ont une syntaxe légèrement différente. MySQLi suit l'ancienne convention PHP Snake_case, tandis que PDO utilise CamelCase. De plus, les méthodes de MySQLi sont utilisées comme propriétés d'objet, tandis que PDO utilise la syntaxe traditionnelle pour les fonctions.

PDO et MySQLi compliquent les choses en vous obligeant à utiliser deux méthodes distinctes pour utiliser les instructions préparées. Cependant, PDO élimine le besoin d’utiliser des fonctions de liaison dédiées.

Par exemple, dans l'API PostgreSQL spécifique au fournisseur, vous pouvez le faire.

Pour référence, voici un exemple de comment effectuer une requête "non préparée" pour obtenir un tableau associatif contenant MySQLi et PDO.

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

En fait, le meilleur moyen est d'utiliser un wrapper, un générateur de requêtes ou un ORM. Bien que PDO puisse lier des valeurs directement lors de l'exécution, ce n'est toujours pas idéal. Dans la classe que j'ai créée, vous pouvez enchaîner tous les appels tout en transmettant des valeurs en tant que liaisons de paramètres de paramètres.

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

L'intégralité du tableau associatif est désormais stocké dans une variable de manière plus concise.

Créer une nouvelle connexion à la base de données

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}

Insérer, mettre à jour, delete

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();

Notez que préparer() et exécuter() peuvent être liés à l'aide de PDO.

Obtenir le nombre de lignes concernées

PDO

$stmt->rowCount();

MySQLi

$stmt->affected_rows;

Insérer la dernière Clé primaire

Notez que les deux méthodes utilisent la variable de connexion, pas $stmt.

PDO

$pdo->lastInsertId();

MySQLi

$mysqli->insert_id;

Obtenir les lignes correspondantes

PDO

dans In PDO, le seul moyen d'y parvenir est de le définir comme option de connexion, en modifiant le comportement de rowCount(). Cela signifie que rowCount() renverra les lignes correspondantes ou les lignes modifiées pour l'ensemble de la connexion à la base de données, mais pas les deux.

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

MySQLi

$mysqli->info;

Cela affichera les informations de la chaîne entière, telles que :

Rows matched: 1 Changed: 0 Warnings: 0

Vous pouvez le faire

preg_match_all('/(\S[^:]+): (\d+)/', $mysqli->info, $matches); 
$infoArr = array_combine ($matches[1], $matches[2]);
var_export($infoArr);

Ces valeurs sont désormais facilement accessibles. Notez que la valeur est une chaîne, vous pouvez donc convertir toutes les valeurs en int, === fonctionnera, ou vous pouvez vérifier strictement ==.

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

Récupérer

Obtenir un tableau associatif

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();

Obtenir une seule ligne

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();

Obtenir une seule valeur (scalaire)

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();

Obtenir un tableau d'objets

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教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn