PHP PDO訪問數據庫

PHP PDO訪問數據庫

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

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


PDO 簡介

“PDO – PHP Data Objects – is a database access layer providing a uniform method of access to multiple databases.”

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

PDO - db abstraction layer

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

數據庫支持

這個擴展能支持任何爲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 代碼塊內並使用異常機制 .通常你只會使用單個連接 – 下面將爲您介紹它的語法.

下文中出現的 $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.”;
}
}

結尾

我希望這篇文章能幫助您從mysql和mysqli擴展遷移至PDO.您有啥想法?現在想遷移到PDO麼?

英文原文:http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章