當用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 wayif ($result = mysqli_query($mysqli, $query)) { while ($user = mysqli_fetch_object($result, 'User')) { echo $user->info()."\n"; }}// MySQLi, object oriented wayif ($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種資料庫驅動和具名引數綁定是其最大優點,通過上面的對比,我相信你也知道了你在自己的項目中會使用哪一種串連資料庫了?