具體參考《PHP核心技術與最佳實踐》的5.1章 《什麼是PDO》php
PHP針對每種數據庫都有一個獨立的模塊、一組獨立的函數。這樣的結構和設計讓PHP兼容多種數據庫變得困難。一旦要將一個應用移到另一種數據庫環境中,或者是須要添加新的數據庫支持,就不得不從新編寫和數據庫相關的操做。一般編寫多個類,用適配器模式來實現。在這個歷史背景下PDO出現了。PDO(PHP Data Objects)提供了一個通用接口訪問多種數據庫,即抽象的數據模型支持鏈接多種數據庫。有了PDO使代碼變得更簡潔、更安全。mysql
在PHP中,鏈接MySQL數據庫的一般有3種方式:sql
具體的能夠參考:【鏈接數據庫】PHP7的鏈接數據庫的三種方法【原創】數據庫
相比MySQLi,PDO的優點在於支持多種數據庫,而MySQLi只能支持MySQL,因此通常更推薦使用PDO來對數據庫進行操做。
PDO提供了一個數據訪問抽象層,這就意味着無論使用哪一種數據庫,均可以用一樣一組API對數據進行操做,保證了可抽象性和訪問接口的一致性。安全
開啓PDO很容易,通常來講安裝好PHP默認都會開啓PDO,若是沒有則去php.ini中找到如下語句,把前面的分號去掉便可dom
;extension=php_pdo.dll
使用PDO的第一步是配置數據源,以後的用法和MySQL擴展操做數據庫的方法沒有什麼區別了,
PDO的操做主要有PDO::query()、PDO::exec()、PDO::prepare()函數
如下是PDO的示例:fetch
<?php /** * PDO 的使用實例 */ try { // 配置PDO的數據源 $dsn = 'mysql: host=localhost; dbname=php_book'; // 構造方法 $db = new PDO($dsn, 'root', '123456'); // 設置異常可捕獲 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->exec("SET NAMES 'UTF8'"); // 插入到日誌中 $sql = "INSERT INTO users(name, email, password, created_at) values ('pdo_test', '8888@qq.com', 'bbb', now())"; $db->exec($sql); // 使用預處理語句 $insert = $db->prepare("INSERT INTO users(name, email, password, created_at) values (?, ?, ?, now())"); $insert->execute(array('pdo_test1', '8448657@qq.com', 'aaa')); // 異常 $insert->execute(array('pdo_test2', '8448657@qq.com', 'aaa', 9, 10)); $sql = "select name, email, password, created_at from users"; $query = $db->prepare($sql); $query->execute(); var_dump($query->fetchAll(PDO::FETCH_ASSOC)); } catch (PDOException $e) { echo $e->getMessage(); }
注意:使用PDO從MySQL數據庫查詢出來的數據都是string類型的,在某些特殊應用下,可能須要轉換格式優化
PDO最大的特色就是引入參數綁定和預編譯。ui
下面是從數據庫中查詢某條記錄:
<?php $pdo = new PDO('mysql: host=localhost; dbname=php_book'); $pdo->query("SELECT name FROM users WHERE id = " . $_GET['id']);
這是一段糟糕的代碼。插入一個原始的請求參數到 SQL 請求中。這將讓被黑客輕鬆地利用[SQL 注入]方式進行攻擊。想一下若是黑客將一個構造的 id 參數經過像 http://domain.com/?id=1%3BDEL... 這樣的 URL 傳入。這將會使 $_GET['id'] 變量的值被設爲 1;DELETE FROM users 而後被執行從而刪除全部的 user 記錄!所以,你應該使用 PDO 限制參數來過濾 ID 輸入。
上面的代碼可優化爲:
<?php $pdo = new PDO('mysql: host=localhost;dbname=php_book'); $stmt = $pdo->prepare('SELECT name FROM users WHERE id = :id'); $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); $stmt->bindParam(':id', $id, PDO::PARAM_INT); $stmt->execute();
在MySQL應用中,爲了防止注入攻擊,一般在PHP中使用intval、addslashes等函數對傳入的參數進行轉義,轉變爲SQL中合法的參數類型,這種方法較複雜,而使用PDO的bindParam方法會變得很快捷,只須要在函數中指定第三個參數,便可對傳入的參數進行轉換,轉換爲須要的類型拼接到原生的SQL語句中
好比:
<?php /** * PDO 的最大特色是引入參數綁定和預編譯 * 參數綁定:經過綁定變量來執行準備好的語句 * 兩種綁定參數的方式 */ $calories = 150; $colour = 'red'; // 配置PDO的數據源 $dsn = 'mysql: host=localhost; dbname=php_book'; // 構造方法 $db = new PDO($dsn, 'root', '123456'); // 執行預處理語句(第一種綁定變量的方式) $sth = $db->prepare('SELECT name, colour, calories FROM fruit WHERE calories > :calories AND colour = :colour'); // 綁定變量,將變量轉化爲int類型 $sth->bindParam(':calories', $calories, PDO::PARAM_INT); // 綁定變量,將變量轉化爲string類型 $sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12); // 執行 $sth->execute(); var_dump($sth->fetchAll(PDO::FETCH_ASSOC)); // 執行預處理語句(第二種綁定變量的方式) $sth = $db->prepare('SELECT name, colour, calories FROM fruit WHERE calories > ? AND colour = ?'); // 綁定變量,將變量轉化爲int類型 $sth->bindParam(1, $calories, PDO::PARAM_INT); // 綁定變量,將變量轉化爲string類型 $sth->bindParam(2, $colour, PDO::PARAM_STR, 12); // 執行 $sth->execute(); var_dump($sth->fetchAll(PDO::FETCH_ASSOC));
預編譯負責兩件事,轉義和軟解析提速。程序要支持預編譯,除了要數據庫支持外,還須要驅動支持(PDO和MySQLi均支持)
一個事務中全部的工做在提交時,即便是分階段執行,也要保證安全的應用於數據庫,不被其餘的鏈接干擾,事務工做能夠在請求發生錯誤時自動取消。
事務的主要特性:原子性、一致性、獨立性、持久性(Atomicity,Consistency,Isolation,Durability,ACID)。典型運用就是經過把批量的改變保存,而後當即執行,這樣就能提升效率,一旦事務不成功,將會回滾到初始狀態,保證數據的一致性。
SQL一般工做在自動提交模式下,這意味着執行的每一個查詢都有本身隱含的事務處理,不管是數據庫支持事務仍是因數據庫不支持而不存在事務,DML語句執行的結果都將當即生效而不可更改。好比在MySQL中執行一條update語句,其功能將會當即生效而且是永久性不可更改性的。而在Oracle數據庫中,默認是事務模式,要delete一條數據,數據並不會被永久性刪除,只有執行了commit命令後纔會生效。
PDO中使用beginTransaction()方法來建立事務。在一個事務中,使用commit()或者是rollback()方法來結束事務,具體應用哪一種方法這取決於事務中代碼運行是否成功。腳本結束或者一個鏈接要關閉時,若是還有一個未處理完的事務,PDO自動將其回滾。這對於腳本意外終止狀況來講是一個安全方案,若是沒有明確提交事務,它將假設發生一些錯誤,爲數據的安全執行回滾。
自動回滾僅發生於經過beginTransaction()創建的事務。若是用手動方式執行一個開始事務的查詢,PDO沒法知道他的狀況故沒法回滾。
代碼以下:
<?php try { $conn = new PDO('mysql: host=localhost; dbname=php_book', 'root', '123456'); // 開啓事務 $conn->beginTransaction(); for($i = 0; $i < 1000000; $i++) { $conn->exec("insert into `users` values(null, 'username')"); } // 提交事務 $conn->commit(); } catch(PDOException $ex) { // 執行回滾 $conn->rollBack(); }
注意:由於使用了事務,要麼成功要麼失敗,若是發現第一條執行了,可是第二條沒有執行或者是失敗了,則應該檢查一下表類型是否爲MyISAM,MyISAM引擎是不支持事務的,須要改用InnoDB或者其餘的支持事務的引擎。