Home > Article > Backend Development > PDO vs. MySQLi: The Battle of PHP Database APIs
Introduction
The days of using mysql_ extensions are over, since PHP 5.5 Its methods have been deprecated and removed starting with PHP 7. But the internet is still full of old tutorials that beginners can just copy/paste and use with older versions of PHP on shared hosting platforms.
If you are using MySQL or MariaDB in PHP, you now have the choice of MySQLi or PDO. The former is just an improved version, supports procedural and OOP, and adds prepared statements, while the latter is an abstraction layer that allows you to use a unified API for all 12 database drivers it supports. Although MySQL is the most popular database in the PHP world.
Theoretically, we don't need to have a vendor-specific API for every database type that exists, because it's much simpler to just use one. While there's certainly a lot of truth to this, the problem is that PDO_MYSQL doesn't have all the latest and greatest features that MySQLi does. I honestly don't understand why this is the case, as this would completely eliminate any reason to use a vendor-specific API. Still, I imagine most people don't need these extra features, but there are certainly some who do.
Advantages of PDO
1. Useful acquisition methods
2. Allow variables and values to be passed directly to execute
3. Ability to automatically detect variable types (what actually happens is that when sent to the server, everything is treated as a string, but converted to the correct type. This works 100% in prepared statements, but in some Has no effect in edge cases, such as in simulation mode.)
4. Provide an option to automatically buffer results using prepared statements
5. Named parameters (although turning simulation mode off in PDO is Useless because you can only use the same name once)
MySQLi Advantages
1. Asynchronous query
2. Get information about the affected rows The ability to provide more information, such as updating rows with the same value (can be done as a constructor setting in PDO and cannot be changed later)
3. Correct database shutdown method
4. Multiple queries at once (ok if simulation mode is turned on in PDO)
5. Use persistent connections to automatically clean up
Differences in code
PDO and MySQLi are very similar, but have slightly different syntax. MySQLi follows the old PHP snake_case convention, while PDO uses camelCase. Additionally, MySQLi's methods are used as object properties, while PDO uses traditional syntax for functions.
Both PDO and MySQLi complicate things by requiring you to use two separate methods to use prepared statements. However, PDO eliminates the need to use dedicated binding functions.
For example, in the vendor-specific PostgreSQL API, you can do this.
For reference, here is an example demonstrating how to perform a "non-prepared" query to obtain an associative array containing MySQLi and PDO.
$arr = $mysqli->query("SELECT * FROM myTable")->fetch_all(MYSQLI_ASSOC);
$arr = $pdo->query("SELECT * FROM myTable")->fetchAll(PDO::FETCH_ASSOC);
Actually, the best way is to use a wrapper, query builder or ORM. While PDO can bind values directly into execute, it's still not ideal. In the class I created, you can chain all calls while passing in values as parameter parameter bindings.
$arr = $mysqli->query("SELECT * FROM myTable WHERE id > ?", [12])->fetchAll('assoc');
The entire associative array is now stored in a variable in a more concise way.
Create a new database connection
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}
Insert, update, 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();
Note that prepare() and execute() can be linked using PDO.
Get the number of affected rows
PDO
$stmt->rowCount();
MySQLi
$stmt->affected_rows;
Insert the latest primary key
Note that both methods use the connection variable, not $stmt.
PDO
$pdo->lastInsertId();
MySQLi
$mysqli->insert_id;
Get matching rows
PDO
In PDO, implement The only way to do this is to set it as a connection option, which changes the behavior of rowCount(). This means that rowCount() will return matching rows or changed rows for the entire database connection, but not both.
$options = [ PDO::MYSQL_ATTR_FOUND_ROWS => true];
MySQLi
$mysqli->info;
This will output the entire string of information like:
Rows matched: 1 Changed: 0 Warnings: 0
You can do this
preg_match_all('/(\S[^:]+): (\d+)/', $mysqli->info, $matches); $infoArr = array_combine ($matches[1], $matches[2]); var_export($infoArr);
Now you can easily access these value. Note that the value is a string, so you can cast all values to int, ==== will work, or you can strictly check ==.
['Rows matched' => '1', 'Changed' => '0', 'Warnings' => '0']
Fetch
Get associative array
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();
Get a single row
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();
Get a single value (scalar)
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();
Get object array
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;
The above is the detailed content of PDO vs. MySQLi: The Battle of PHP Database APIs. For more information, please follow other related articles on the PHP Chinese website!