亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

Chinaunix

標題: [收藏] PHP通過PDO鏈接ODBC [打印本頁]

作者: 會游泳的魚    時間: 2008-12-09 12:03
標題: [收藏] PHP通過PDO鏈接ODBC
引用地址: 
http://bbs.3qit.com/thread-57756-1-1.html
PHP 通過PDO鏈接ODBCPHP 5.1 發(fā)布時將附帶一個全新的數(shù)據(jù)庫連接層,即 PHP Data Objects (PDO)。雖然 PHP 一直都擁有很好的數(shù)據(jù)庫連接,但 PDO 讓 PHP 達到一個新的高度。學習如何獲得、安裝和使用 PDO,以連接到 IBM® DB2® Universal Database™ 和 IBM Cloudscape™ 數(shù)據(jù)庫,插入和檢索數(shù)據(jù),并探索更多高級特性,例如:[color="#ff0000"]預(yù)處理語句(prepared statements)、綁定參數(shù)(bound parameters)、可滾動游標(scrollable cursors)、定位更新(positioned updates)以及 LOB。另外,本文將簡要地介紹一下對多字節(jié)數(shù)據(jù)的處理。 [color="#000080"]背景隨著擁有更成熟 OO 語法的 PHP 5 的發(fā)布,PHP 越來越多地受到越來越大的機構(gòu)的關(guān)注,對于 PHP 來說,提供更加一致的和可訪問的數(shù)據(jù)訪問 API 變得越來越重要。PHP 與流行的開放源代碼關(guān)系數(shù)據(jù)庫管理系統(tǒng)(RDBMS)MySQL 之間總是很有默契。這對拍檔的成功很大程度上是由于它們免費可用,而且進入的門檻也比較低,這兩種產(chǎn)品的合作使它們各自都取得了廣受推崇的地位。很多 PHP 應(yīng)用程序開發(fā)人員都習慣于 PHP-MySQL 這對組合,以致 PHP 對其他數(shù)據(jù)庫的支持常常模仿 MySQL 客戶機庫 API。然而,并不是所有的數(shù)據(jù)庫客戶機 API 都是一樣的,也不是所有的數(shù)據(jù)庫都提供相同的特性。雖然存在模仿,但不同的 PHP 數(shù)據(jù)庫擴展都有它們各自的怪僻和不同之處,所以從一種數(shù)據(jù)庫遷移到另一種數(shù)據(jù)庫時會有一些困難。雖然這不是創(chuàng)建 PDO 的直接原因,但是在設(shè)計過程中還是有一定影響的。如果您是帶著想結(jié)合使用 PHP 和 DB2 的目的閱讀本文,那么您很可能屬于以下類型中的一種:您從一家小公司開始,在 MySQL(舉個例子)上運行 PHP,由于業(yè)務(wù)增長,您需要 DB2 所提供的可伸縮性/可靠性/支持或其他特性。您希望移植代碼,以使用 DB2,但由于 API 的變化,您需要編寫或?qū)崿F(xiàn)一個抽象層,以便在 DB2 上測試應(yīng)用程序的同時可以繼續(xù)在舊的數(shù)據(jù)庫上運行。不僅如此,您還希望能有自己的選擇,并保留支持其他 RDBMS 的可能性,因為您清楚,有些客戶機可能已經(jīng)和其他平臺栓在一起了。 您用 PHP 在 MySQL之上構(gòu)建了一個小型的部門應(yīng)用程序(同樣,這只是舉個例子,我并不是要跟 MySQL 過不去)。事實證明這個應(yīng)用程序本身很有用,現(xiàn)在已經(jīng)在這個部門之外使用,并且闖入了 CIO/CTO 的法眼 —— 現(xiàn)在需要遵從托管的標準數(shù)據(jù)庫。(是的,這是第一點的一個變種。) 在其他某些復(fù)雜的企業(yè)級應(yīng)用程序的后臺,您已經(jīng)有一個 DB2 實例;您希望利用 PHP 的快速應(yīng)用程序開發(fā)和原型設(shè)計來生成動態(tài)報告。[color="#003366"]目標至此我們已經(jīng)掌握了數(shù)據(jù)庫及 PHP 的背景知識,現(xiàn)在正好可以提及 PDO 背后的一些設(shè)計目標:為大多數(shù)數(shù)據(jù)庫 API 中的常見特性提供一致的 API。 具有可擴展性,以使數(shù)據(jù)庫供應(yīng)商 X 仍然可以暴露特性 Y 并保持 PDO 的兼容性。 提供大量基本的兼容性技巧,以便能夠更方便地創(chuàng)建跨數(shù)據(jù)庫兼容的應(yīng)用程序。 不為給定數(shù)據(jù)庫 API 中本來沒有的特性(例如序列)提供完全抽象或仿真。PDO 類意圖為您提供對數(shù)據(jù)庫本地特性的一致性訪問,并減少干擾。 通過將與 PHP 內(nèi)部打交道的代碼(這是最難于編寫的部分)集中起來,簡化 PHP 數(shù)據(jù)庫驅(qū)動程序的創(chuàng)建。 最后一點非常重要。PDO 是模塊化結(jié)構(gòu),它被分成一個公共核心以及一個或多個驅(qū)動程序擴展,公共核心提供了在腳本(PDO 本身)中使用的 API,驅(qū)動程序擴展則為 PDO 和本地 RDBMS 客戶機 API 庫架起一座橋梁。DB2 用戶將會希望使用 PDO_ODBC 驅(qū)動程序,據(jù)稱它可以提供以下特性: 它經(jīng)過重新編寫,能支持遵從 ODBC V3 的驅(qū)動程序和驅(qū)動程序管理器。它還考慮了對 DB2 特定特性和優(yōu)化的支持,這成為設(shè)計過程中的一部分 —— 不是后來補充的。 它支持經(jīng)過試驗和測試的存儲過程和大型對象。它不僅能夠工作,而且非常好用。 對于取 10,000 行記錄這樣的 DB2 訪問操作,使用 PDO_ODBC 驅(qū)動程序時的性能比使用傳統(tǒng)的 PHP Unified ODBC 擴展要快大約 10 倍。之所以有這么大的差異,是因為在 PDO 中默認的游標是輕量級的只能向前移動的游標。 [color="#003366"]獲取和安裝 PDOPHP 5.1 發(fā)布時將附帶 PDO,但是也可以通過 PECL 這個 PHP 擴展庫(PHP Extension Repository)來結(jié)合使用 PDO 和 PHP 5.0.3 及以上版本。如果您使用的是 Windows®,那么您會欣喜地發(fā)現(xiàn)安裝過程要簡單得多。 我將假設(shè)您已經(jīng)擁有配置 PHP 5 使之使用您選擇的 Web 服務(wù)器的經(jīng)驗,只有在此假設(shè)下,我才能集中精力關(guān)注更相關(guān)的細節(jié)。同樣,我還將假設(shè)您使用的是一個 DB2 Universal Database 服務(wù)器或網(wǎng)絡(luò)服務(wù)器模式下的 IBM Cloudscape 數(shù)據(jù)庫,并且接受了用戶為 db2inst1、密碼為 ibmdb2 的默認安裝選項。如果您自己編譯驅(qū)動程序,那么在進行編譯的機器上,應(yīng)該安裝有 DB2 客戶機,并且存在應(yīng)用程序開發(fā) header,否則編譯將遭到失敗。在 PHP 5.0.3 及以上版本上通過 PECL 進行安裝默認情況下,PHP 將安裝 "PEAR" 包管理系統(tǒng)。您選擇的 OS 發(fā)行版很可能已經(jīng)創(chuàng)建了一個包含 PEAR 的組件的包,很可能您已經(jīng)安裝了這個包,并準備運行它。讓我們試驗一下。 如果它不能工作如果您沒能看到類似于左側(cè)文本的輸出,那么很可能您沒有安裝需要的所有包。這時應(yīng)查閱您選擇的 OS 發(fā)行版的文檔,看看接下來應(yīng)該做什么;蛘撸梢宰约壕幾g PHP。清單 1. 列出已安裝的 PEAR 包$ pear list            Installed packages:            ===================            Package        Version State            Archive_Tar    1.1     stable            Console_Getopt 1.2     stable            PEAR           1.3.4   stable            XML_RPC        1.1.0   stable            這個包列表表明,我已經(jīng)安裝了 PEAR 1.3.4。很可能您也會安裝那個版本。為了成功地安裝 PDO,需要升級到 PEAR 1.3.5;這個過程很快,很順利:$ sudo pear upgrade PEAR現(xiàn)在便可以放心安裝 PDO 了:$ sudo pear install PDO您已經(jīng)安裝了 PDO 核心,為了使之生效,需要在 php.ini 文件中啟用它。您需要添加以下一行: extension=pdo.so            現(xiàn)在安裝用于 PDO 的 ODBC 驅(qū)動程序,如果您需要連接到 DB2、Cloudscape 或 Apache Derby,就需要這個驅(qū)動程序: $ sudo pear install PDO_ODBC您將看到這樣的提示:flavour,dir ? (just leave blank for help)。這是一個稍微有點隱蔽的提示,它詢問需要配置哪種類型的 ODBC 驅(qū)動程序,以及將它安裝在哪里。如果在安裝 DB2 時選擇了默認安裝選項,那么可以輸入 ibm-db2,這相當于 ibm-db2,/home/db2inst1/sqllib。如果您選擇了不同的安裝位置,那么應(yīng)該用它置替換 /home/db2inst1/sqllib。輸入了正確的細節(jié)后,按下 enter 鍵,這樣驅(qū)動程序就會構(gòu)建和安裝。 您需要將驅(qū)動程序添加到 php.ini 文件中,從而激活驅(qū)動程序。確保將下面這一行添加在之前所添加的 pdo.so 這一行之后,否則 PHP 不能正確地初始化。 extension=pdo_odbc.so            PHP 5.1 及以上版本上的安裝PHP 5.1 發(fā)布時附帶了 PDO。為獲得 DB2 支持,只需將下面的開關(guān)添加到配置行。您顯然希望添加更多的配置選項,以滿足您自己的 Web 服務(wù)器。關(guān)于這方面的詳細內(nèi)容,可以查看 PHP 文檔。我將假設(shè)您使用的是一個最近的 Linux® 發(fā)行版,并運行 Apache 2: $ tar xjf php-5.1.0.tar.bz2            $ cd php-5.1.0            $ ./configure --with-pdo-odbc=ibm-db2,/home/db2inst1/sqllib \            --with-apxs2=/usr/sbin/apxs            $ make            $ sudo make install            [color="#003366"]Windows 上的安裝Windows 上的安裝比起 UNIX® 上的安裝來要簡單一些。如果您下載了 PHP 5.1,那么就已經(jīng)擁有了這個包中相關(guān)的 DLL。否則,您需要從 PHP 快照站點(請參閱本文后面的“下載”小節(jié))下載這些 DLL。 為了激活 PDO,將下面兩行添加到 php.ini 文件: extension=php_pdo.dll            extension=php_pdo_odbc.dll            重新啟動 Web 服務(wù)器安裝完畢后,應(yīng)該完全重新啟動 Web 服務(wù)器,以確保 PHP 裝載新的擴展,這樣就可以開始使用 PDO 了。如果您使用的是 UNIX 平臺,那么需要獲得 DB2 客戶機的 DB2 實例環(huán)境,以便正確地初始化。如果您使用的是 bourne shell 型的 shell,那么可以通過運行命令 . /home/db2inst1/sqllib/db2profile 來獲得。(注意: 假定開頭部分的句號已經(jīng)在那里!)您需要作出安排,使之在 Web 服務(wù)器啟動腳本中自動發(fā)生。 PDO 中的關(guān)鍵概念為了掌握 PDO,需要了解 5 個關(guān)鍵概念。這 5 個概念是: 連接和連接管理 事務(wù)和自動提交 預(yù)處理語句和存儲過程 錯誤和錯誤處理 特定于驅(qū)動程序的功能性,包括 滾動游標 和 大型對象連接和連接管理連接是通過創(chuàng)建 PDO 基類的實例而建立的。不管您想要使用哪種驅(qū)動程序,您總是使用 PDO 類名。構(gòu)造函數(shù)接受用于指定數(shù)據(jù)源(即 DSN)的參數(shù),可能還包括用戶名和密碼參數(shù)(如果有的話)。最后一個參數(shù)用于傳遞附加的調(diào)優(yōu)參數(shù)到 PDO 或底層驅(qū)動程序 —— 后面很快會有更詳細的論述。下面是一個簡短的連接到 DB2 的示例腳本:
清單 2. 如何使用 PDO 連接到 DB2try {            $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');            echo "Connected\n";            } catch (Exception $e) {            echo "Failed: " . $e->getMessage();            }odbc:SAMPLE 告訴 PDO 它應(yīng)該使用 ODBC 驅(qū)動程序,并且應(yīng)該使用 "SAMPLE" 數(shù)據(jù)庫。如果使用一個驅(qū)動程序管理器,那么可以用一個 ODBC 級數(shù)據(jù)源名稱替代 SAMPLE。實際上,在冒號字符之后可以指定任何有效的 ODBC 數(shù)據(jù)源連接字符串。 如果連接成功,您將看到消息 "Connected",否則,PDO 將拋出一個 PDOException,解釋為什么連接失敗?赡艿脑虬o效的參數(shù),不正確的用戶/密碼,甚至是您忘了裝載驅(qū)動程序。 值得注意的是,除非您捕捉從構(gòu)造函數(shù)拋出的異常,否則,如果 PHP 腳本未能連接到數(shù)據(jù)庫,它將終止。這與傳統(tǒng)的 PHP 數(shù)據(jù)庫擴展有很大的不同。對于不喜歡異常的人來說,只有兩個“硬故障(hard-failure)”點可能拋出異常,這是其中一個點(另一個地點是,當您 試圖使用事務(wù)時缺乏對事務(wù)的支持)。對于所有其他錯誤,PDO 將使用您選擇的 錯誤處理設(shè)置。 連接將保持開放狀態(tài),直到所有對它的引用被釋放。如果在主腳本的頂端打開連接, 并將其句柄存儲在一個全局變量中,那么該連接將一直處于開放狀態(tài),直到腳本結(jié)束,或者直到 $dbh 變量被設(shè)為 null。如果在一個函數(shù)中打開連接,并且只將句柄存儲在一個本地變量中,那么當函數(shù)返回時,連接將被關(guān)閉。這些語義對于 PHP 中的任何對象都是一樣的,沒有什么特別的地方。 ODBC 連接池如果您使用的是 Windows,或者如果您選擇在 UNIX 型平臺上使用一個 ODBC 驅(qū)動程序管理器,那么值得注意的是,PDO_ODBC 將自動嘗試使用該驅(qū)動程序管理器的 ODBC 連接池特性。這個特性類似于 PHP 級連接緩存,不要求專門請求一個持久的連接。此外,緩存是在 ODBC 級進行的,這意味著在同一個進程中運行的其他組件(例如在 IIS 下運行的 ASP/.Net 腳本)也能利用相同的連接池。 對于流量較大的站點,讓 PHP 在不同請求的間隙中緩存打開的連接,使得每個進程(每個惟一的連接參數(shù)集)只需花費一次建立連接的成本,這樣做常常很有益處。雖然這聽起來像是一個不錯的 想法,但您應(yīng)該仔細評估這樣做對系統(tǒng)的影響,因為當大量緩存的連接空閑在那里的時候,就會適得其反。 要建立一個緩存的連接(如果您更熟悉傳統(tǒng)的數(shù)據(jù)庫擴展的話,也可以說是 *pconnect()),需要在實例化數(shù)據(jù)庫連接時傳遞一個屬性:清單 3. 如何用 PDO 連接到 DB2,使用持久(緩存)連接try {            $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2',            array(PDO_ATTR_PERSISTENT => true));            echo "Connected\n";            } catch (Exception $e) {            echo "Failed: " . $e->getMessage();            }            事務(wù)和自動提交至此,您已經(jīng)通過 PDO 連接到了 DB2,在發(fā)出查詢之前,您應(yīng)該理解 PDO 是如何管理事務(wù)的。如果之前沒有接觸過事務(wù),那么首先要知道事務(wù)的 4 個特征:原子性(Atomicity)、一致性(Consistency)、獨立性(Isolation)和持久性(Durability),即 ACID。用外行人的話說,對于在一個事務(wù)中執(zhí)行的任何工作,即使它是分階段執(zhí)行的,也一定可以保證該工作會安全地應(yīng)用于數(shù)據(jù)庫,并且在工作被提交時,不 會受到來自其他連接的影響。事務(wù)性工作可以根據(jù)請求自動撤銷(假設(shè)您還沒有提交它),這使得腳本中的錯誤處理變得更加容易。 事務(wù)通常是通過把一批更改積蓄起來、使之同時生效而實現(xiàn)的。這樣做的好處是可以大大提高這些更新的效率。換句話說,事務(wù)可以使腳本更快,而且可能更健壯(不過需要正確地使用事務(wù)才能獲得這樣的好處)。 警告只有在通過 PDO::beginTransaction() 啟動事務(wù)的情況下,才會發(fā)生自動回滾。如果手動地發(fā)出開始一個事務(wù)的查詢,那么 PDO 就無法知道該事務(wù),從而不能在必要時進行回滾。 不幸的是,并不是每種數(shù)據(jù)庫都支持事務(wù),所以當?shù)谝淮未蜷_連接時,PDO 需要在所謂的“自動提交(auto-commit)”模式下運行。自動提交模式意味著,如果數(shù)據(jù)庫支持事務(wù),那么您所運行的每一個查詢都有它自己的隱式事 務(wù),如果數(shù)據(jù)庫不支持事務(wù),每個查詢就沒有這樣的事務(wù)。如果您需要一個事務(wù),那么必須使用 PDO::beginTransaction() 方法來啟動一個事務(wù)。如果底層驅(qū)動程序不支持事務(wù),那么將會拋出一個 PDOException(無論錯誤處理設(shè)置是怎樣的:這總是一個嚴重錯誤狀態(tài))。在一個事務(wù)中,可以使用 PDO::commit() 或 PDO::rollBack() 來結(jié)束該事務(wù),這取決于事務(wù)中運行的代碼是否成功。 DB2 特性雖然我認為事務(wù)通常要更快一些,但您還是應(yīng)該自己評估事務(wù)是否真的可以加快代碼。例如,在高并發(fā)環(huán)境中您可能會發(fā)現(xiàn),過度使用事務(wù)會增加鎖開銷。如果在應(yīng) 用程序中出現(xiàn)這種情況,那么建議的補救辦法是在一般情況下使用自動提交,而對于真正需要全部 ACID 特征的代碼部分則仍然使用事務(wù)。 當腳本結(jié)束時,或者當一個連接即將被關(guān)閉時,如果有一個未完成的事務(wù),那么 PDO 將自動回滾該事務(wù)。這是一種安全措施,有助于避免在腳本非正常結(jié)束時出現(xiàn)不一致的情況 —— 如果沒有顯式地提交事務(wù),那么假設(shè)有某個地方會出現(xiàn)不一致,所以要執(zhí)行回滾,以保證數(shù)據(jù)的安全性。 清單 4. 在事務(wù)中執(zhí)行批處理            try {            $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2',            array(PDO_ATTR_PERSISTENT => true));            echo "Connected\n";            $dbh->setAttribute(PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION);            $dbh->beginTransaction();            $dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')");            $dbh->exec("insert into salarychange (id, amount, changedate)            values (23, 50000, NOW())");            $dbh->commit();            } catch (Exception $e) {            $dbh->rollBack();            echo "Failed: " . $e->getMessage();            }            在上面的示例中,假設(shè)我們?yōu)橐粋新雇員創(chuàng)建一組條目,這個雇員有一個 ID 號,即 23。除了輸入這個人的基本數(shù)據(jù)外,我們還需要記錄雇員的薪水。兩個更新分別完成起來很簡單,但通過將這兩個更新包括在 beginTransaction() 和 commit() 調(diào)用中,就可以保證在更改完成之前,其他人無法看到更改。如果發(fā)生了錯誤,catch 塊可以回滾事務(wù)開始以來發(fā)生的所有更改,并打印出一條錯誤消息。 并不是一定要在事務(wù)中作出更新。您也可以發(fā)出復(fù)雜的查詢來提取數(shù)據(jù),還可以使用那種信息構(gòu)建更多的更新和查詢。當事務(wù)在活動時,可以保證其他人在工作進行當中無法作出更改。事實上,這不是 100% 的正確,但如果您之前沒有聽說過事務(wù)的話,這樣介紹也未嘗不可。
關(guān)于 PHP 應(yīng)用程序中安全性的說明PHP Security Consortium雖然本文表明在使用 PDO 時不再需要引用輸入,但這不是說您應(yīng)該盲目地使數(shù)據(jù)通過數(shù)據(jù)庫。XSS 攻擊是很實際的危險。您應(yīng)該總是確保對傳入應(yīng)用程序的不受信任的數(shù)據(jù)應(yīng)用適當?shù)倪^濾器,并采取措施避免讓不受信任的數(shù)據(jù)在站點上發(fā)出 HTML 或 javascript。 請訪問 The PHP Security Consortium 以了解關(guān)于這些危險的更多知識,以及應(yīng)該如何避免這些危險。 很多 PHP 腳本中一個常見的缺陷是缺乏輸入檢驗。這種缺陷可以被利用,從而招致 XSS(Cross Site Scripting)以及 SQL 入侵攻擊。在 SQL 入侵中,不受信任的數(shù)據(jù)(例如發(fā)給 Web 網(wǎng)頁的反饋)和其他文本被銜接在一起,構(gòu)成一個查詢。攻擊者可以蓄意地安排他們的輸入,使之溢出引號之外,并在您想運行的真正查詢后面鏈接上任意一個查詢。這種攻擊使攻擊者可以更新、插入或刪除數(shù)據(jù),甚至可能可以看到數(shù)據(jù)庫中的任意信息。 XSS 也是一個類似的問題。不過這一次不受信任的數(shù)據(jù)瞄準的是瀏覽站點的人們,而不是應(yīng)用程序本身。通過提交包含 HTML 或 javascript 組合的文本,攻擊者期望您之后會將那種數(shù)據(jù)直接輸出到其他訪問站點的人那里,從而使惡意代碼可以在站點訪問者的瀏覽器上運行。 在編寫應(yīng)用程序時,需要同時考慮這兩種攻擊。如果小心地檢驗和過濾輸入,這兩種攻擊都是可以防止的。對 XSS 的處理很有技巧性,所以在這里我不便多講(不過可以從側(cè)欄找到有用的參考資料)。相比之下,SQL 入侵更容易對付。您只需在構(gòu)造查詢之前,適當?shù)嘏懦繅K不受信任的數(shù)據(jù)。這種事情有點煩雜,特別是當您有大量的字段要處理時,很容易忘記做這件事。 雖然這是有用的(并且也是重要的)信息,但是您可能想知道,為什么我要花時間提到這一點,本文的重點不是結(jié)合使用 PDO 和 DB2 嗎?原因是這樣的:PHP 現(xiàn)在得到很廣泛的部署,自然地,大量流行的基于 PHP 的應(yīng)用程序也得到了廣泛的部署。每當某一種這樣的應(yīng)用程序(和 PHP 本身沒有聯(lián)系)被發(fā)現(xiàn)存在漏洞時,PHP 常常被誤認為是不安全的,可被利用的或者有缺陷的。為了避免將來出現(xiàn)這樣的情況,我們可以采取的一個措施是鼓勵應(yīng)用程序開發(fā)人員多考慮安全問題,從而減少由誠實的錯誤導(dǎo)致的損害。扯遠了,下面繼續(xù)介紹其他關(guān)鍵概念。 預(yù)處理語句和存儲過程很多更成熟的數(shù)據(jù)庫都支持預(yù)處理語句的概念。什么是預(yù)處理語句?您可以把預(yù)處理語句看作您想要運行的 SQL 的一種編譯過的模板,它可以使用變量參數(shù)進行定制。預(yù)處理語句可以帶來兩大好處: 查詢只需解析(或準備)一次,但是可以用相同或不同的參數(shù)執(zhí)行多次。當查詢準備好后,數(shù)據(jù)庫將分析、編譯和優(yōu)化執(zhí)行該查詢的計劃。對于復(fù)雜的查詢,這個過程要花比較長的時間,如果您需要以不同參數(shù)多次重復(fù)相同的查詢,那么該過程將大大降低應(yīng)用程序的速度。通過使用預(yù)處理語句,可以避免重復(fù)分析/編譯/優(yōu)化周期。簡言之,預(yù)處理語句使用更少的資源,因而運行得更快。 提供給預(yù)處理語句的參數(shù)不需要用引號括起來,驅(qū)動程序會處理這些。如果應(yīng)用程序獨占地使用預(yù)處理語句,那么可以確保沒有 SQL 入侵發(fā)生。(然而,如果您仍然將查詢的其他部分建立在不受信任的輸入之上,那么就仍然存在風險)。 預(yù)處理語句是如此有用,以致 PDO 實際上打破了在目標 4 中設(shè)下的規(guī)則:如果驅(qū)動程序不支持預(yù)處理語句,那么 PDO 將仿真預(yù)處理語句。 下面是使用預(yù)處理語句的兩個例子。第一個例子 通過替換指定占位符的 name 和 value,執(zhí)行一次插入。而 第二個例子 使用問號占位符執(zhí)行一條 select 語句。 清單 4. 使用預(yù)處理語句的重復(fù)插入            $stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");            $stmt->bindParam(':name', $name);            $stmt->bindParam(':value', $value);            // insert one row            $name = 'one';            $value = 1;            $stmt->execute();            // insert another row with different values            $name = 'two';            $value = 2;            $stmt->execute();            清單 5. 使用預(yù)處理語句取數(shù)據(jù)$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");            if ($stmt->execute(array('one'))) {            while ($row = $stmt->fetch()) {            print_r($row);            }            }            如果數(shù)據(jù)庫驅(qū)動程序支持,您還可以綁定輸出和輸入?yún)?shù)。輸出參數(shù)通常用于從存儲過程獲取值。輸出參數(shù)使用起來比輸入?yún)?shù)要復(fù)雜一些,當綁定一個給定的輸出參數(shù)時,必須知道該參數(shù)的長度。如果為參數(shù)綁定的值大于您建議的長度,那么就會產(chǎn)生錯誤。 清單 6. 帶輸出參數(shù)調(diào)用存儲過程$stmt = $dbh->prepare("CALL sp_returns_string(?)");            $stmt->bindParam(1, $return_value, PDO_PARAM_STR, 4000);            // call the stored procedure            $stmt->execute();            print "procedure returned $return_value\n";            您還可以指定同時具有輸入和輸出值的參數(shù),其語法類似于輸出參數(shù)。在接下來的例子中,字符串 'hello' 被傳遞給存儲過程,當存儲過程返回時,hello 被替換為該存儲過程返回的值。 清單 7. 帶輸入/輸出參數(shù)調(diào)用存儲過程$stmt = $dbh->prepare("CALL sp_takes_string_returns_string(?)");            $value = 'hello';            $stmt->bindParam(1, $value, PDO_PARAM_STR|PDO_PARAM_INPUT_OUTPUT, 4000);            // call the stored procedure            $stmt->execute();            print "procedure returned $value\n";            錯誤和錯誤處理PDO 提供了 3 種不同的錯誤處理模式,以滿足不同風格的編程: PDO_ERRMODE_SILENT 這是默認模式。PDO 將只設(shè)置錯誤代碼,以通過 errorCode() 和 errorInfo() 方法對語句和數(shù)據(jù)庫對象進行檢查。如果錯誤是由于對語句對象的調(diào)用而產(chǎn)生的,那么可以在那個對象上調(diào)用 errorCode() 或 errorInfo() 方法。如果錯誤是由于調(diào)用數(shù)據(jù)庫對象而產(chǎn)生的,那么可以在那個數(shù)據(jù)庫對象上調(diào)用上述兩個方法。 PDO_ERRMODE_WARNING 除了設(shè)置錯誤代碼以外,PDO 還將發(fā)出一條傳統(tǒng)的 E_WARNING 消息。如果您只是想看看發(fā)生了什么問題,而無意中斷應(yīng)用程序的流程,那么在調(diào)試/測試當中這種設(shè)置很有用。 PDO_ERRMODE_EXCEPTION 除了設(shè)置錯誤代碼以外,PDO 還將拋出一個 PDOException,并設(shè)置其屬性,以反映錯誤代碼和錯誤信息。這種設(shè)置在調(diào)試當中也很有用,因為它會放大腳本中產(chǎn)生錯誤的地方,從而可以非常快速地指出代碼中有問題的潛在區(qū)域(記住,如果異常導(dǎo)致腳本終止,則事務(wù)將自動回滾)。 異常模式另一個有用的地方是,與傳統(tǒng)的 PHP 風格的警告相比,您可以更清晰地構(gòu)造自己的錯誤處理,而且,比起以靜寂方式以及顯式地檢查每個數(shù)據(jù)庫調(diào)用的返回值,異常模式需要的代碼/嵌套也更少。 PDO 定制了使用 SQL-92 SQLSTATE 錯誤代碼字符串的標準;不同 PDO 驅(qū)動程序負責將它們本地代碼映射為適當?shù)?SQLSTATE 代碼。例如,SQLSTATE 是用于 DB2(以及通常的 ODBC)的本地錯誤代碼格式,這是多么方便。rrorCode() 方法返回一個 SQLSTATE 代碼。如果您需要關(guān)于一個錯誤的更多特定的信息,PDO 還提供了一個 errorInfo() 方法,該方法將返回一個數(shù)組,其中包含 SQLSTATE 代碼、特定于驅(qū)動程序的錯誤代碼以及特定于驅(qū)動程序的錯誤字符串。 分頁數(shù)據(jù)、滾動游標和定位更新在 Web 應(yīng)用程序中,一種常見的范例是對查詢結(jié)果進行分頁。如果您使用一個 Internet 搜索引擎,那么很可能每天都會做這樣的事。您輸入搜索詞,然后得到前 10-20 個匹配項。如果您想看到更多搜索結(jié)果,可以單擊 "next page" 鏈接。如果想回頭看前面看過的結(jié)果,可以單擊 "previous page" 鏈接。記得在幾年前,當我第一次在 Web 上使用這樣的東西時,我對自己說:“為什么我不能通過滾動查看所有數(shù)據(jù)呢?” 問題的答案說簡單也簡單,說復(fù)雜也復(fù)雜 —— 我只想說,HTTP 不會智能地使數(shù)據(jù)庫上的可滾動游標一直處于開放狀態(tài),即便如此,需要大量傳輸?shù)?Web 應(yīng)用程序也會很快地消耗掉大量開放的可滾動游標。因此,最簡單的解決方案是為用戶顯示所有的匹配項 —— 但是用戶很容易迷失在大量的結(jié)果當中。比較符合邏輯的措施是人工地將數(shù)據(jù)格式化到多個頁面上,使用戶可以每次查看一部分可以管理的數(shù)據(jù)。 所以人們編寫可以取所有數(shù)據(jù)的 PHP 應(yīng)用程序,然后只顯示前 10 行。根據(jù)下一次請求,應(yīng)用程序又顯示 11-20 行,依此類推。這對于只返回少量數(shù)據(jù)的查詢來說很不錯,但是,如果有很多匹配項(比如多于 100),那么先取全部數(shù)據(jù)然后丟棄其中的 90%,這種做法很浪費。PHP 的創(chuàng)始人 Rasmus Lerdorf 就這種情形特地為 MySQL 發(fā)明了一個特殊的 "LIMIT, OFFSET" 子句。它允許您通知數(shù)據(jù)庫,您只對一小部分行感興趣,這樣它就不會取其他不需要的行了。其語法(或非常類似的東西)已經(jīng)被其他流行的開放源代碼數(shù)據(jù)庫采納,但并不是所有數(shù)據(jù)庫都提供了相同的語法。 Troels Arvin 收集了一些非常有用的信息,對不同 RDBMS 所支持的語法進行了比較。 如果您想在以 DB2 為后臺數(shù)據(jù)庫的 PHP 應(yīng)用程序中實現(xiàn)分頁結(jié)果,那么可以(也應(yīng)該)使用下面示例中的語法。這里我們假設(shè)有一個 books 表,表中包含書名和作者,我們現(xiàn)在想要每次在一頁中顯示 10 個以上結(jié)果: 清單 8. 使用 SQL Standard "Window Functions" 實現(xiàn)數(shù)據(jù)分頁            $db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');            // the offset is passed in from the user when they click on a link            // this cast to integer ensures that no SQL injection can occur            $offset = (int)$_GET['offset'];            $stmt = $db->prepare("select * from (            select            ROW_NUMBER() OVER (ORDER BY author) as rownum,            *            from books            ) as books_window            WHERE rownum > $offset AND rownum             if ($stmt->execute()) {            while (($row = $stmt->fetch()) !== false) {            print_r($row);            }            }            Cloudscape 說明在撰寫本文之際,Cloudscape 在其 SQL 實現(xiàn)中還不支持 ROW_NUMBER(),所以需要使用可滾動游標。 現(xiàn)在,如果您要編寫一個更通用的應(yīng)用程序,并希望實現(xiàn)分頁的結(jié)果集,但是不想專門編寫很多的代碼,并且也不想使用更重量級的抽象層,Troels Arvin 的非常有幫助的 RDBMS 信息建議,您可以使用游標作為更輕便(稍微慢一點)的方案。碰巧的是,PDO 具有這方面的 API 級的支持。下面將談到如何使用這種支持來達到與上面示例相同的效果: 清單 9. 使用滾動游標實現(xiàn)數(shù)據(jù)分頁            $db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');            $stmt = $db->prepare("select * from books order by author", array(            PDO_ATTR_CURSOR => PDO_CURSOR_SCROLL));            // the offset is passed in from the user when they click on a link            // this cast to integer ensures that no SQL injection can occur            $offset = (int)$_GET['offset'];            if ($stmt->execute()) {            // moves the cursor to the requested offset and fetches the first            for ($tofetch = 10,            $row = $stmt->fetch(PDO_FETCH_ASSOC, PDO_FETCH_ORI_REL, $offset);            $row !== false && $tofetch-- > 0;            $row = $stmt->fetch(PDO_FETCH_ASSOC)) {            print_r($row);            }            }            需要強調(diào)的是,雖然滾動游標對于更冗長的 window 函數(shù)方案來說是一個很方便的替代方案,但這種方案要慢很多。如果在一個傳輸量比較少的環(huán)境中進行測試,您可能發(fā)現(xiàn)不了速度上的差異,但當規(guī)模擴大時,您就會開始發(fā)現(xiàn)速度降慢帶來的痛苦。 定位更新可滾動游標的另一個用途是,基于 SQL 中無法表達的重大標準驅(qū)動更新。如果您有一個 Web 頁面鏈接的表,并且需要在每晚的批處理過程中更新那個表,以反映 Web 頁面當前大小,那么可以編寫如下代碼: 清單 10. 使用滾動游標作出定位更新            $db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');            // create a named, scrolling, updateable cursor            $stmt = $db->prepare("select url, size from links FOR UPDATE OF size", array(            PDO_ATTR_CURSOR => PDO_CURSOR_SCROLL,            PDO_ATTR_CURSOR_NAME => 'link_pos'));            if ($stmt->execute()) {            // a statement for applying our updates.            // Notice the WHERE CURRENT OF clause mentions "link_pos",            // which is the name of the cursor we're using to select the data            $upd = $db->prepare("UPDATE links set size = ? WHERE CURRENT OF link_pos");            // grab each row            while (($row = $stmt->fetch()) !== false) {            // There are much more efficient ways to do this;            // this is a brief example only: grab all the content            // from the URL            $content = file_get_conents($row['url']);            // and measure its length            $size = strlen($content)            // and pass that as a parameter to our update statement            $upd->execute(array($size));            }            }            大型對象在應(yīng)用程序中的某個地方,您可能發(fā)現(xiàn)需要在數(shù)據(jù)庫中存儲“大型(large)”數(shù)據(jù)。大型通常意味著“大約 4kb 或 4kb 以上”,盡管在沒有“大型”數(shù)據(jù)之前 DB2 最大可以處理 32kb 的數(shù)據(jù)。 大型對象可以是文本的,也可以是二進制的。PDO 允許在 bindParam() 或 bindColumn() 調(diào)用中通過使用 PDO_PARAM_LOB 類型代碼來使用大型數(shù)據(jù)類型。PDO_PARAM_LOB 告訴 PDO 將數(shù)據(jù)映射為流,所以可以使用 PHP Streams API 來操縱這樣的數(shù)據(jù)。下面是一個示例: 清單 11. 從數(shù)據(jù)庫取一副圖像            $db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');            $stmt = $db->prepare("select contenttype, imagedata from images where id=?");            $stmt->execute(array($_GET['id']));            list($type, $lob) = $stmt->fetch();            header("Content-Type: $type");            fpassthru($lob);            上面的介紹很簡明扼要。現(xiàn)在讓我們試試另一面,將上傳的圖像插入到一個數(shù)據(jù)庫中: 清單 12. 將圖像插入數(shù)據(jù)庫中            $db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');            $stmt = $db->prepare("insert into images (id, contenttype, imagedata) values (?, ?, ?)");            $id = get_new_id(); // some function to allocate a new ID            // assume that we are running as part of a file upload form            // You can find more information in the PHP documentation            $fp = fopen($_FILES['file']['tmp_name'], 'rb');            $stmt->bindParam(1, $id);            $stmt->bindParam(2, $_FILES['file']['type']);            $stmt->bindParam(3, $fp, PDO_PARAM_LOB);            $stmt->execute();            這兩個例子都是宏觀層次的。請記住,被取的大型對象是一個流,可以通過所有常規(guī)的流函數(shù)來使用它,例如 fgets()、fread()、fgetcsv() 和 stream_get_contents()。


本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u/5652/showart_1687443.html




歡迎光臨 Chinaunix (http://72891.cn/) Powered by Discuz! X3.2