PHP PDO訪問數據庫

PHP PDO訪問數據庫

BY FUSHANLANG, ON SEPTEMBER 26TH, 2010
原文地址:HTTP://WWW.FUSHANLANG.COM/BLOG/PHP-PDO-TO-ACCESS-THE-DATABASE-1032/

爲何你應該使用PHP PDO訪問數據庫

許多PHP程序員學習過如歌使用MySQL或MySQL擴展來訪問數據庫. 不過,自PHP 5.1版本以來,一個更好的解決方案出現了. PHP Data Objects (PDO) 提供了讓你更有{productive}的準備{statements},處理對象的方法.php

PDO 簡介

「PDO – PHP Data Objects – is a database access layer providing a uniform method of access to multiple databases.」mysql

它不受數據庫特定語法限制, 但它可讓切換數據庫和平臺的過程更無痛,更簡潔的切換數據庫鏈接字符串.程序員

PDO - db abstraction layer

這個教程並是一個徹底的SQL入門. 它主要是爲了幫助那些已經在使用MySQL或MySQLi擴展的人們過渡到更增強大、兼容性更好的PDO.sql

數據庫支持數據庫

這個擴展能支持任何爲PDO設計了驅動的數據庫. 在寫這篇文章的時候,如下數據庫已經被支持:數組

  • PDO_DBLIB ( FreeTDS / Microsoft SQL Server / Sybase )
  • PDO_FIREBIRD ( Firebird/Interbase 6 )
  • PDO_IBM ( IBM DB2 )
  • PDO_INFORMIX ( IBM Informix Dynamic Server )
  • PDO_MYSQL ( MySQL 3.x/4.x/5.x )
  • PDO_OCI ( Oracle Call Interface )
  • PDO_ODBC ( ODBC v3 (IBM DB2, unixODBC and win32 ODBC) )
  • PDO_PGSQL ( PostgreSQL )
  • PDO_SQLITE ( SQLite 3 and SQLite 2 )
  • PDO_4D ( 4D )

全部這些驅動都沒有被您的系統預裝,這裏有一種快速的方式來找到您須要的驅動:安全

print_r(PDO::getAvailableDrivers());服務器

鏈接

不一樣的數據庫可能在鏈接方法上有那麼一點點的不一樣. 下面,咱們將介紹幾種常見的數據庫的鏈接方法. 你將會注意到前三種看起來差很少, 不過像SQLite之類的語言就有他本身獨特的語法.函數

Connection String

try {
# MS SQL Server and Sybase with PDO_DBLIB
$DBH = new PDO(「mssql:host=$host;dbname=$dbname, $user, $pass」);
$DBH = new PDO(「sybase:host=$host;dbname=$dbname, $user, $pass」);
# MySQL with PDO_MYSQL
$DBH = new PDO(「mysql:host=$host;dbname=$dbname」, $user, $pass);
# SQLite Database
$DBH = new PDO(「sqlite:my/database/path/database.db」);
}
catch(PDOException $e) {
echo $e->getMessage();
}

請注意try/catch代碼塊 – 您應該始終將您 PDO 的操做封裝在一個 try/catch 代碼塊內並使用異常機制 .一般你只會使用單個鏈接 – 下面將爲您介紹它的語法.oop

下文中出現的 $DBH 意思是 ‘database handle’.

你能夠經過把handle設置爲null來關閉任何數據庫鏈接.

# close the connection

$DBH = null;

你能夠從 PHP.net 獲取更多關於特色數據庫的選項和鏈接字串(connection strings)的信息

異常處理

PDO 可使用異常(Exceptions)來處理錯誤,這意味你須要把{處理PDO的}包括在一個try/catch代碼塊. 你也能夠經過設置錯誤模式(error mode attribute)強制PDO在您最近建立的數據庫鏈接上使用這三種錯誤模式中的一種. 如下提供了語法:

$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );

$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );

$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

不過無論您設置什麼錯誤模式, 錯誤的鏈接總會產生一個異常, 因此您應該在建立一個數據庫時包含一個try/catch代碼塊.

PDO::ERRMODE_SILENT

這是默認的錯誤模式. 若是您使用了這種錯誤模式, 你將像您用mysql或mysqli擴展的時候那樣本身檢查錯誤. 這裏還有兩種更理想的符合[[DRY programming]]思想的方法.

PDO::ERRMODE_WARNING

這種模式將會發出(issue)一個標準的PHP warning,而後繼續執行程序. 這種方法在調試時會頗有用.

PDO::ERRMODE_EXCEPTION

這也許是人們在大多數狀況下但願使用的模式. 它拋出(fire)一個異常, 容許你優雅的處理錯誤而且隱藏那些可能會致使安全風險的數據. 這裏是一個處理異常的實例:

# connect to the database
try {
$DBH = new PDO(「mysql:host=$host;dbname=$dbname」, $user, $pass);
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
# UH-OH! Typed DELECT instead of SELECT!
$DBH->prepare(‘DELECT name FROM people’);
}
catch(PDOException $e) {
echo 「I’m sorry, Dave. I’m afraid I can’t do that.」;
file_put_contents(‘PDOErrors.txt’, $e->getMessage(), FILE_APPEND);
}

上面是一個在select statement中的內部錯誤; 這將會引起一個異常. 這段異常處理代碼將會把錯誤詳情發送到一個日誌文件中, 而後顯示一個友好的(固然,友不友好隨你便)消息給用戶.

插入、更新數據

插入新數據、或更新一個已經存在的數據,是最多見的數據庫操做之一. PDO提供了一種[[normally a two-step process]]. 本節中介紹的全部內容一樣適用於 UPDATE 和 INSERT 操做.

2 to 3 step insert and update

下面是一個最基本的插入的例子:

# STH means 「Statement Handle」

$STH = $DBH->prepare(「INSERT INTO folks ( first_name ) values ( ‘Cathy’ )」);

$STH->execute();

您也能夠直接使用exec()方法完成相同的操做(PS:使用exec()方法能夠減小一個調用). 在大多數狀況下,您可能會屢次調用這個方法, 因此呢, 您能夠享受到prepared statements帶來的好處. 甚至若是您只想調用它一次, 使用prepared statements will 也會幫您擋住 SQL injection 攻擊.

Prepared Statements

使用 prepared statements 將幫助您防止 SQL injection 的危險.

Prepared statement的語句是隻須要發送數據到服務器的預編譯 SQL 語句, 它具備自動處理數據以避免受 SQL injection 攻擊的優勢。

您能夠經過在您的SQL語句中包含佔位符來使用 prepared statement . 這裏有三個例子: 一個沒有佔位符的, 一個有未命名佔位符(Unnamed Placeholders)的, 和一個有命名佔位符(Named Placeholders)的.

# no placeholders – ripe for SQL Injection!
$STH = $DBH->(「INSERT INTO folks (name, addr, city) values ($name, $addr, $city)」);
# unnamed placeholders
$STH = $DBH->(「INSERT INTO folks (name, addr, city) values (?, ?, ?);
# named placeholders
$STH = $DBH->(「INSERT INTO folks (name, addr, city) value (:name, :addr, :city)」);

您可能想避免使用第一種方法; 下面爲您提供了它們直接的比較. 選擇未命名佔位符或命名佔位符將會影響您如何爲這些語句設置數據.

未命名佔位符(Unnamed Placeholders)

# assign variables to each place holder, indexed 1-3
$STH->bindParam(1, $name);
$STH->bindParam(2, $addr);
$STH->bindParam(3, $city);
# insert one row
$name = 「Daniel」
$addr = 「1 Wicked Way」;
$city = 「Arlington Heights」;
$STH->execute();
# insert another row with different values
$name = 「Steve」
$addr = 「5 Circle Drive」;
$city = 「Schaumburg」;
$STH->execute();

只需兩步!首先,咱們爲不一樣的佔位符(Placeholder)綁定變量 (lines 2-4). 而後,咱們爲那些佔位符(Placeholder)賦值而後執行查詢. 要想發送另外的一組數據,只須要改變那些變量的值,而後再執行便可.

(譯註: 原文在第一步與第二布均使用了assign來描述過程)

這個使用不少參數的方法彷佛有點麻煩?若是您的數據存儲在數組中,有一個簡單的方法:

# the data we want to insert
$data = array(‘Cathy’, ’9 Dark and Twisty Road’, ‘Cardiff’);
$STH = $DBH->(「INSERT INTO folks (name, addr, city) values (?, ?, ?);
$STH->execute($data);

這很簡單,不是嗎?

在數組中的數據等同於佔位符。 $data[0]對應第一個佔位符,$data[1]第二個,依此類推,但若是您的數組索引並未排序,這將沒法正常工做,您將須要從新索引這個數組.

命名佔位符(Named Placeholders)

您可能已經猜到語法了,下面給出了一個例子:

# the first argument is the named placeholder name – notice named
# placeholders always start with a colon.
$STH->bindParam(‘:name’, $name);

您也能夠在這裏使用一個快捷方式,但它能夠和關聯數組一塊兒使用.

# the data we want to insert
$data = array( ‘name’ => ‘Cathy’, ‘addr’ => ’9 Dark and Twisty’, ‘city’ => ‘Cardiff’ );
# the shortcut!
$STH = $DBH->(「INSERT INTO folks (name, addr, city) value (:name, :addr, :city)」);
$STH->execute($data);

你的數組中的鍵不須要以一個冒號開始,可是必須符合指定的佔位符。若是你有一個二維數組(就是數組中的數組),您能夠遍歷它們,只需調用執行的每一個數據的數組。

另外一個命名佔位符不錯的特色是直接能夠插入對象到您的數據庫,若是命名的屬性匹配字段的話.下面是一個例子對象:

# a simple object
class person {
public $name;
public $addr;
public $city;
function __construct($n,$a,$c) {
$this->name = $n;
$this->addr = $a;
$this->city = $c;
}
# etc …
}
$cathy = new person(‘Cathy’,’9 Dark and Twisty’,'Cardiff’);
# here’s the fun part:
$STH = $DBH->(「INSERT INTO folks (name, addr, city) value (:name, :addr, :city)」);
$STH->execute((array)$cathy);

在執行中,對象被轉換爲一個數組.對象的屬性被視爲數組中的一個鍵. By casting the object to an array in the execute, the properties are treated as array keys.

選擇數據

Fetch data into arrays or objects

數據經過fetch()方法得到, {一種應用於陳述式句柄的方法}. 在使用fetch之間, 您最好告訴PDO您喜歡取得數據的樣子. 您有如下幾個選擇:

  • PDO::FETCH_ASSOC: 返回一個包含列名索引的數組
  • PDO::FETCH_BOTH (default): 返回一個由同時包含列名和數字索引的數組
  • PDO::FETCH_BOUND: 經過 ->bindColumn() 方法將列的值賦到變量上。
  • PDO::FETCH_CLASS:列的值賦給指定對象的屬性裏。若是指定的屬性不存在,會自動建立。
  • PDO::FETCH_INTO: 更新一個已經存在的命名對象的實例
  • PDO::FETCH_LAZY: 結合 了PDO::FETCH_BOTH,PDO::FETCH_OBJ,在它們被調用時建立對象變量
  • PDO::FETCH_NUM: 返回一個由同時包含列數字索引的數組
  • PDO::FETCH_OBJ: fanhuire返回一個有對應的列名的屬性的匿名對象

在現實中,大多數狀況下會使用如下三種: FETCH_ASSOC, FETCH_CLASS, FETCH_OBJ. 您須要使用如下語法設置獲取類型:

$STH->setFetchMode(PDO::FETCH_ASSOC);

您也能夠直接在fetch()方法中設置獲取模式.

FETCH_ASSOC

這種模式建立一個按列名索引的關聯數組.這應該會讓用過MySQL/MySQLi擴展的人感到親切.這裏有一個使用這種方法選擇數據的例子.

# using the shortcut ->query() method here since there are no variable
# values in the select statement.
$STH = $DBH->query(‘SELECT name, addr, city from folks’);
# setting the fetch mode
$STH->setFetchMode(PDO::FETCH_ASSOC);
while($row = $STH->fetch()) {
echo $row['name'] . 「n」;
echo $row['addr'] . 「n」;
echo $row['city'] . 「n」;
}

這個 while 循環將在獲取完全部數據後中止.{The while loop will continue to go through the result set one row at a time until complete.}

FETCH_OBJ

這種模式爲每一行數據建立一個標準類,下面是一個例子:

# creating the statement
$STH = $DBH->query(‘SELECT name, addr, city from folks’);
# setting the fetch mode
$STH->setFetchMode(PDO::FETCH_OBJ);
# showing the results
while($row = $STH->fetch()) {
echo $row->name . 「n」;
echo $row->addr . 「n」;
echo $row->city . 「n」;
}

FETCH_CLASS

您的對象的屬性應該在constructor被調用前設置!這一點很重要!

這種模式容許你直接將獲取的數據發送到您選擇的類中.當您使用FETCH_CLASS時,您的對象的屬性應該在constructor被調用前設置。讀一遍,它是重要的。若是屬性相匹配的列名不存在,這些屬性將被建立,(公共)爲您。

這意味着若是你須要轉換後出來的數據,它能夠經過你的對象自動爲轉換.

舉個列子,假設的狀況下該地址必須爲特定格式,咱們能夠經過constructor上作到這一點,下面是一個例子:

class secret_person {
public $name;
public $addr;
public $city;
public $other_data;
function __construct($other = 」) {
$this->address = preg_replace(‘/[a-z]/’, ‘x’, $this->address);
$this->other_data = $other;
}
}

OK,再讓咱們看看效果如何?

$STH = $DBH->query(‘SELECT name, addr, city from folks’);
$STH->setFetchMode(PDO::FETCH_CLASS, ‘secret_person’);
while($obj = $STH->fetch()) {
echo $obj->addr;
}

若是地址是,‘5 Rosebud,’ 您將會在輸出中看到 ‘5 Rxxxxxx’. 固然,可能有些狀況下,您但願在constructor函數在數據被賦值以前調用.PDO也能夠作到~

$STH->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, ‘secret_person’);

如今,當你使用這個模式(PDO::FETCH_PROPS_LATE)的地址不會被遮,constructor會被首先調用而後再賦值.

最後,若是你真的須要,你能夠在使用PDO獲取數據到對象時,將參數傳遞給構造函數:

$STH->setFetchMode(PDO::FETCH_CLASS, ‘secret_person’, array(‘stuff’));

若是你須要傳遞不一樣的數據到每一個對象的構造函數,你能夠設置在fetch方法內設置模式法模式:

$i = 0;
while($rowObj = $STH->fetch(PDO::FETCH_CLASS, ‘secret_person’, array($i))) {
// do stuff
$i++
}

其餘有用的方法

雖然 PDO 並無面面俱到的(這擴展可不小!), 這裏仍還還有一些您想知道的方法.

$DBH->lastInsertId();

lastInsertId()方法始終調用數據庫句柄,而不是表達式的句柄,而且會返回該數據庫鏈接上一次插入語句的自增ID.

$DBH->exec(‘DELETE FROM folks WHERE 1′);

$DBH->exec(「SET time_zone = ‘-8:00′」);

exec()方法用於那些不能返回數據或不影響行的操做. 上面是兩種調用exec()方法的例子.

$safe = $DBH->quote($unsafe);

quote() 方法將字符轉義爲安全的字符以便在查詢中使用. 若是您不使用已經準備號的語句,您能夠用此方法<<*>>。

$rows_affected = $STH->rowCount();

rowCount() 方法返回一個代表被一個操做影響的行數的整數(簡直是廢話,難不成仍是浮點數?). 更具這個錯誤報告(http://bugs.php.net/40822) ,在最近的一個PDO版本上這個方法不可以很好的與SELECT語句工做. 若是您遇到了這個問題而不想升級PHP的話, 你能夠用如下的方法來替代它:

$sql = 「SELECT COUNT(*) FROM folks」;
if ($STH = $DBH->query($sql)) {
# check the row count
if ($STH->fetchColumn() > 0) {
# issue a real select here, because there’s data! } else { echo 「No rows matched the query.」; } }
相關文章
相關標籤/搜索