當用PHP訪問數據庫時,除了PHP自帶的數據庫驅動,我們一般還有兩種比較好的選擇:PDO和MySQLi。在實際開發過程中要決定選擇哪一種首先要對二者有一個比較全面的了解。本文就針對他們的不同點進行分析,並對多數據庫類型支持、穩定性、性能等等方面進行對比。
|
|
PDO | MySQLi |
| Database support | 12 different drivers | MySQL only |
| API | OOP | OOP + procedural |
| Connection | Easy | Easy |
| Named parameters | Yes | No |
| Object mapping | Yes | Yes |
| Prepared statements (client side) |
Yes | No |
| Performance | Fast | Fast |
| Stored procedures | Yes | Yes |
一、連接
// PDO
$pdo = new PDO("mysql:host=localhost;dbname=database", 'username', 'password');
// mysqli, procedural way
$mysqli = mysqli_connect('localhost','username','password','database');
// mysqli, object oriented way
$mysqli = new mysqli('localhost','username','password','database');
二、API 支持
PDO和MySQLi都是通過面向對象的形式提供API,但是同時MySQLi也提供了面向過程的API,這種形式對於新手來說更容易理解。如果你對原生的php mysql 驅動熟悉,你會發現很輕松得就能使用MySQLi的接口來替換原來的數據訪問。用PDO的好處是,PDO支持多種數據庫,而MySQLi只支持MySQL,一但你掌握了就你可以隨心所欲的使用連接多種數據庫。
三、數據庫的支持

PDO比MySQLi最大的優點就是PDO支持很多種數據庫,而MySQLi只支持MySQLi。要看PDO支持哪些數據庫用下面的代碼:
var_dump(PDO::getAvailableDrivers());
支持多數據庫有什么好處呢?當你的程序以后想從mysql換成sql server或者oracle時,PDO的優勢就能體現出來了,因為換數據庫對於程序接口是透明的,php代碼改動很小,如果你是用MySQLi,那么所有用到數據庫的地方都要重寫,這樣的改動我也只能呵呵了。
四、命名參數支持
PDO命名參數及參數綁定:
$params = array(':username' => 'test', ':email' => $mail, ':last_login' => time() - 3600);
$pdo->prepare('
SELECT * FROM users
WHERE username = :username
AND email = :email
AND last_login > :last_login');
$pdo->execute($params);
而MySQLi的參數綁定:
$query = $mysqli->prepare('
SELECT * FROM users
WHERE username = ?
AND email = ?
AND last_login > ?');
$query->bind_param('sss', 'test', $mail, time() - 3600);
$query->execute();
我們從上面對比就可以看出PDO是通過命名參數進行值的綁定,而MySQLi的參數綁定是通過點位符“?”並嚴格按這個問號的順序來綁定值。這樣雖然代碼顯得沒有PDO那種通過名字對應那么長,但是有一個不好的地方是可讀性和可維護性都降低了,參數個數比較少的時候還不覺得,當參數上了10多個或者更多的情況就比較痛苦了,你必須要按問號的順序來一個一個對應來賦值,萬一其中一個位錯了,后面的都跟着錯了。
不幸的是MySQLi不支持PDO那樣的命名參數綁定。
五、對象映射(Object Mapping)
基於數據庫的開發一般都是從數據庫中讀取數據然后把這些數據用一個對象來承載。PDO和MySQLi都支持對象映射,假設有一個User類,它有一些屬性對應到數據庫。
class User {
public $id;
public $first_name;
public $last_name;
public function info()
{
return '#'.$this->id.': '.$this->first_name.' '.$this->last_name;
}
}
如果沒有對象映射,我們要讀取數據之后,一個一個字段的賦值,這是很繁瑣的。
下面請看二者使用對象的代碼:
$query = "SELECT id, first_name, last_name FROM users";
// PDO
$result = $pdo->query($query);
$result->setFetchMode(PDO::FETCH_CLASS, 'User');
while ($user = $result->fetch()) {
echo $user->info()."\n";
}
// MySQLI, procedural way
if ($result = mysqli_query($mysqli, $query)) {
while ($user = mysqli_fetch_object($result, 'User')) {
echo $user->info()."\n";
}
}
// MySQLi, object oriented way
if ($result = $mysqli->query($query)) {
while ($user = $result->fetch_object('User')) {
echo $user->info()."\n";
}
}
六、安全性

二者都可以防止sql注入。我們先看一個例子。
$_GET['username'] = "'; DELETE FROM users; /*"
當用戶輸入的username參數的值為上面的值("'; DELETE FROM users; /*"),如果你沒有對這個值做任何處理,用戶就成功將delete語句注入,那么user表的記錄就會被全部刪除。
6.1、手動轉義
// PDO, "manual" escaping
$username = PDO::quote($_GET['username']);
$pdo->query("SELECT * FROM users WHERE username = $username");
// mysqli, "manual" escaping
$username = mysqli_real_escape_string($_GET['username']);
$mysqli->query("SELECT * FROM users WHERE username = '$username'");
上面采用了PDO和MySQLi的API自帶的函數對獲取到的參數的值進行了轉義。
6.2、prepared statement參數綁定
下面推薦更加高效安全的prepared statement參數綁定的方式:
// PDO, prepared statement
$pdo->prepare('SELECT * FROM users WHERE username = :username');
$pdo->execute(array(':username' => $_GET['username']));
// mysqli, prepared statements
$query = $mysqli->prepare('SELECT * FROM users WHERE username = ?');
$query->bind_param('s', $_GET['username']);
$query->execute();
七、性能
由於PDO能夠支持其它非MySQL的數據庫,而MySQLi專門針對MySQL設計的,所以MySQLi相對於PDO性能稍微好一些。但是PDO和MySQLi都還是沒有PHP原生的MySQL擴展快。但是這樣性能比較其實意義不太大,因為它們都是相當快了,如果你的程序性能要求不是特別苛刻話,三者都可以滿足你。至於你要選擇哪一種就要你根據的實踐情況進行權衡。
八、總結
PDO支持12種數據庫驅動和命名參數綁定是其最大優點,通過上面的對比,我相信你也知道了你在自己的項目中會使用哪一種連接數據庫了?

