Maison >développement back-end >tutoriel php >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('No rows'); var_export($arr); $stmt = null;
MySQLi
$stmt = $mysqli->prepare("SELECT id, name, age FROM myTable WHERE name = ?"); $stmt->bind_param("s", $_POST['name']); $stmt->execute(); $arr = $stmt->get_result()->fetch_all(MYSQLI_ASSOC); if(!$arr) exit('No rows'); var_export($arr); $stmt->close();
Obtenir une seule ligne
PDO
$stmt = $pdo->prepare("SELECT id, name, age FROM myTable WHERE name = ?"); $stmt->execute([$_POST['name']]); $arr = $stmt->fetch(PDO::FETCH_ASSOC); if(!$arr) exit('No rows'); var_export($arr); $stmt = null;
MySQLi
$stmt = $mysqli->prepare("SELECT id, name, age FROM myTable WHERE name = ?"); $stmt->bind_param("s", $_POST['name']); $stmt->execute(); $arr = $stmt->get_result()->fetch_assoc(); if(!$arr) exit('No rows'); 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['name']]); $arr = $stmt->fetch(PDO::FETCH_COLUMN); if(!$arr) exit('No rows'); var_export($arr); $stmt = null;
MySQLi
$stmt = $mysqli->prepare("SELECT id, name, age FROM myTable WHERE name = ?"); $stmt->bind_param("s", $_POST['name']); $stmt->execute(); $arr = $stmt->get_result()->fetch_row()[0]; if(!$arr) exit('No rows'); 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(['Joe']); $arr = $stmt->fetchAll(PDO::FETCH_CLASS, 'myClass'); if(!$arr) exit('No rows'); 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['id']); $stmt->execute(); $result = $stmt->get_result(); while($row = $result->fetch_object('myClass')) { $arr[] = $row; } if(!$arr) exit('No rows'); 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['search']}%"; $stmt = $pdo->prepare("SELECT id, name, age FROM myTable WHERE name LIKE ?"); $stmt->execute([$search]); $arr = $stmt->fetchAll(); if(!$arr) exit('No rows'); var_export($arr); $stmt = null; Copy
MySQLi
$search = "%{$_POST['search']}%"; $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('No rows'); 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('No rows'); 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('No rows'); var_export($arr); $stmt->close();
输出:
['Cool Event' => 'Seattle', 'Fun Event' => 'Dallas', 'Boring Event' => 'Chicago']
获取组列
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('No rows'); 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('No rows'); var_export($arr); $stmt->close(); Copy
输出:
[ 'blonde' => ['Patrick', 'Olivia'], 'brunette' => ['Kyle', 'Ricky'], 'red' => ['Jordan', 'Eric'] ]
获取键/值对数组
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('No rows'); 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('No rows'); var_export($arr); $stmt->close(); Copy
输出:
[ 17 => ['max_bench' => 230, 'max_squat' => 175], 84 => ['max_bench' => 195, 'max_squat' => 235], 136 => ['max_bench' => 135, 'max_squat' => 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('No rows'); 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('No rows'); var_export($arr); $stmt->close(); Copy
输出:
[ 'blonde' => [ ['name' => 'Patrick', 'age' => 22], ['name' => 'Olivia', 'age' => 18] ], 'brunette' => [ ['name' => 'Kyle', 'age'=> 25], ['name' => 'Ricky', 'age' => 34] ], 'red' => [ ['name' => 'Jordan', 'age' => 17], ['name' => 'Eric', 'age' => 52] ] ]
在数组中的位置
PDO
$inArr = [1, 3, 5]; $clause = implode(',', array_fill(0, count($inArr), '?')); $stmt = $pdo->prepare("SELECT * FROM myTable WHERE id IN ($clause)"); $stmt->execute($inArr); $resArr = $stmt->fetchAll(); if(!$resArr) exit('No rows'); var_export($resArr); $stmt = null; Copy
MySQLi
$inArr = [12, 23, 44]; $clause = implode(',', array_fill(0, count($inArr), '?')); / $types = str_repeat('i', 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('No rows'); var_export($resArr); $stmt->close();
与其他占位符一起排列的位置
PDO
$inArr = [1, 3, 5]; $clause = implode(',', array_fill(0, count($inArr), '?')); $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('No rows'); var_export($resArr); $stmt = null; Copy
MySQLi
$inArr = [12, 23, 44]; $clause = implode(',', array_fill(0, count($inArr), '?')); $types = str_repeat('i', count($inArr)); $types .= 'i'; //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('No rows'); 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(['Rick', 'NY'])) throw new Exception('Stmt 1 Failed'); else if(!$stmt2->execute([27, 139])) throw new Exception('Stmt 2 Failed'); $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['name'], $_POST['age']); $stmt2->bind_param("si", $_POST['name'], $_SESSION['id']); $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([':name' => 'David', ':id' => 3]); $stmt = null;
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!