PDO的使用

具體參考《PHP核心技術與最佳實踐》的5.1章 《什麼是PDO》php

1. PDO的介紹

PHP針對每種數據庫都有一個獨立的模塊、一組獨立的函數。這樣的結構和設計讓PHP兼容多種數據庫變得困難。一旦要將一個應用移到另一種數據庫環境中,或者是須要添加新的數據庫支持,就不得不從新編寫和數據庫相關的操做。一般編寫多個類,用適配器模式來實現。在這個歷史背景下PDO出現了。PDO(PHP Data Objects)提供了一個通用接口訪問多種數據庫,即抽象的數據模型支持鏈接多種數據庫。有了PDO使代碼變得更簡潔、更安全。mysql

在PHP中,鏈接MySQL數據庫的一般有3種方式:sql

  • MySQL系列函數:最經常使用,是過程式風格的一組應用(不建議,在PHP7.0已廢除)
  • MySQLi系列函數:是MySQL函數的加強改進版,提供了過程化和麪向對象兩種風格的API,增長了預編譯和參數綁定等新的特性
  • PDO:從語法上講,PDO更接近MySQLi

具體的能夠參考:【鏈接數據庫】PHP7的鏈接數據庫的三種方法【原創】數據庫

相比MySQLi,PDO的優點在於支持多種數據庫,而MySQLi只能支持MySQL,因此通常更推薦使用PDO來對數據庫進行操做。
PDO提供了一個數據訪問抽象層,這就意味着無論使用哪一種數據庫,均可以用一樣一組API對數據進行操做,保證了可抽象性和訪問接口的一致性。安全

開啓PDO很容易,通常來講安裝好PHP默認都會開啓PDO,若是沒有則去php.ini中找到如下語句,把前面的分號去掉便可dom

;extension=php_pdo.dll

2. PDO的使用

使用PDO的第一步是配置數據源,以後的用法和MySQL擴展操做數據庫的方法沒有什麼區別了,
PDO的操做主要有PDO::query()、PDO::exec()、PDO::prepare()函數

  • PDO::query():主要是用於有記錄結果返回的操做,特別是SELECT操做
  • PDO::exec():主要是針對沒有結果集合返回的操做,好比INSERT、UPDATE、DELETE等操做,它返回的結果是當前操做影響的列數
  • PDO::prepare():主要是預處理操做,須要經過$rs->execute()來執行預處理裏面的SQL語句,這個方法能夠綁定參數,功能比較強大

如下是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類型的,在某些特殊應用下,可能須要轉換格式優化

3. PDO的參數綁定和預編譯

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均支持)

4. PDO事務處理

一個事務中全部的工做在提交時,即便是分階段執行,也要保證安全的應用於數據庫,不被其餘的鏈接干擾,事務工做能夠在請求發生錯誤時自動取消。
事務的主要特性:原子性、一致性、獨立性、持久性(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或者其餘的支持事務的引擎。

相關文章
相關標籤/搜索