## 使用?[PDO](http://php.net/manual/zh/book.pdo.php)?及其預處理語句功能。
在 PHP 中,有很多方式來連接到一個 MySQL 數據庫。PDO(PHP 數據對象)是其中最新且最健壯的一種。 PDO 跨多種不同類型數據庫有一個一致的接口,使用面向對象的方式,支持更多的新數據庫支持的特性。
你應該使用 PDO 的預處理語句函數來幫助防范 SQL 注入攻擊。 使用函數?[bindValue](http://php.net/manual/zh/pdostatement.bindvalue.php)?來確保你的 SQL 免于一級 SQL 注入攻擊。 (雖然并不是 100% 安全的,查看進一步閱讀獲取更多細節。) 在以前,這必須使用一些「魔術引號(magic quotes)」函數的組合來實現。PDO 使得那堆東西不再需要。
## 示例
~~~
<?php
try{
// 新建一個數據庫連接
// You'll probably want to replace hostname with localhost in the first parameter.
// The PDO options we pass do the following:
// \PDO::ATTR_ERRMODE enables exceptions for errors. This is optional but can be handy.
// \PDO::ATTR_PERSISTENT disables persistent connections, which can cause concurrency issues in certain cases. See "Gotchas".
// \PDO::MYSQL_ATTR_INIT_COMMAND alerts the connection that we'll be passing UTF-8 data.
// This may not be required depending on your configuration, but it'll save you headaches down the road
// if you're trying to store Unicode strings in your database. See "Gotchas".
$link = new \PDO( 'mysql:host=your-hostname;dbname=your-db',
'your-username',
'your-password',
array(
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_PERSISTENT => false,
\PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8mb4'
)
);
$handle = $link->prepare('select Username from Users where UserId = ? or Username = ? limit ?');
// PHP bug: if you don't specify PDO::PARAM_INT, PDO may enclose the argument in quotes.
// This can mess up some MySQL queries that don't expect integers to be quoted.
// See: https://bugs.php.net/bug.php?id=44639
// If you're not sure whether the value you're passing is an integer, use the is_int() function.
$handle->bindValue(1, 100, PDO::PARAM_INT);
$handle->bindValue(2, 'Bilbo Baggins');
$handle->bindValue(3, 5, PDO::PARAM_INT);
$handle->execute();
// Using the fetchAll() method might be too resource-heavy if you're selecting a truly massive amount of rows.
// If that's the case, you can use the fetch() method and loop through each result row one by one.
// You can also return arrays and other things instead of objects. See the PDO documentation for details.
$result = $handle->fetchAll(\PDO::FETCH_OBJ);
foreach($result as $row){
print($row->Username);
}
}
catch(\PDOException $ex){
print($ex->getMessage());
}
?>
~~~
## 陷阱
* 當綁定整型變量時,如果不傳遞 PDO::PARAM_INT 參數有事可能會導致 PDO 對數據加引號。 這會搞壞特定的 MySQL 查詢。查看[該 bug 報告](https://bugs.php.net/bug.php?id=44639)。
* 未使用 `set names utf8mb4` 作為首個查詢,可能會導致 Unicode 數據錯誤地存儲進數據庫,這依賴于你的配置。 如果你絕對有把握你的 Unicode 編碼數據不會出問題,那你可以不管這個。
* 啟用持久連接可能會導致怪異的并發相關的問題。 這不是一個 PHP 的問題,而是一個應用層面的問題。只要你仔細考慮了后果,持久連接一般會是安全的。 查看?[Stack Overfilow 這個問題](http://stackoverflow.com/questions/3332074/what-are-the-disadvantages-of-using-persistent-connection-in-pdo)。
* 即使你使用了 `set names utf8mb4`,你也得確認實際的數據庫表使用的是 utf8mb4 字符集!
* 可以在單個 execute() 調用中執行多條 SQL 語句。 只需使用分號分隔語句,但注意[這個 bug](https://bugs.php.net/bug.php?id=61207),在該文檔所針對的 PHP 版本中還沒修復。
* [Laruence:PDOStatement::bindParam 的一個陷阱](http://www.laruence.com/2012/10/16/2831.html)
## 進一步閱讀
* [PHP 手冊:PDO](http://php.net/manual/zh/book.pdo.php)
* [為什么你應該使用 PHP 的 PDO 訪問數據庫](http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/)?([中文](http://www.fushanlang.com/blog/php-pdo-to-access-the-database-1032/))
* [Stack Overflow: PHP PDO vs 普通的 mysql_connect](http://stackoverflow.com/questions/1402017/php-pdo-vs-normal-mysql-connect)
* [Stack Overflow: PDO 預處理語句足以防范 SQL 注入嗎?](http://stackoverflow.com/questions/134099/are-pdo-prepared-statements-sufficient-to-prevent-sql-injection)
* [Stack Overflow: 在 MySQL 中使用 SET NAMES utf8?](http://stackoverflow.com/questions/2159434/set-names-utf8-in-mysql)