一区二区久久-一区二区三区www-一区二区三区久久-一区二区三区久久精品-麻豆国产一区二区在线观看-麻豆国产视频

PHP 5 數(shù)據(jù)對(duì)象 (PDO) 抽象層與 Oracle

一名新 php 數(shù)據(jù)對(duì)象 (PDO) 數(shù)據(jù)抽象層的原始開(kāi)發(fā)人員為您簡(jiǎn)要介紹該抽象層,重點(diǎn)講述與 Oracle 一起運(yùn)行的情況。

需要 php:5.0
需要其他:Oracle 8 或更高版本客戶端庫(kù)
下載用于 Oracle 的 PDO (Windows):php_pdo.dll, php_pdo_oci.dll
下載用于 Oracle 的 PDO (Unix):pdo, pdo_oci


PDO 簡(jiǎn)介

php 主要是由志愿者完成的項(xiàng)目;盡管有少數(shù)一些固定的“核心”開(kāi)發(fā)人員,但是我們沒(méi)有一個(gè)人在全職受薪的開(kāi)發(fā) php。除此之外,我們分別位于世界不同地方,您可以想象長(zhǎng)期開(kāi)發(fā)的協(xié)調(diào)工作是何等困難。因此,php 主要是基于突發(fā)奇想的個(gè)人短期需求來(lái)發(fā)展的,其原因也多種多樣,有的是試驗(yàn),有的則是因?yàn)椤懊魈煊谢钜弧薄1M管這樣通常每一步都會(huì)改善 php,但從長(zhǎng)遠(yuǎn)來(lái)看則是缺乏完整性 - 數(shù)據(jù)庫(kù)擴(kuò)展就是一個(gè)重要的例子。

在各種不同的數(shù)據(jù)擴(kuò)展(oci、mysql、postgresql、mssql 等)之間根本沒(méi)有真正的一致性,甚至在某些情況下,在這些擴(kuò)展內(nèi)部也沒(méi)有真正的一致性。幾乎所有這些擴(kuò)展都在使用與基礎(chǔ)數(shù)據(jù)庫(kù) API 緊密相連的不同代碼完成著相同種類(lèi)的任務(wù)。而且因?yàn)槲覀儯?a href=/itjie/phpjishu/ target=_blank class=infotextkey>php 核心開(kāi)發(fā)人員和擴(kuò)展開(kāi)發(fā)人員)的人手非常有限,因此這就造成了代碼更加難以維護(hù),從而為 php 帶來(lái)了很大的問(wèn)題。

由于 php 越來(lái)越受歡迎并不斷成功,因此主要 php 數(shù)據(jù)庫(kù)擴(kuò)展的維護(hù)者們參加了在德國(guó)舉行的 LinuxTag 2003 大會(huì),在會(huì)上我們交換了對(duì) php 前景的看法。在討論 php 發(fā)展的隨機(jī)性時(shí),我們確定了在 php 中進(jìn)行數(shù)據(jù)庫(kù)訪問(wèn)的一些目標(biāo):

?提供一種輕型、清晰、方便的 API
?統(tǒng)一各種不同 RDBMS 庫(kù)的共有特性,但不排除更高級(jí)的特性。
?通過(guò) php 腳本提供可選的較大程度的抽象/兼容性。

我們之所以提出了這種 php 數(shù)據(jù)對(duì)象 (PDO) 的概念,是因?yàn)槲覀兿Mㄟ^(guò)采用 Zend Engine 2(php 5 的核心)先進(jìn)的面向?qū)ο筇匦垣@得該 API 的一些更優(yōu)秀的性能。

php 中的數(shù)據(jù)抽象層概念一點(diǎn)都算不上新;在 Google 中查詢“php database abstraction”會(huì)找到大約 83,200 個(gè)匹配項(xiàng)。它幾乎是許多 php 開(kāi)發(fā)人員夢(mèng)寐以求的,而其產(chǎn)生則部分歸因于我們不完整的 API。如果您曾經(jīng)嘗試過(guò)使用第三方抽象層來(lái)完成任何真正重要的工作,通常會(huì)發(fā)現(xiàn)這些抽象層對(duì)于手頭的工作來(lái)說(shuō)設(shè)計(jì)的功能過(guò)于強(qiáng)大了 - 或者表現(xiàn)為在使用前需要進(jìn)行大量學(xué)習(xí),或者表現(xiàn)為接口速度緩慢,參數(shù)需要經(jīng)過(guò)多層腳本函數(shù)調(diào)用才能到達(dá)數(shù)據(jù)庫(kù)自有的 API;通常是存在上述兩種表象。

為什么這些抽象層會(huì)存在這種問(wèn)題?這些抽象層總是在試圖完成太多的任務(wù),甚至可能是不可能的任務(wù)。我們決定以實(shí)用為目標(biāo),僅將一些最常見(jiàn)的數(shù)據(jù)庫(kù) API 特性作為我們的基礎(chǔ),并使得 PDO 驅(qū)動(dòng)程序能夠?qū)⑺鼈兲囟ㄓ诋a(chǎn)品的特性暴露為常規(guī)擴(kuò)展函數(shù)。


為什么使用 PDO?


聽(tīng)過(guò)有關(guān)數(shù)據(jù)庫(kù)抽象擴(kuò)展謠傳的大多數(shù)人會(huì)立刻對(duì) PDO 的擴(kuò)展方面產(chǎn)生疑惑 - 我們是否要分析 SQL,將其轉(zhuǎn)換為相應(yīng)的后端方言呢?我們?nèi)绾翁幚硖匦?X 或特性 Y,等等。因此,當(dāng)您聽(tīng)說(shuō)我們?cè)?PDO 中根本不用為此而擔(dān)憂時(shí)可能會(huì)大吃一驚;我們不希望使所有內(nèi)容都完全統(tǒng)一,因?yàn)橐沟眠@種統(tǒng)一成為可能,只能是將自己限制在最低的通用標(biāo)準(zhǔn)。

如果 PDO 不是一個(gè)整體的抽象層,那還有什么別的原因值得您考慮使用它嗎?

?性能。PDO 從一開(kāi)始就吸取了現(xiàn)有數(shù)據(jù)庫(kù)擴(kuò)展成功和失敗的經(jīng)驗(yàn)教訓(xùn)。因?yàn)?PDO 的代碼是全新的,所以我們有機(jī)會(huì)重新開(kāi)始設(shè)計(jì)性能,以利用 php 5 的最新特性。
?能力。PDO 旨在將常見(jiàn)的數(shù)據(jù)庫(kù)功能作為基礎(chǔ)提供,同時(shí)提供對(duì)于 RDBMS 獨(dú)特功能的方便訪問(wèn)。
?簡(jiǎn)單。PDO 旨在使您能夠輕松使用數(shù)據(jù)庫(kù)。API 不會(huì)強(qiáng)行介入您的代碼,同時(shí)會(huì)清楚地表明每個(gè)函數(shù)調(diào)用的過(guò)程。
?運(yùn)行時(shí)可擴(kuò)展。PDO 擴(kuò)展是模塊化的,使您能夠在運(yùn)行時(shí)為您的數(shù)據(jù)庫(kù)后端加載驅(qū)動(dòng)程序,而不必重新編譯或重新安裝整個(gè) php 程序。例如,PDO_OCI 擴(kuò)展會(huì)替代 PDO 擴(kuò)展實(shí)現(xiàn) Oracle 數(shù)據(jù)庫(kù) API。還有一些用于 MySQL、PostgreSQL、ODBC 和 Firebird 的驅(qū)動(dòng)程序,更多的驅(qū)動(dòng)程序尚在開(kāi)發(fā)。

您可能想了解 PDO 與其他常用的抽象層的對(duì)比情況,例如 PEAR DB 或 ADODB。無(wú)論在 API 方面還是在性能方面,PDO 都比其他常見(jiàn)抽象層要輕型,但是涉及到在各個(gè)數(shù)據(jù)庫(kù)后端之間提供統(tǒng)一性方面,則不如那些抽象層,例如用于處理大量可移植性問(wèn)題的 PEAR MDB 2 抽象層。


在哪里可以獲得 PDO?


PDO 是通過(guò) PECL(發(fā)音為“pee-kle”,歐洲語(yǔ)言風(fēng)格),即 php 擴(kuò)展庫(kù)提供的。如果您在運(yùn)行 Linux 計(jì)算機(jī),請(qǐng)按照下面的說(shuō)明進(jìn)行設(shè)置;稍后是在 Windows 上安裝的詳細(xì)信息。

請(qǐng)注意,PDO 及其驅(qū)動(dòng)程序當(dāng)前處于“alpha”狀態(tài);這就意味著我們會(huì)合理保證沒(méi)有重大缺陷,但是該程序包功能并不完善 - 我們還要添加很多功能。雖然我們鼓勵(lì)您測(cè)試該程序包,但是實(shí)在不推薦在現(xiàn)階段將其用于生產(chǎn)。


Unix/Linux 安裝


如果您以前尚未嘗試過(guò) php 5,則請(qǐng)花一點(diǎn)時(shí)間來(lái)通讀一下“新聞”和各種聲明。在 UNIX 計(jì)算機(jī)上,您可能要安裝或升級(jí) libxml2;如果沒(méi)有 libxml2,“pear”程序包管理工具就無(wú)法運(yùn)行,您安裝 PDO 時(shí)就會(huì)遇到很多困難。獲取 php 5,并將其編譯和安裝。確保指定的前綴不是 /usr/local/,這樣它就不會(huì)與 php 4 安裝發(fā)生沖突了:


% ./configure --prefix=/usr/local/php5 --with-zlib [此處指定其他選項(xiàng)]
% make install

 

現(xiàn)在您就可以使用“pear”工具獲取并安裝 PDO 以及用于 PDO 的 Oracle 驅(qū)動(dòng)程序了。因?yàn)?PDO 當(dāng)前標(biāo)記為 alpha,所以默認(rèn)情況下 pear 工具不會(huì)下載該程序包。在該程序包名稱(chēng)后面添加后綴“-alpha”,通知該 pear 工具可以安裝 alpha 版本:


% PATH="/usr/local/php5/bin:$PATH"
% pear install PDO-alpha

 

您需要告知 php 從專(zhuān)用于 php 5 的 php.ini 文件加載 PDO 驅(qū)動(dòng)程序。如果您使用的前綴與我使用的一樣,php 則會(huì)在 /usr/local/php5/lib/php.ini 中查找 php.ini 文件。向該文件中添加以下行:

extension=pdo.so

現(xiàn)在您需要獲取數(shù)據(jù)庫(kù)特定的驅(qū)動(dòng)程序;對(duì)于 Oracle,此特定程序稱(chēng)為 PDO_OCI。在 shell 中,鍵入:

% pear install PDO_OCI-alpha

此驅(qū)動(dòng)程序也需要從 php.ini 文件加載;將下行添加到前面添加的那行之后:

extension=pdo_oci.so

現(xiàn)在檢查一下,確保它能夠運(yùn)行:

% php -m

在模塊列表中,您應(yīng)該會(huì)看到 PDO 和 PDO_OCI。


防火墻礙事了?


如果您位于防火墻的后面,則在使用 pear 安裝程序獲取程序包時(shí)可能會(huì)遇到一些問(wèn)題。如果發(fā)生這種情況,則可以按照下列說(shuō)明手動(dòng)下載并安裝這些程序包:


% wget http://pecl.php.NET/get/PDO
% pear install PDO-0.1.1.tgz


[ 將 extension=pdo.so 添加到 php.ini ]


% wget http://pecl.php.NET/get/PDO_OCI
% pear install PDO_OCI-0.1.tgz


[ 將 extension=pdo_oci.so 添加到 php.ini ]

在上述兩種情況下,都需要首先調(diào)用“pear install”(后跟下載的真正程序包);上述示例中的版本號(hào)在本文編寫(xiě)之時(shí)是最新的,但隨著開(kāi)發(fā)的繼續(xù)進(jìn)行會(huì)發(fā)生變化。


Windows 安裝


如果您正在運(yùn)行 Windows,則請(qǐng)按照下列說(shuō)明執(zhí)行:

?從 http://www.php.NET/downloads.php#v5 獲取 php 5,將其解壓縮到 C:/php5。
?從 http://snaps.php.NET/win32/PECL_5_0/php_pdo.dll 和http://snaps.php.NET/win32/PECL_5_0/php_pdo_oci.dll 分別獲取 PDO 和 PDO_OCI,將其放入 C:/php5/ext。或者,您可以從 php 5 下載頁(yè)上列出的“用于 php 5.0.0 的 PECL 模塊集合”zip 文件中找到所有這些 PDO 驅(qū)動(dòng)程序,以及所有 PECL 程序包的所有 Windows 版本。
?編輯 C:/php5/php.ini 文件,并添加下列內(nèi)容:

extension=php_pdo.dll
extension=php_pdo_oci.dll

編輯 php.ini 文件時(shí),有一點(diǎn)很重要,即要在任何其他 PDO 驅(qū)動(dòng)程序之前先加載 PDO 擴(kuò)展,否則就不能正確初始化(在這種情況下會(huì)出錯(cuò))。

如果在 Windows 目錄中有一個(gè) php 4 的全局 php.ini 文件,則可能會(huì)遇到問(wèn)題。最好的解決方法是,移動(dòng)php.ini 文件,使其與 php 4 SAPI 位于相同的文件夾中,以隔離 php 4 安裝;例如,將其移動(dòng)到與 php4apache.dll 相同的文件夾中。請(qǐng)注意,php 5 程序中并非所有文檔都是最新的;推薦的安裝過(guò)程如上面所述 - 如 install.txt 文件所聲明的,請(qǐng)勿將任何 DLL 復(fù)制到 windows 文件夾或 system 文件夾中 - 任何內(nèi)容都是自包含的。如果您運(yùn)行的是 apache,并且遇到無(wú)法加載 DLL 的錯(cuò)誤,則檢查一下是否將 C:/php5 添加到了 PATH 中。另外,還要注意 php 5 的 CGI 版本現(xiàn)在的名稱(chēng)為 php-cgi.exe。


連接 PDO


首先創(chuàng)建 PDO 類(lèi)的一個(gè)實(shí)例,將其用作數(shù)據(jù)庫(kù)句柄。使用哪個(gè)基礎(chǔ)驅(qū)動(dòng)程序并不重要;您總要使用 PDO 類(lèi)名。構(gòu)造函數(shù)的第一個(gè)參數(shù)為數(shù)據(jù)源名稱(chēng) (DSN),第二個(gè)參數(shù)為用戶名,第三個(gè)參數(shù)為該用戶名的口令。DSN 的 PDO 命名慣例為 PDO 驅(qū)動(dòng)程序的名稱(chēng),后面一個(gè)冒號(hào),再后面是可選的驅(qū)動(dòng)程序特定的信息。在我們的示例中,會(huì)加載 OCI 驅(qū)動(dòng)程序但不指定任何其他信息;這樣會(huì)使用默認(rèn)的數(shù)據(jù)庫(kù)。對(duì)于其他驅(qū)動(dòng)程序,如 ODBC 驅(qū)動(dòng)程序,第一個(gè)冒號(hào)后面的所有內(nèi)容都將被用作 ODBC DSN。MySQL 驅(qū)動(dòng)程序會(huì)同樣以不同的方式解釋它的 DSN。

如果無(wú)法加載該驅(qū)動(dòng)程序,或者發(fā)生了連接失敗,則會(huì)拋出一個(gè) PDOException,以便您可以決定如何最好地處理該故障。


<?php
try {
$dbh = new PDO("OCI:", "scott", "tiger");
} catch (PDOException $e) {
echo "Failed to obtain database handle " .$e->getMessage();
    }
?>


在連接字符串中,您可以指定兩個(gè)可選參數(shù);第一個(gè)是數(shù)據(jù)庫(kù)名稱(chēng),第二個(gè)是字符集;這些參數(shù)與可選的第三個(gè)和第四個(gè)參數(shù)相對(duì)應(yīng),后兩個(gè)參數(shù)您可能在 oci8 擴(kuò)展函數(shù) ociconnect() 或 ociplogon() 中使用過(guò)。要使用特定的字符集連接一個(gè)特定的數(shù)據(jù)庫(kù),則可以執(zhí)行下列操作:


<?php
try {
$dbh = new PDO("OCI:dbname=accounts;charset=UTF-8", "scott", "tiger");
} catch (PDOException $e) {
echo "Failed to obtain database handle " .$e->getMessage();
    }
?>


省略 try..catch 控制結(jié)構(gòu)并無(wú)裨益。如果在應(yīng)用程序的較高級(jí)別沒(méi)有定義異常處理,則在無(wú)法建立數(shù)據(jù)庫(kù)連接的情況下,該腳本會(huì)終止。


連接管理


目前,PDO 完全沒(méi)有執(zhí)行自己的任何連接管理,因此每個(gè)“新 PDO”調(diào)用都會(huì)建立一個(gè)新的數(shù)據(jù)庫(kù)連接。該連接在 $dbh 變量越界時(shí),或者當(dāng)您為其指定 NULL 值時(shí)會(huì)被釋放。


<?php
try {
$dbh = new PDO("OCI:dbname=accounts;charset=UTF-8", "scott", "tiger");
} catch (PDOException $e) {
echo "Failed to obtain database handle " .$e->getMessage();
exit;
    }
// 在此處對(duì)數(shù)據(jù)庫(kù)執(zhí)行一些操作
    // ...

// 現(xiàn)在完成,釋放該連接
$dbh = null;
?>


計(jì)劃在不久的將來(lái)為 PDO 增加連接緩存功能;就當(dāng)前的 oci8 擴(kuò)展而言,會(huì)重用與現(xiàn)有服務(wù)器的連接,并且在這些連接中,還會(huì)重用閑置的登錄。當(dāng)在緩存連接模式中運(yùn)行時(shí),如上面的代碼段所示釋放 $dbh 時(shí)會(huì)將該登錄標(biāo)記為可由其他連接重用。

如果您使用 ODBC 驅(qū)動(dòng)程序訪問(wèn) Oracle,則可能會(huì)很高興地注意到,默認(rèn)情況下 PDO_ODBC 驅(qū)動(dòng)程序支持 ODBC 連接池。


使用 PDO


了解一個(gè)編程 API 的最好方式就是使用它,因此我們來(lái)看一下附帶的這個(gè)演示,以了解如何進(jìn)行批次更新(代碼如下)。


<?php

// Create a PDO database handle object
// the 'oci:' string specifies that the OCI driver should be used
// you could use 'oci:dbname=name' to specify the database name.
// The second and third parameters are the username and password respectively
$dbh = new PDO('oci:', 'scott', 'tiger');

// Create a test table to hold the data from credits.csv
$dbh->exec("
CREATE TABLE CREDITS (
 extension varchar(255),
 name varchar(255)
)");

// start a transaction
$dbh->beginTransaction();

// prepare to insert a large quantitiy of data
$stmt = $dbh->prepare("INSERT INTO CREDITS (extension, name) VALUES (:extension, :name)");

// bind the inputs to php variables; specify that the data will be strings
// with a maximum length of 64 characters
$stmt->bindParam(':extension', $extension, PDO_PARAM_STR, 64);
$stmt->bindParam(':name',   $name,  PDO_PARAM_STR, 64);

// Open the .csv file for import
$fp = fopen('credits.csv', 'r');
while (!feof($fp)) {
 list($extension, $name) = fgetcsv($fp, 1024);
 $stmt->execute();
}
fclose($fp);

// Commit the changes
$dbh->commit();

?>


既然我們已經(jīng)成功連接到了 Oracle,那么現(xiàn)在就可以創(chuàng)建一個(gè)表來(lái)保存一些數(shù)據(jù)了。對(duì)于此示例,我們使用一些 php 擴(kuò)展及其作者,并將這些內(nèi)容輸入一個(gè)數(shù)據(jù)庫(kù)中。數(shù)據(jù)庫(kù)句柄對(duì)象的 exec() 方法可用來(lái)發(fā)出不會(huì)返回結(jié)果集的快速一次性查詢,因此我們?cè)谶@里使用該方法來(lái)發(fā)出 CREATE TABLE 查詢。

為了使得示例更自然,我從 php 源代碼中抽取了擴(kuò)展及其作者的信息,并將其存儲(chǔ)到了一個(gè) CSV 文件中(請(qǐng)參見(jiàn)“相關(guān)附件:credits.csv”)。這就代表一個(gè)常見(jiàn)情形:從 CSV 文件批次導(dǎo)入數(shù)據(jù)。在我們的示例中,我們充分利用了 Oracle 的預(yù)處理語(yǔ)句和綁定參數(shù),以獲得一個(gè)高效的數(shù)據(jù)導(dǎo)入腳本。在講述該示例之前,有必要了解一下 PDO 處理事務(wù)的方式。


PDO 中的事務(wù)處理


Oracle 具有一個(gè)敏感的默認(rèn)操作模式:當(dāng)您進(jìn)行連接時(shí),將會(huì)位于一個(gè)隱式事務(wù)處理中,在提交事務(wù)之前其中的更改不會(huì)完全生效。除了事務(wù)處理的標(biāo)準(zhǔn)優(yōu)點(diǎn)(原子性、一致性、隔離性、可持久性 - ACID)之外,數(shù)據(jù)庫(kù)服務(wù)器在執(zhí)行每次更新之后還不需要重新構(gòu)建索引和其他內(nèi)部結(jié)構(gòu);它可以延遲到提交之后進(jìn)行。這樣會(huì)加速代碼的執(zhí)行。Oracle 這點(diǎn)確實(shí)很好。

但不幸的是,并非每個(gè)數(shù)據(jù)庫(kù)供應(yīng)商都支持事務(wù)處理,并且因?yàn)?PDO 旨在以一種相對(duì)可移植的方式支持這些事務(wù)處理,所以它默認(rèn)情況下以自動(dòng)提交模式運(yùn)行。啟用自動(dòng)提交模式后,數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序會(huì)隱式提交每個(gè)成功的更新。當(dāng)您調(diào)用 $dbh->beginTransaction() 時(shí),就會(huì)請(qǐng)求關(guān)閉自動(dòng)提交,直到調(diào)用 $dbh->commit() 或者 $dbh->rollBack() 才會(huì)重新啟用,具體取決于您的代碼是怎樣編寫(xiě)的。如果基礎(chǔ)驅(qū)動(dòng)程序不支持事務(wù)處理,則會(huì)拋出一個(gè) PDOException。

如果發(fā)生了問(wèn)題并且 php 出錯(cuò),您的腳本將退出并且事務(wù)處于待批狀態(tài);或者您關(guān)閉數(shù)據(jù)庫(kù)句柄時(shí),PDO 會(huì)自動(dòng)針對(duì)任何待批的事務(wù)調(diào)用 $dbh->rollBack()。此行為會(huì)減少向數(shù)據(jù)庫(kù)中提交可能未定義或者已損壞數(shù)據(jù)的可能性,這是用于處理已放棄事務(wù)的標(biāo)準(zhǔn)語(yǔ)義。

 

預(yù)處理語(yǔ)句、存儲(chǔ)過(guò)程


PDO 支持使用 Oracle 樣式命名的占位符語(yǔ)法將變量幫定到 SQL 中的預(yù)處理語(yǔ)句(與 oci8 擴(kuò)展中的 ocibindbyname() 類(lèi)似)。PDO 還為其他數(shù)據(jù)庫(kù)(如 ODBC)提供了命名占位符模擬,甚至可以為生來(lái)就不支持該概念的數(shù)據(jù)庫(kù)(如 MySQL)模擬預(yù)處理語(yǔ)句和綁定參數(shù)。這是 php 向前邁進(jìn)的積極一步,因?yàn)檫@樣可以使開(kāi)發(fā)人員能夠用 php 編寫(xiě)“企業(yè)級(jí)”的數(shù)據(jù)庫(kù)應(yīng)用程序,而不必特別關(guān)注數(shù)據(jù)庫(kù)平臺(tái)的能力。

使用 PDO 預(yù)處理語(yǔ)句非常簡(jiǎn)單,調(diào)用數(shù)據(jù)庫(kù)句柄的 prepare() 方法即可。它會(huì)返回一個(gè)語(yǔ)句句柄對(duì)象,然后您可以使用該對(duì)象來(lái)綁定參數(shù)和執(zhí)行語(yǔ)句。在此示例中,我們將要定義兩個(gè)命名占位符,“:extension”和“:name”,這兩個(gè)占位符分別與 .CSV 文件中的 php 擴(kuò)展名稱(chēng)和其中一個(gè)作者的姓名相對(duì)應(yīng)。


$stmt = $dbh->prepare("INSERT INTO CREDITS (extension, name) VALUES (:extension, :name)");


預(yù)處理了語(yǔ)句之后,我們使用 bindParam() 方法來(lái)將這些命名參數(shù)分別與 php 變量名稱(chēng)“$extension”和“$name”相關(guān)聯(lián)(這與 ocibindbyname() 類(lèi)似)。我們還會(huì)通知 Oracle,這些數(shù)據(jù)將要格式化為字符串,最大長(zhǎng)度為 64 個(gè)字符。


$stmt->bindParam(':extension', $extension, PDO_PARAM_STR, 64);
$stmt->bindParam(':name',      $name,      PDO_PARAM_STR, 64);


我們現(xiàn)在即準(zhǔn)備好插入數(shù)據(jù)了 - 我們只需要打開(kāi)該 CSV 文件,并從中獲取數(shù)據(jù)即可。通過(guò)使用 fopen() 和 fgetcsv() 函數(shù)可以相當(dāng)簡(jiǎn)單地完成此操作。然后,我們可以使用 php list() 構(gòu)造函數(shù)直接將 CSV 的列指定給變量“$extension”和“$name”。因?yàn)檫@些變量已經(jīng)綁定到了語(yǔ)句中,所以我們現(xiàn)在要做的只是調(diào)用該語(yǔ)句對(duì)象的 execute() 方法使其執(zhí)行插入。這種方式既方便又快捷 - 在事務(wù)處理時(shí)每個(gè)迭代循環(huán)只有兩行。到達(dá)文件尾時(shí),我們就可以立即使用數(shù)據(jù)庫(kù)句柄的 commit() 方法來(lái)提交這些更改了。

如果您只是要傳遞輸入?yún)?shù),并且有許多這樣的參數(shù)要傳遞,那么您會(huì)覺(jué)得下面所示的快捷方式語(yǔ)法非常有幫助;此語(yǔ)法使您能夠省去對(duì) $stmt->bindParam() 的調(diào)用。


$stmt = $dbh->prepare("INSERT INTO CREDITS (extension, name) VALUES (:extension, :name)");
$stmt->execute(array(':extension' => $extension, ':name' => $name));


您還可以使用 bindParam 來(lái)為存儲(chǔ)過(guò)程設(shè)置輸入/輸出參數(shù);語(yǔ)法是完全相同的,只是查詢有所不同。下面的代碼演示如何調(diào)用一個(gè)名為“sp_add_item”的存儲(chǔ)過(guò)程;其目的是要針對(duì)輸入設(shè)置 $item_name,然后該存儲(chǔ)過(guò)程將在返回時(shí)更新 $error_code。


$stmt = $dbh->prepare("begin sp_add_item(:item_name, :error_code); end");
$stmt->bindParam(':item_name', $item_name, PDO_PARAM_STR,  12);
$stmt->bindParam(':error_code', $error_code, PDO_PARAM_STR, 12);
$stmt->execute();

 


抓取數(shù)據(jù)


使用 PDO 抓取數(shù)據(jù)與進(jìn)行插入或更新相似,只是您執(zhí)行完查詢之后,將要重復(fù)調(diào)用 fetch() 方法來(lái)獲取結(jié)果集的下一行。進(jìn)行獲取的最簡(jiǎn)單情況如下所示,值得注意的一點(diǎn)是,您還可以將參數(shù)綁定到查詢,以控制如 WHERE 子句這樣的內(nèi)容;執(zhí)行此操作的語(yǔ)法與我們已經(jīng)看到的 bindParam() 代碼完全相同。

 

$stmt = $dbh->prepare("SELECT extension, name from CREDITS");
if ($stmt->execute()) {
while ($row = stmt->fetch()) {
print_r($row);
    }
}


PDO 支持一些不同的抓取策略,這些策略在方便性和性能方面有所差別;通過(guò)將下列選項(xiàng)之一指定為 fetch() 方法的參數(shù),您可以更改其返回值以適應(yīng)您的語(yǔ)法:


?PDO_FETCH_NUM - 每個(gè)行抓取返回一個(gè)按照列位置索引的數(shù)組,并且以 0 為基數(shù)(第一列是第 0 個(gè)元素)。

while ($row = $stmt->fetch(PDO_FETCH_NUM)) {
 printf("Extension %s, by %s<br>", $row[0], $row[1]);
}


?PDO_FETCH_ASSOC - 每個(gè)行抓取根據(jù)行集中的列名,返回一個(gè)按列名索引的數(shù)組。

while ($row = $stmt->fetch(PDO_FETCH_ASSOC)) {
 echo "Extension $row[EXTENSION] by $row[NAME]<br>";
}

?PDO_FETCH_BOTH - 每個(gè)行抓取返回一個(gè)既按照列位置又按照列名索引的數(shù)組。也就是上述兩種情況的直接組合。如果沒(méi)有指定抓取模式,則該模式為默認(rèn)模式。
?PDO_FETCH_OBJ - 每個(gè)行抓取返回一個(gè)匿名對(duì)象,其屬性名與列名對(duì)應(yīng)。

while ($row = $stmt->fetch(PDO_FETCH_ASSOC)) {
 echo "Extension {$row->EXTENSION} by {$row->NAME}<br>";
}

?PDO_FETCH_LAZY - 每個(gè)行抓取返回一個(gè)引用語(yǔ)句對(duì)象的重載對(duì)象。這“看起來(lái)”好像是 PDO_FETCH_OBJ 和 PDO_FETCH_BOTH 的組合,只是只有當(dāng)您在腳本中訪問(wèn) php 變量時(shí)才創(chuàng)建這些變量。
?PDO_FETCH_BOUND - 抓取每行,返回 TRUE。在使用綁定輸出列時(shí)這種方式非常有用,它可以避免創(chuàng)建不需要的任何數(shù)組或?qū)ο蟆#ㄕ?qǐng)參見(jiàn)下面的示例)。


無(wú)論您使用哪種抓取策略,當(dāng)沒(méi)有其他行可抓取時(shí),fetch() 方法將會(huì)返回 FALSE。

現(xiàn)在我要講述一些技巧,如果您需要最后再調(diào)整一下腳本性能的話,這些技巧可能會(huì)對(duì)您有所幫助。但先給你一個(gè)忠告:要像躲避瘟疫一樣避免不成熟的優(yōu)化。您應(yīng)該總是首選最清晰、可維護(hù)性最好的解決方案。請(qǐng)記住,在一個(gè)典型的 Web 應(yīng)用程序中,您不能衡量各種抓取模式間的區(qū)別,除非腳本要處理很多行。我再重復(fù)一遍:抓取模式間的性能區(qū)別非常小 - 請(qǐng)使用最適合您代碼的模式。

請(qǐng)記住,使用 PDO_FETCH_NUM 的花銷(xiāo)最小,因?yàn)樵L問(wèn)列數(shù)據(jù)只是一個(gè)簡(jiǎn)單的數(shù)值查詢。PDO_FETCH_OBJ 使您能夠使用 OO 語(yǔ)法將數(shù)據(jù)集的列作為對(duì)象的屬性來(lái)訪問(wèn),但是每個(gè)屬性訪問(wèn)都涉及一個(gè)附加的散列查詢,使得使用它的花銷(xiāo)基本上與 PDO_FETCH_ASSOC 相同。每個(gè)這樣的模式都會(huì)復(fù)制整行,從而占用稍多的內(nèi)存。

很多數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序都會(huì)代表您預(yù)先抓取并緩存一定數(shù)量的行。php 每次訪問(wèn)其中一個(gè)這樣行中的列時(shí),它都需要將其復(fù)制到自己的專(zhuān)用內(nèi)存區(qū)域中。如果您的查詢涉及很多行,而只需要基于某種復(fù)雜的邏輯訪問(wèn)給定行的特定列,則您會(huì)發(fā)現(xiàn) PDO_FETCH_LAZY 是一種避免使用很多內(nèi)存的有用方法,因?yàn)樗挥性谀L問(wèn)給定列時(shí)才復(fù)制該列。使用此方式時(shí)要注意,從某個(gè)給定語(yǔ)句為每個(gè) fetch() 抓取的“惰性對(duì)象”是每次迭代時(shí)使用的同一對(duì)象(以減少每次創(chuàng)建/銷(xiāo)毀它的開(kāi)銷(xiāo))。這就暗示著您不能只是簡(jiǎn)單地存儲(chǔ)該對(duì)象用于以后的比較,因?yàn)樗匀粫?huì)引用該語(yǔ)句的當(dāng)前行 - 您需要手動(dòng)復(fù)制所需要的部分。

最后一種模式為 PDO_FETCH_BOUND,該模式會(huì)告知 PDO 您已經(jīng)將所有列綁定到了 php 變量,并且除了要它在到達(dá)行集的末尾時(shí)通知您外不需要它執(zhí)行別的任何操作。綁定輸出列在概念上與綁定輸入?yún)?shù)相似,只是綁定輸出列可以用于所有數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序。您可以將 php 變量綁定到命名列,PDO 將在每次調(diào)用 execute() 時(shí)對(duì)其進(jìn)行更新。此技術(shù)可用來(lái)剃去結(jié)果集中每列、每行的一些虛擬機(jī)器操作碼(這種代碼速度比原生碼要慢)。這種技術(shù)的缺點(diǎn)在于,可能會(huì)使您的代碼難以跟蹤(也稱(chēng)為 WTF 系數(shù)較高),您使用變量名稱(chēng)時(shí)需要倍加小心。下面的代碼說(shuō)明了綁定輸出列的使用。請(qǐng)注意,您不必指定 PDO_FETCH_BOUND 即可使用 $stmt->bindColumn();PDO_FETCH_BOUND 只是一個(gè)對(duì)于您了解只能使用綁定值的情況的一種優(yōu)化。


$stmt = $dbh->prepare("SELECT extension, name from CREDITS");
if ($stmt->execute()) {
$stmt->bindColumn('EXTENSION', $extension);
$stmt->bindColumn('NAME',      $name);
while ($stmt->fetch(PDO_FETCH_BOUND)) {
echo "Extension:$extension, Author:$name/n";
    }
}

 


可移植性


區(qū)分大小寫(xiě)的列

PDO 旨在令使用可移植 SQL 的腳本運(yùn)行良好、可移植。本文中提及的所有查詢(調(diào)用存儲(chǔ)過(guò)程除外)在使用任何 PDO 驅(qū)動(dòng)程序時(shí)其運(yùn)行性能應(yīng)該相同 - 包括所有綁定輸入變量和綁定輸出列。

但有一個(gè)轉(zhuǎn)換問(wèn)題 - 當(dāng)您使用 PDO_FETCH_ASSOC 抓取數(shù)據(jù)時(shí),不同的驅(qū)動(dòng)程序會(huì)以不同的方式返回列名 - 某些會(huì)將列名轉(zhuǎn)化為大寫(xiě),某些轉(zhuǎn)換為小寫(xiě),某些則會(huì)使其呈查詢中指定的樣式。這對(duì)于 php 腳本來(lái)說(shuō)是一個(gè)潛在的問(wèn)題,因?yàn)閿?shù)組鍵區(qū)分大小寫(xiě)。PDO 提供了一個(gè)兼容性屬性來(lái)幫助規(guī)范腳本的結(jié)果。下面的小代碼段是上面 PDO_FETCH_BOUND 示例的可移植版本,因?yàn)?setAttribute() 方法調(diào)用會(huì)指導(dǎo) PDO 將抓取返回的列名全部轉(zhuǎn)換為大寫(xiě):


$dbh = new PDO('OCI:', 'scott', 'tiger');
$dbh->setAttribute(PDO_ATTR_CASE, PDO_CASE_UPPER);
stmt = $dbh->prepare("SELECT extension, name from CREDITS");
if ($stmt->execute()) {
$stmt->bindColumn('EXTENSION', $extension);
$stmt->bindColumn('NAME',      $name);
while ($stmt->fetch(PDO_FETCH_BOUND)) {
echo "Extension:$extension, Author:$name/n";
    }
}


除了 PDO_CASE_UPPER 之外,還有 PDO_CASE_LOWER(它會(huì)將列名轉(zhuǎn)換為小寫(xiě))和 PDO_CASE_NATURAL(它是默認(rèn)選項(xiàng):使列保持?jǐn)?shù)據(jù)庫(kù)驅(qū)動(dòng)程序返回的形式)。

錯(cuò)誤和錯(cuò)誤處理

可移植腳本的另一個(gè)難題是處理從各種數(shù)據(jù)庫(kù)處理程序返回的各種不同的錯(cuò)誤消息;某些數(shù)據(jù)庫(kù)對(duì)于程序化處理錯(cuò)誤的支持能力很差,而其他一些數(shù)據(jù)庫(kù)則具有非常豐富的錯(cuò)誤代碼。只要可行,PDO 將為您的腳本提供一個(gè)統(tǒng)一的錯(cuò)誤代碼,從而使您不必為應(yīng)對(duì)可移植性的這個(gè)方面所累。當(dāng)然,PDO 還會(huì)為驅(qū)動(dòng)程序提供原生錯(cuò)誤代碼和錯(cuò)誤消息,以防您需要用它來(lái)進(jìn)行診斷,或者錯(cuò)誤代碼映射不完整。

另一個(gè)困擾 php 數(shù)據(jù)庫(kù)擴(kuò)展的一致性問(wèn)題是錯(cuò)誤處理策略的一致性:某些擴(kuò)展會(huì)返回的錯(cuò)誤代碼需要您手動(dòng)抓取錯(cuò)誤字符串,而其他一些擴(kuò)展則只是發(fā)出 php 警告。PDO 允許您從下列三種不同的錯(cuò)誤處理策略中選擇一種:

?PDO_ERRMODE_SILENT
這是默認(rèn)模式;它只是使用語(yǔ)句和數(shù)據(jù)庫(kù)句柄對(duì)象的 errorCode() 和 errorInfo() 方法為您設(shè)置要檢查的錯(cuò)誤代碼。


if (!$dbh->exec($sql)) {
 echo $dbh->errorCode() ."<BR>";
 $info = $dbh->errorInfo();
 // $info[0] == $dbh->errorCode() 統(tǒng)一的錯(cuò)誤代碼
 // $info[1] 是驅(qū)動(dòng)程序特定的錯(cuò)誤代碼
 // $info[2] 是驅(qū)動(dòng)程序特定的錯(cuò)誤字符串
}

?
PDO_ERRMODE_WARNING
除了設(shè)置錯(cuò)誤代碼之外,PDO 還會(huì)發(fā)出 php 警告,您可以使用常規(guī)的 php 錯(cuò)誤處理程序捕獲該警告,并集中應(yīng)用您準(zhǔn)備好用于應(yīng)用程序的任何錯(cuò)誤處理/記錄策略,或者只是使該錯(cuò)誤顯示在瀏覽器中(在內(nèi)部測(cè)試過(guò)程中非常有用)。
?
PDO_ERRMODE_EXCEPTION
除了設(shè)置錯(cuò)誤代碼之外,PDO 還會(huì)拋出一個(gè) PDOException,并將其屬性設(shè)置為包含該錯(cuò)誤代碼和信息。然后,您可以在代碼的較高級(jí)別捕獲該異常,使用全局異常處理程序捕獲該異常,或者不對(duì)其進(jìn)行處理而終止腳本(此時(shí)將回滾任何未決的事務(wù))。

try {
 $dbh->exec($sql);
} catch (PDOException $e) {
 // 顯示警告消息
 print $e->getMessage();
 $info = $e->errorInfo;
 // $info[0] == $e->code; unified error code
 // $info[1] 是驅(qū)動(dòng)程序特定的錯(cuò)誤代碼
 // $info[2] 是驅(qū)動(dòng)程序特定的錯(cuò)誤字符串
}


請(qǐng)注意,與警告或異常相比,靜默模式針對(duì)運(yùn)行時(shí)錯(cuò)誤使用的資源最少,但是為了獲得該速度,您犧牲了一些簡(jiǎn)單性,而變得有一點(diǎn)復(fù)雜。

統(tǒng)一錯(cuò)誤代碼表當(dāng)前包括下列常量: PDO_ERR_NONE、PDO_ERR_CANT_MAP、PDO_ERR_SYNTAX、PDO_ERR_CONSTRAINT、PDO_ERR_NOT_FOUND、PDO_ERR_ALREADY_EXISTS、PDO_ERR_NOT_IMPLEMENTED、PDO_ERR_MISMATCH、PDO_ERR_TRUNCATED、PDO_ERR_DISCONNECTED。

這些常量所代表的意思字面即可推知,但是 PDO_ERR_CANT_MAP 代碼除外;這是一個(gè) PDO 特定的代碼,也就是說(shuō)它無(wú)法將驅(qū)動(dòng)程序特定的代碼映射到統(tǒng)一的錯(cuò)誤代碼,因此您應(yīng)該查詢 errorInfo() 方法返回的驅(qū)動(dòng)程序特定代碼來(lái)獲得更多信息。

數(shù)據(jù)類(lèi)型

PDO 在某種程度上類(lèi)型不可知,因此它喜歡將數(shù)據(jù)表示為字符串,而不是將其轉(zhuǎn)換為整數(shù)或雙精度類(lèi)型。此時(shí)您可能對(duì)此有些迷惑,但是原因非常簡(jiǎn)單:字符串類(lèi)型是最精確的類(lèi)型,在 php 中具有最廣泛的應(yīng)用范圍;過(guò)早地將數(shù)據(jù)轉(zhuǎn)換為整數(shù)或者雙精度類(lèi)型可能會(huì)導(dǎo)致截?cái)嗷蛏崛脲e(cuò)誤。通過(guò)將數(shù)據(jù)以字符串抽出,PDO 為您提供了一些腳本控制,您可以使用普通的 php 類(lèi)型轉(zhuǎn)換工具(如數(shù)學(xué)運(yùn)算過(guò)程中的轉(zhuǎn)換和隱式)來(lái)控制如何進(jìn)行轉(zhuǎn)換以及何時(shí)進(jìn)行轉(zhuǎn)換。


NULL

如果結(jié)果集中的某列包含一個(gè) NULL 值,PDO 則會(huì)將其映射為 php null 值。Oracle 在將數(shù)據(jù)返回 PDO 時(shí)會(huì)將空字符串轉(zhuǎn)換為 NULL,但是 php 支持的任何其他數(shù)據(jù)庫(kù)都不會(huì)這樣處理,從而導(dǎo)致了可移植性問(wèn)題。PDO 提供了一個(gè)驅(qū)動(dòng)程序級(jí)屬性 PDO_ATTR_ORACLE_NULLS,該屬性會(huì)為其他數(shù)據(jù)驅(qū)動(dòng)程序模擬此行為:


$dbh = new PDO('OCI:', 'scott', 'tiger');
$dbh->setAttribute(PDO_ATTR_ORACLE_NULLS, true);
// 現(xiàn)在從此 $dbh 打開(kāi)的任何語(yǔ)句中的
// 空字符串都將被轉(zhuǎn)換為 NULL

 

POD 的現(xiàn)狀和未來(lái)


PDO 現(xiàn)在仍相當(dāng)不成熟,但是會(huì)快速成熟起來(lái)。在編寫(xiě)本文之時(shí),我在本文中提到的任何內(nèi)容都能夠通過(guò) PDO_OCI 驅(qū)動(dòng)程序適用于 Oracle 8 或更高版本(在 Oracle 8.0 和 9.2 上測(cè)試過(guò))。

已經(jīng)計(jì)劃增加以下主要特性,在不久將可以使用:

1.使用 php 流的 LOB 支持。 使用綁定參數(shù),您能夠?qū)⑷魏瘟髻Y源(如文件、套接字、HTTP 資源、壓縮/篩選的流)作為輸入或輸出參數(shù)傳遞到在 LOB 上運(yùn)行的查詢中。與之相似,類(lèi)型為 LOB 的輸出參數(shù)將表現(xiàn)為 php 流,因此您可以使用 fread()、fwrite()、fseek() 和其他流函數(shù)來(lái)訪問(wèn)這些參數(shù)。此時(shí),在 PDO 中根本沒(méi)有 LOB 支持。
2.持久性連接和緩存的預(yù)處理語(yǔ)句。 持久性連接使您能夠避免在每個(gè)頁(yè)面命中時(shí)打開(kāi)和關(guān)閉數(shù)據(jù)庫(kù)服務(wù)器連接。緩存的預(yù)處理語(yǔ)句又前進(jìn)了一步,它使您能夠持久保持查詢的預(yù)處理版本以及數(shù)據(jù)庫(kù)句柄。
3.游標(biāo)。 目前,PDO 只提供前向只讀游標(biāo),但是將來(lái)會(huì)提供可滾動(dòng)游標(biāo)(需要基礎(chǔ)驅(qū)動(dòng)程序支持)、REF-CURSOR、使用游標(biāo)進(jìn)行定位更新,以及可更新滾動(dòng)游標(biāo)。

我們希望在 php 5.1 中默認(rèn)啟用 php 擴(kuò)展(距此目標(biāo)尚遠(yuǎn)),但是在此之前,我們希望能讓 PDO 在 php 5.0 發(fā)布時(shí)穩(wěn)定運(yùn)行,但是我們?nèi)粘9ぷ髦械膲毫ι陨酝涎恿诉@些工作。同時(shí),通過(guò) PECL 發(fā)布 PDO 使我們能夠在收到問(wèn)題報(bào)告時(shí)做出回應(yīng),并根據(jù)不同于 php 5.0 發(fā)布時(shí)間表的時(shí)間表發(fā)布修復(fù)版本,因此您在 php 5.1 發(fā)布前即可使用 PDO。

我們需要您的反饋

如果您試用了 PDO,并且發(fā)現(xiàn)了問(wèn)題,請(qǐng)務(wù)必使用我們的錯(cuò)誤跟蹤軟件將其報(bào)告給我們。如果您使用的是 Oracle 驅(qū)動(dòng)程序,則請(qǐng)使用此頁(yè):

http://pecl.php.NET/bugs/report.php?package=PDO_OCI

如果您使用的是其他驅(qū)動(dòng)程序,則請(qǐng)用其名稱(chēng)替換該 URL 中 PDO_OCI。

如果您使用 PDO 時(shí)遇到問(wèn)題,或者針對(duì)某些特性存在疑問(wèn),或者具有特性請(qǐng)求,請(qǐng)聯(lián)系 pecl-dev@lists.php.NET。如果您愿意,當(dāng)然還可以直接聯(lián)系我 (wez@php.NET),但是請(qǐng)注意,我每天都會(huì)收到大量有關(guān) php電子郵件;您可能會(huì)發(fā)現(xiàn)如果首先與前面的郵件列表聯(lián)系會(huì)更快得到答復(fù)。

-----------
關(guān)于作者
Wez Furlong 是 Brain Room Ltd. 的技術(shù)總監(jiān),他在該公司不但使用 php 用于 Web 開(kāi)發(fā),還將其用作 Linux 和 Windows 應(yīng)用程序和系統(tǒng)的嵌入式腳本引擎。Wez 是 php 的核心開(kāi)發(fā)人員,經(jīng)常向 SQLite、COM/.NET、Activephp、mailparse 和 Streams API 等投稿,他是 PECL 即 php 擴(kuò)展社區(qū)庫(kù)的“頭兒”。他的咨詢公司的網(wǎng)頁(yè)為 http://www.thebrainroom.NET

php技術(shù)PHP 5 數(shù)據(jù)對(duì)象 (PDO) 抽象層與 Oracle,轉(zhuǎn)載需保留來(lái)源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 免费色播 | 亚洲免费小视频 | 伊人久久大香线蕉综合爱婷婷 | 男女男精品视频网站 | 色多多a| 国产精品成人免费视频99 | 久久久久久亚洲精品中文字幕 | 国产午夜免费视频片夜色 | 亚洲图片视频在线 | 91aaa免费观看在线观看资源 | 色综合五月| 国产美乳在线观看 | 久久国产视频网站 | 久久国产香蕉一区精品 | 欧美 亚洲 综合 卡通 另类 区 | 久久aa| 亚洲成a人片777777久久 | 精品国产一区二区三区在线观看 | 婷婷亚洲视频 | 成人午夜毛片 | 性欧美4k高清精品 | 久久五月激情婷婷日韩 | 欧美午夜a级精美理论片 | 亚洲综合久 | 日本免费视屏 | 伊人第四色 | 精品国产成人 | 在线观看色视频网站 | 一区二区免费视频观看 | 91国内揄拍国内精品对白 | 全国最大色成免费网站 | 美女又美女又黄又免费网站 | 精品无人区乱码麻豆1区2区 | 日韩亚洲国产综合久久久 | 婷婷在线观看视频 | 中文字幕第二一区 | 精品视频免费在线观看 | 久久国产加勒比精品无码 | 国内视频一区二区 | 国产麻豆免费观看91 | 色婷婷综合缴情综六月 |