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

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
最近訪問(wèn)板塊 發(fā)新帖
查看: 2031 | 回復(fù): 0
打印 上一主題 下一主題

[收藏] PHP通過(guò)PDO鏈接ODBC [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2008-12-09 12:03 |只看該作者 |倒序?yàn)g覽
引用地址: 
http://bbs.3qit.com/thread-57756-1-1.html
PHP 通過(guò)PDO鏈接ODBCPHP 5.1 發(fā)布時(shí)將附帶一個(gè)全新的數(shù)據(jù)庫(kù)連接層,即 PHP Data Objects (PDO)。雖然 PHP 一直都擁有很好的數(shù)據(jù)庫(kù)連接,但 PDO 讓 PHP 達(dá)到一個(gè)新的高度。學(xué)習(xí)如何獲得、安裝和使用 PDO,以連接到 IBM® DB2® Universal Database™ 和 IBM Cloudscape™ 數(shù)據(jù)庫(kù),插入和檢索數(shù)據(jù),并探索更多高級(jí)特性,例如:[color="#ff0000"]預(yù)處理語(yǔ)句(prepared statements)、綁定參數(shù)(bound parameters)、可滾動(dòng)游標(biāo)(scrollable cursors)、定位更新(positioned updates)以及 LOB。另外,本文將簡(jiǎn)要地介紹一下對(duì)多字節(jié)數(shù)據(jù)的處理。 [color="#000080"]背景隨著擁有更成熟 OO 語(yǔ)法的 PHP 5 的發(fā)布,PHP 越來(lái)越多地受到越來(lái)越大的機(jī)構(gòu)的關(guān)注,對(duì)于 PHP 來(lái)說(shuō),提供更加一致的和可訪問(wèn)的數(shù)據(jù)訪問(wèn) API 變得越來(lái)越重要。PHP 與流行的開放源代碼關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)(RDBMS)MySQL 之間總是很有默契。這對(duì)拍檔的成功很大程度上是由于它們免費(fèi)可用,而且進(jìn)入的門檻也比較低,這兩種產(chǎn)品的合作使它們各自都取得了廣受推崇的地位。很多 PHP 應(yīng)用程序開發(fā)人員都習(xí)慣于 PHP-MySQL 這對(duì)組合,以致 PHP 對(duì)其他數(shù)據(jù)庫(kù)的支持常常模仿 MySQL 客戶機(jī)庫(kù) API。然而,并不是所有的數(shù)據(jù)庫(kù)客戶機(jī) API 都是一樣的,也不是所有的數(shù)據(jù)庫(kù)都提供相同的特性。雖然存在模仿,但不同的 PHP 數(shù)據(jù)庫(kù)擴(kuò)展都有它們各自的怪僻和不同之處,所以從一種數(shù)據(jù)庫(kù)遷移到另一種數(shù)據(jù)庫(kù)時(shí)會(huì)有一些困難。雖然這不是創(chuàng)建 PDO 的直接原因,但是在設(shè)計(jì)過(guò)程中還是有一定影響的。如果您是帶著想結(jié)合使用 PHP 和 DB2 的目的閱讀本文,那么您很可能屬于以下類型中的一種:您從一家小公司開始,在 MySQL(舉個(gè)例子)上運(yùn)行 PHP,由于業(yè)務(wù)增長(zhǎng),您需要 DB2 所提供的可伸縮性/可靠性/支持或其他特性。您希望移植代碼,以使用 DB2,但由于 API 的變化,您需要編寫或?qū)崿F(xiàn)一個(gè)抽象層,以便在 DB2 上測(cè)試應(yīng)用程序的同時(shí)可以繼續(xù)在舊的數(shù)據(jù)庫(kù)上運(yùn)行。不僅如此,您還希望能有自己的選擇,并保留支持其他 RDBMS 的可能性,因?yàn)槟宄,有些客戶機(jī)可能已經(jīng)和其他平臺(tái)栓在一起了。 您用 PHP 在 MySQL之上構(gòu)建了一個(gè)小型的部門應(yīng)用程序(同樣,這只是舉個(gè)例子,我并不是要跟 MySQL 過(guò)不去)。事實(shí)證明這個(gè)應(yīng)用程序本身很有用,現(xiàn)在已經(jīng)在這個(gè)部門之外使用,并且闖入了 CIO/CTO 的法眼 —— 現(xiàn)在需要遵從托管的標(biāo)準(zhǔn)數(shù)據(jù)庫(kù)。(是的,這是第一點(diǎn)的一個(gè)變種。) 在其他某些復(fù)雜的企業(yè)級(jí)應(yīng)用程序的后臺(tái),您已經(jīng)有一個(gè) DB2 實(shí)例;您希望利用 PHP 的快速應(yīng)用程序開發(fā)和原型設(shè)計(jì)來(lái)生成動(dòng)態(tài)報(bào)告。[color="#003366"]目標(biāo)至此我們已經(jīng)掌握了數(shù)據(jù)庫(kù)及 PHP 的背景知識(shí),現(xiàn)在正好可以提及 PDO 背后的一些設(shè)計(jì)目標(biāo):為大多數(shù)數(shù)據(jù)庫(kù) API 中的常見(jiàn)特性提供一致的 API。 具有可擴(kuò)展性,以使數(shù)據(jù)庫(kù)供應(yīng)商 X 仍然可以暴露特性 Y 并保持 PDO 的兼容性。 提供大量基本的兼容性技巧,以便能夠更方便地創(chuàng)建跨數(shù)據(jù)庫(kù)兼容的應(yīng)用程序。 不為給定數(shù)據(jù)庫(kù) API 中本來(lái)沒(méi)有的特性(例如序列)提供完全抽象或仿真。PDO 類意圖為您提供對(duì)數(shù)據(jù)庫(kù)本地特性的一致性訪問(wèn),并減少干擾。 通過(guò)將與 PHP 內(nèi)部打交道的代碼(這是最難于編寫的部分)集中起來(lái),簡(jiǎn)化 PHP 數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序的創(chuàng)建。 最后一點(diǎn)非常重要。PDO 是模塊化結(jié)構(gòu),它被分成一個(gè)公共核心以及一個(gè)或多個(gè)驅(qū)動(dòng)程序擴(kuò)展,公共核心提供了在腳本(PDO 本身)中使用的 API,驅(qū)動(dòng)程序擴(kuò)展則為 PDO 和本地 RDBMS 客戶機(jī) API 庫(kù)架起一座橋梁。DB2 用戶將會(huì)希望使用 PDO_ODBC 驅(qū)動(dòng)程序,據(jù)稱它可以提供以下特性: 它經(jīng)過(guò)重新編寫,能支持遵從 ODBC V3 的驅(qū)動(dòng)程序和驅(qū)動(dòng)程序管理器。它還考慮了對(duì) DB2 特定特性和優(yōu)化的支持,這成為設(shè)計(jì)過(guò)程中的一部分 —— 不是后來(lái)補(bǔ)充的。 它支持經(jīng)過(guò)試驗(yàn)和測(cè)試的存儲(chǔ)過(guò)程和大型對(duì)象。它不僅能夠工作,而且非常好用。 對(duì)于取 10,000 行記錄這樣的 DB2 訪問(wèn)操作,使用 PDO_ODBC 驅(qū)動(dòng)程序時(shí)的性能比使用傳統(tǒng)的 PHP Unified ODBC 擴(kuò)展要快大約 10 倍。之所以有這么大的差異,是因?yàn)樵?PDO 中默認(rèn)的游標(biāo)是輕量級(jí)的只能向前移動(dòng)的游標(biāo)。 [color="#003366"]獲取和安裝 PDOPHP 5.1 發(fā)布時(shí)將附帶 PDO,但是也可以通過(guò) PECL 這個(gè) PHP 擴(kuò)展庫(kù)(PHP Extension Repository)來(lái)結(jié)合使用 PDO 和 PHP 5.0.3 及以上版本。如果您使用的是 Windows®,那么您會(huì)欣喜地發(fā)現(xiàn)安裝過(guò)程要簡(jiǎn)單得多。 我將假設(shè)您已經(jīng)擁有配置 PHP 5 使之使用您選擇的 Web 服務(wù)器的經(jīng)驗(yàn),只有在此假設(shè)下,我才能集中精力關(guān)注更相關(guān)的細(xì)節(jié)。同樣,我還將假設(shè)您使用的是一個(gè) DB2 Universal Database 服務(wù)器或網(wǎng)絡(luò)服務(wù)器模式下的 IBM Cloudscape 數(shù)據(jù)庫(kù),并且接受了用戶為 db2inst1、密碼為 ibmdb2 的默認(rèn)安裝選項(xiàng)。如果您自己編譯驅(qū)動(dòng)程序,那么在進(jìn)行編譯的機(jī)器上,應(yīng)該安裝有 DB2 客戶機(jī),并且存在應(yīng)用程序開發(fā) header,否則編譯將遭到失敗。在 PHP 5.0.3 及以上版本上通過(guò) PECL 進(jìn)行安裝默認(rèn)情況下,PHP 將安裝 "PEAR" 包管理系統(tǒng)。您選擇的 OS 發(fā)行版很可能已經(jīng)創(chuàng)建了一個(gè)包含 PEAR 的組件的包,很可能您已經(jīng)安裝了這個(gè)包,并準(zhǔn)備運(yùn)行它。讓我們?cè)囼?yàn)一下。 如果它不能工作如果您沒(méi)能看到類似于左側(cè)文本的輸出,那么很可能您沒(méi)有安裝需要的所有包。這時(shí)應(yīng)查閱您選擇的 OS 發(fā)行版的文檔,看看接下來(lái)應(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            這個(gè)包列表表明,我已經(jīng)安裝了 PEAR 1.3.4。很可能您也會(huì)安裝那個(gè)版本。為了成功地安裝 PDO,需要升級(jí)到 PEAR 1.3.5;這個(gè)過(guò)程很快,很順利:$ sudo pear upgrade PEAR現(xiàn)在便可以放心安裝 PDO 了:$ sudo pear install PDO您已經(jīng)安裝了 PDO 核心,為了使之生效,需要在 php.ini 文件中啟用它。您需要添加以下一行: extension=pdo.so            現(xiàn)在安裝用于 PDO 的 ODBC 驅(qū)動(dòng)程序,如果您需要連接到 DB2、Cloudscape 或 Apache Derby,就需要這個(gè)驅(qū)動(dòng)程序: $ sudo pear install PDO_ODBC您將看到這樣的提示:flavour,dir ? (just leave blank for help)。這是一個(gè)稍微有點(diǎn)隱蔽的提示,它詢問(wèn)需要配置哪種類型的 ODBC 驅(qū)動(dòng)程序,以及將它安裝在哪里。如果在安裝 DB2 時(shí)選擇了默認(rèn)安裝選項(xiàng),那么可以輸入 ibm-db2,這相當(dāng)于 ibm-db2,/home/db2inst1/sqllib。如果您選擇了不同的安裝位置,那么應(yīng)該用它置替換 /home/db2inst1/sqllib。輸入了正確的細(xì)節(jié)后,按下 enter 鍵,這樣驅(qū)動(dòng)程序就會(huì)構(gòu)建和安裝。 您需要將驅(qū)動(dòng)程序添加到 php.ini 文件中,從而激活驅(qū)動(dòng)程序。確保將下面這一行添加在之前所添加的 pdo.so 這一行之后,否則 PHP 不能正確地初始化。 extension=pdo_odbc.so            PHP 5.1 及以上版本上的安裝PHP 5.1 發(fā)布時(shí)附帶了 PDO。為獲得 DB2 支持,只需將下面的開關(guān)添加到配置行。您顯然希望添加更多的配置選項(xiàng),以滿足您自己的 Web 服務(wù)器。關(guān)于這方面的詳細(xì)內(nèi)容,可以查看 PHP 文檔。我將假設(shè)您使用的是一個(gè)最近的 Linux® 發(fā)行版,并運(yùn)行 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® 上的安裝來(lái)要簡(jiǎn)單一些。如果您下載了 PHP 5.1,那么就已經(jīng)擁有了這個(gè)包中相關(guān)的 DLL。否則,您需要從 PHP 快照站點(diǎn)(請(qǐng)參閱本文后面的“下載”小節(jié))下載這些 DLL。 為了激活 PDO,將下面兩行添加到 php.ini 文件: extension=php_pdo.dll            extension=php_pdo_odbc.dll            重新啟動(dòng) Web 服務(wù)器安裝完畢后,應(yīng)該完全重新啟動(dòng) Web 服務(wù)器,以確保 PHP 裝載新的擴(kuò)展,這樣就可以開始使用 PDO 了。如果您使用的是 UNIX 平臺(tái),那么需要獲得 DB2 客戶機(jī)的 DB2 實(shí)例環(huán)境,以便正確地初始化。如果您使用的是 bourne shell 型的 shell,那么可以通過(guò)運(yùn)行命令 . /home/db2inst1/sqllib/db2profile 來(lái)獲得。(注意: 假定開頭部分的句號(hào)已經(jīng)在那里。┠枰鞒霭才,使之在 Web 服務(wù)器啟動(dòng)腳本中自動(dòng)發(fā)生。 PDO 中的關(guān)鍵概念為了掌握 PDO,需要了解 5 個(gè)關(guān)鍵概念。這 5 個(gè)概念是: 連接和連接管理 事務(wù)和自動(dòng)提交 預(yù)處理語(yǔ)句和存儲(chǔ)過(guò)程 錯(cuò)誤和錯(cuò)誤處理 特定于驅(qū)動(dòng)程序的功能性,包括 滾動(dòng)游標(biāo) 和 大型對(duì)象連接和連接管理連接是通過(guò)創(chuàng)建 PDO 基類的實(shí)例而建立的。不管您想要使用哪種驅(qū)動(dòng)程序,您總是使用 PDO 類名。構(gòu)造函數(shù)接受用于指定數(shù)據(jù)源(即 DSN)的參數(shù),可能還包括用戶名和密碼參數(shù)(如果有的話)。最后一個(gè)參數(shù)用于傳遞附加的調(diào)優(yōu)參數(shù)到 PDO 或底層驅(qū)動(dòng)程序 —— 后面很快會(huì)有更詳細(xì)的論述。下面是一個(gè)簡(jiǎn)短的連接到 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ū)動(dòng)程序,并且應(yīng)該使用 "SAMPLE" 數(shù)據(jù)庫(kù)。如果使用一個(gè)驅(qū)動(dòng)程序管理器,那么可以用一個(gè) ODBC 級(jí)數(shù)據(jù)源名稱替代 SAMPLE。實(shí)際上,在冒號(hào)字符之后可以指定任何有效的 ODBC 數(shù)據(jù)源連接字符串。 如果連接成功,您將看到消息 "Connected",否則,PDO 將拋出一個(gè) PDOException,解釋為什么連接失敗。可能的原因包括無(wú)效的參數(shù),不正確的用戶/密碼,甚至是您忘了裝載驅(qū)動(dòng)程序。 值得注意的是,除非您捕捉從構(gòu)造函數(shù)拋出的異常,否則,如果 PHP 腳本未能連接到數(shù)據(jù)庫(kù),它將終止。這與傳統(tǒng)的 PHP 數(shù)據(jù)庫(kù)擴(kuò)展有很大的不同。對(duì)于不喜歡異常的人來(lái)說(shuō),只有兩個(gè)“硬故障(hard-failure)”點(diǎn)可能拋出異常,這是其中一個(gè)點(diǎn)(另一個(gè)地點(diǎn)是,當(dāng)您 試圖使用事務(wù)時(shí)缺乏對(duì)事務(wù)的支持)。對(duì)于所有其他錯(cuò)誤,PDO 將使用您選擇的 錯(cuò)誤處理設(shè)置。 連接將保持開放狀態(tài),直到所有對(duì)它的引用被釋放。如果在主腳本的頂端打開連接, 并將其句柄存儲(chǔ)在一個(gè)全局變量中,那么該連接將一直處于開放狀態(tài),直到腳本結(jié)束,或者直到 $dbh 變量被設(shè)為 null。如果在一個(gè)函數(shù)中打開連接,并且只將句柄存儲(chǔ)在一個(gè)本地變量中,那么當(dāng)函數(shù)返回時(shí),連接將被關(guān)閉。這些語(yǔ)義對(duì)于 PHP 中的任何對(duì)象都是一樣的,沒(méi)有什么特別的地方。 ODBC 連接池如果您使用的是 Windows,或者如果您選擇在 UNIX 型平臺(tái)上使用一個(gè) ODBC 驅(qū)動(dòng)程序管理器,那么值得注意的是,PDO_ODBC 將自動(dòng)嘗試使用該驅(qū)動(dòng)程序管理器的 ODBC 連接池特性。這個(gè)特性類似于 PHP 級(jí)連接緩存,不要求專門請(qǐng)求一個(gè)持久的連接。此外,緩存是在 ODBC 級(jí)進(jìn)行的,這意味著在同一個(gè)進(jìn)程中運(yùn)行的其他組件(例如在 IIS 下運(yùn)行的 ASP/.Net 腳本)也能利用相同的連接池。 對(duì)于流量較大的站點(diǎn),讓 PHP 在不同請(qǐng)求的間隙中緩存打開的連接,使得每個(gè)進(jìn)程(每個(gè)惟一的連接參數(shù)集)只需花費(fèi)一次建立連接的成本,這樣做常常很有益處。雖然這聽(tīng)起來(lái)像是一個(gè)不錯(cuò)的 想法,但您應(yīng)該仔細(xì)評(píng)估這樣做對(duì)系統(tǒng)的影響,因?yàn)楫?dāng)大量緩存的連接空閑在那里的時(shí)候,就會(huì)適得其反。 要建立一個(gè)緩存的連接(如果您更熟悉傳統(tǒng)的數(shù)據(jù)庫(kù)擴(kuò)展的話,也可以說(shuō)是 *pconnect()),需要在實(shí)例化數(shù)據(jù)庫(kù)連接時(shí)傳遞一個(gè)屬性:清單 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ù)和自動(dòng)提交至此,您已經(jīng)通過(guò) PDO 連接到了 DB2,在發(fā)出查詢之前,您應(yīng)該理解 PDO 是如何管理事務(wù)的。如果之前沒(méi)有接觸過(guò)事務(wù),那么首先要知道事務(wù)的 4 個(gè)特征:原子性(Atomicity)、一致性(Consistency)、獨(dú)立性(Isolation)和持久性(Durability),即 ACID。用外行人的話說(shuō),對(duì)于在一個(gè)事務(wù)中執(zhí)行的任何工作,即使它是分階段執(zhí)行的,也一定可以保證該工作會(huì)安全地應(yīng)用于數(shù)據(jù)庫(kù),并且在工作被提交時(shí),不 會(huì)受到來(lái)自其他連接的影響。事務(wù)性工作可以根據(jù)請(qǐng)求自動(dòng)撤銷(假設(shè)您還沒(méi)有提交它),這使得腳本中的錯(cuò)誤處理變得更加容易。 事務(wù)通常是通過(guò)把一批更改積蓄起來(lái)、使之同時(shí)生效而實(shí)現(xiàn)的。這樣做的好處是可以大大提高這些更新的效率。換句話說(shuō),事務(wù)可以使腳本更快,而且可能更健壯(不過(guò)需要正確地使用事務(wù)才能獲得這樣的好處)。 警告只有在通過(guò) PDO::beginTransaction() 啟動(dòng)事務(wù)的情況下,才會(huì)發(fā)生自動(dòng)回滾。如果手動(dòng)地發(fā)出開始一個(gè)事務(wù)的查詢,那么 PDO 就無(wú)法知道該事務(wù),從而不能在必要時(shí)進(jìn)行回滾。 不幸的是,并不是每種數(shù)據(jù)庫(kù)都支持事務(wù),所以當(dāng)?shù)谝淮未蜷_連接時(shí),PDO 需要在所謂的“自動(dòng)提交(auto-commit)”模式下運(yùn)行。自動(dòng)提交模式意味著,如果數(shù)據(jù)庫(kù)支持事務(wù),那么您所運(yùn)行的每一個(gè)查詢都有它自己的隱式事 務(wù),如果數(shù)據(jù)庫(kù)不支持事務(wù),每個(gè)查詢就沒(méi)有這樣的事務(wù)。如果您需要一個(gè)事務(wù),那么必須使用 PDO::beginTransaction() 方法來(lái)啟動(dòng)一個(gè)事務(wù)。如果底層驅(qū)動(dòng)程序不支持事務(wù),那么將會(huì)拋出一個(gè) PDOException(無(wú)論錯(cuò)誤處理設(shè)置是怎樣的:這總是一個(gè)嚴(yán)重錯(cuò)誤狀態(tài))。在一個(gè)事務(wù)中,可以使用 PDO::commit() 或 PDO::rollBack() 來(lái)結(jié)束該事務(wù),這取決于事務(wù)中運(yùn)行的代碼是否成功。 DB2 特性雖然我認(rèn)為事務(wù)通常要更快一些,但您還是應(yīng)該自己評(píng)估事務(wù)是否真的可以加快代碼。例如,在高并發(fā)環(huán)境中您可能會(huì)發(fā)現(xiàn),過(guò)度使用事務(wù)會(huì)增加鎖開銷。如果在應(yīng) 用程序中出現(xiàn)這種情況,那么建議的補(bǔ)救辦法是在一般情況下使用自動(dòng)提交,而對(duì)于真正需要全部 ACID 特征的代碼部分則仍然使用事務(wù)。 當(dāng)腳本結(jié)束時(shí),或者當(dāng)一個(gè)連接即將被關(guān)閉時(shí),如果有一個(gè)未完成的事務(wù),那么 PDO 將自動(dòng)回滾該事務(wù)。這是一種安全措施,有助于避免在腳本非正常結(jié)束時(shí)出現(xiàn)不一致的情況 —— 如果沒(méi)有顯式地提交事務(wù),那么假設(shè)有某個(gè)地方會(huì)出現(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)橐粋(gè)新雇員創(chuàng)建一組條目,這個(gè)雇員有一個(gè) ID 號(hào),即 23。除了輸入這個(gè)人的基本數(shù)據(jù)外,我們還需要記錄雇員的薪水。兩個(gè)更新分別完成起來(lái)很簡(jiǎn)單,但通過(guò)將這兩個(gè)更新包括在 beginTransaction() 和 commit() 調(diào)用中,就可以保證在更改完成之前,其他人無(wú)法看到更改。如果發(fā)生了錯(cuò)誤,catch 塊可以回滾事務(wù)開始以來(lái)發(fā)生的所有更改,并打印出一條錯(cuò)誤消息。 并不是一定要在事務(wù)中作出更新。您也可以發(fā)出復(fù)雜的查詢來(lái)提取數(shù)據(jù),還可以使用那種信息構(gòu)建更多的更新和查詢。當(dāng)事務(wù)在活動(dòng)時(shí),可以保證其他人在工作進(jìn)行當(dāng)中無(wú)法作出更改。事實(shí)上,這不是 100% 的正確,但如果您之前沒(méi)有聽(tīng)說(shuō)過(guò)事務(wù)的話,這樣介紹也未嘗不可。
關(guān)于 PHP 應(yīng)用程序中安全性的說(shuō)明PHP Security Consortium雖然本文表明在使用 PDO 時(shí)不再需要引用輸入,但這不是說(shuō)您應(yīng)該盲目地使數(shù)據(jù)通過(guò)數(shù)據(jù)庫(kù)。XSS 攻擊是很實(shí)際的危險(xiǎn)。您應(yīng)該總是確保對(duì)傳入應(yīng)用程序的不受信任的數(shù)據(jù)應(yīng)用適當(dāng)?shù)倪^(guò)濾器,并采取措施避免讓不受信任的數(shù)據(jù)在站點(diǎn)上發(fā)出 HTML 或 javascript。 請(qǐng)?jiān)L問(wèn) The PHP Security Consortium 以了解關(guān)于這些危險(xiǎn)的更多知識(shí),以及應(yīng)該如何避免這些危險(xiǎn)。 很多 PHP 腳本中一個(gè)常見(jiàn)的缺陷是缺乏輸入檢驗(yàn)。這種缺陷可以被利用,從而招致 XSS(Cross Site Scripting)以及 SQL 入侵攻擊。在 SQL 入侵中,不受信任的數(shù)據(jù)(例如發(fā)給 Web 網(wǎng)頁(yè)的反饋)和其他文本被銜接在一起,構(gòu)成一個(gè)查詢。攻擊者可以蓄意地安排他們的輸入,使之溢出引號(hào)之外,并在您想運(yùn)行的真正查詢后面鏈接上任意一個(gè)查詢。這種攻擊使攻擊者可以更新、插入或刪除數(shù)據(jù),甚至可能可以看到數(shù)據(jù)庫(kù)中的任意信息。 XSS 也是一個(gè)類似的問(wèn)題。不過(guò)這一次不受信任的數(shù)據(jù)瞄準(zhǔn)的是瀏覽站點(diǎn)的人們,而不是應(yīng)用程序本身。通過(guò)提交包含 HTML 或 javascript 組合的文本,攻擊者期望您之后會(huì)將那種數(shù)據(jù)直接輸出到其他訪問(wèn)站點(diǎn)的人那里,從而使惡意代碼可以在站點(diǎn)訪問(wèn)者的瀏覽器上運(yùn)行。 在編寫應(yīng)用程序時(shí),需要同時(shí)考慮這兩種攻擊。如果小心地檢驗(yàn)和過(guò)濾輸入,這兩種攻擊都是可以防止的。對(duì) XSS 的處理很有技巧性,所以在這里我不便多講(不過(guò)可以從側(cè)欄找到有用的參考資料)。相比之下,SQL 入侵更容易對(duì)付。您只需在構(gòu)造查詢之前,適當(dāng)?shù)嘏懦繅K不受信任的數(shù)據(jù)。這種事情有點(diǎn)煩雜,特別是當(dāng)您有大量的字段要處理時(shí),很容易忘記做這件事。 雖然這是有用的(并且也是重要的)信息,但是您可能想知道,為什么我要花時(shí)間提到這一點(diǎn),本文的重點(diǎn)不是結(jié)合使用 PDO 和 DB2 嗎?原因是這樣的:PHP 現(xiàn)在得到很廣泛的部署,自然地,大量流行的基于 PHP 的應(yīng)用程序也得到了廣泛的部署。每當(dāng)某一種這樣的應(yīng)用程序(和 PHP 本身沒(méi)有聯(lián)系)被發(fā)現(xiàn)存在漏洞時(shí),PHP 常常被誤認(rèn)為是不安全的,可被利用的或者有缺陷的。為了避免將來(lái)出現(xiàn)這樣的情況,我們可以采取的一個(gè)措施是鼓勵(lì)應(yīng)用程序開發(fā)人員多考慮安全問(wèn)題,從而減少由誠(chéng)實(shí)的錯(cuò)誤導(dǎo)致的損害。扯遠(yuǎn)了,下面繼續(xù)介紹其他關(guān)鍵概念。 預(yù)處理語(yǔ)句和存儲(chǔ)過(guò)程很多更成熟的數(shù)據(jù)庫(kù)都支持預(yù)處理語(yǔ)句的概念。什么是預(yù)處理語(yǔ)句?您可以把預(yù)處理語(yǔ)句看作您想要運(yùn)行的 SQL 的一種編譯過(guò)的模板,它可以使用變量參數(shù)進(jìn)行定制。預(yù)處理語(yǔ)句可以帶來(lái)兩大好處: 查詢只需解析(或準(zhǔn)備)一次,但是可以用相同或不同的參數(shù)執(zhí)行多次。當(dāng)查詢準(zhǔn)備好后,數(shù)據(jù)庫(kù)將分析、編譯和優(yōu)化執(zhí)行該查詢的計(jì)劃。對(duì)于復(fù)雜的查詢,這個(gè)過(guò)程要花比較長(zhǎng)的時(shí)間,如果您需要以不同參數(shù)多次重復(fù)相同的查詢,那么該過(guò)程將大大降低應(yīng)用程序的速度。通過(guò)使用預(yù)處理語(yǔ)句,可以避免重復(fù)分析/編譯/優(yōu)化周期。簡(jiǎn)言之,預(yù)處理語(yǔ)句使用更少的資源,因而運(yùn)行得更快。 提供給預(yù)處理語(yǔ)句的參數(shù)不需要用引號(hào)括起來(lái),驅(qū)動(dòng)程序會(huì)處理這些。如果應(yīng)用程序獨(dú)占地使用預(yù)處理語(yǔ)句,那么可以確保沒(méi)有 SQL 入侵發(fā)生。(然而,如果您仍然將查詢的其他部分建立在不受信任的輸入之上,那么就仍然存在風(fēng)險(xiǎn))。 預(yù)處理語(yǔ)句是如此有用,以致 PDO 實(shí)際上打破了在目標(biāo) 4 中設(shè)下的規(guī)則:如果驅(qū)動(dòng)程序不支持預(yù)處理語(yǔ)句,那么 PDO 將仿真預(yù)處理語(yǔ)句。 下面是使用預(yù)處理語(yǔ)句的兩個(gè)例子。第一個(gè)例子 通過(guò)替換指定占位符的 name 和 value,執(zhí)行一次插入。而 第二個(gè)例子 使用問(wèn)號(hào)占位符執(zhí)行一條 select 語(yǔ)句。 清單 4. 使用預(yù)處理語(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ù)處理語(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ù)庫(kù)驅(qū)動(dòng)程序支持,您還可以綁定輸出和輸入?yún)?shù)。輸出參數(shù)通常用于從存儲(chǔ)過(guò)程獲取值。輸出參數(shù)使用起來(lái)比輸入?yún)?shù)要復(fù)雜一些,當(dāng)綁定一個(gè)給定的輸出參數(shù)時(shí),必須知道該參數(shù)的長(zhǎng)度。如果為參數(shù)綁定的值大于您建議的長(zhǎng)度,那么就會(huì)產(chǎn)生錯(cuò)誤。 清單 6. 帶輸出參數(shù)調(diào)用存儲(chǔ)過(guò)程$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ù),其語(yǔ)法類似于輸出參數(shù)。在接下來(lái)的例子中,字符串 'hello' 被傳遞給存儲(chǔ)過(guò)程,當(dāng)存儲(chǔ)過(guò)程返回時(shí),hello 被替換為該存儲(chǔ)過(guò)程返回的值。 清單 7. 帶輸入/輸出參數(shù)調(diào)用存儲(chǔ)過(guò)程$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";            錯(cuò)誤和錯(cuò)誤處理PDO 提供了 3 種不同的錯(cuò)誤處理模式,以滿足不同風(fēng)格的編程: PDO_ERRMODE_SILENT 這是默認(rèn)模式。PDO 將只設(shè)置錯(cuò)誤代碼,以通過(guò) errorCode() 和 errorInfo() 方法對(duì)語(yǔ)句和數(shù)據(jù)庫(kù)對(duì)象進(jìn)行檢查。如果錯(cuò)誤是由于對(duì)語(yǔ)句對(duì)象的調(diào)用而產(chǎn)生的,那么可以在那個(gè)對(duì)象上調(diào)用 errorCode() 或 errorInfo() 方法。如果錯(cuò)誤是由于調(diào)用數(shù)據(jù)庫(kù)對(duì)象而產(chǎn)生的,那么可以在那個(gè)數(shù)據(jù)庫(kù)對(duì)象上調(diào)用上述兩個(gè)方法。 PDO_ERRMODE_WARNING 除了設(shè)置錯(cuò)誤代碼以外,PDO 還將發(fā)出一條傳統(tǒng)的 E_WARNING 消息。如果您只是想看看發(fā)生了什么問(wèn)題,而無(wú)意中斷應(yīng)用程序的流程,那么在調(diào)試/測(cè)試當(dāng)中這種設(shè)置很有用。 PDO_ERRMODE_EXCEPTION 除了設(shè)置錯(cuò)誤代碼以外,PDO 還將拋出一個(gè) PDOException,并設(shè)置其屬性,以反映錯(cuò)誤代碼和錯(cuò)誤信息。這種設(shè)置在調(diào)試當(dāng)中也很有用,因?yàn)樗鼤?huì)放大腳本中產(chǎn)生錯(cuò)誤的地方,從而可以非常快速地指出代碼中有問(wèn)題的潛在區(qū)域(記住,如果異常導(dǎo)致腳本終止,則事務(wù)將自動(dòng)回滾)。 異常模式另一個(gè)有用的地方是,與傳統(tǒng)的 PHP 風(fēng)格的警告相比,您可以更清晰地構(gòu)造自己的錯(cuò)誤處理,而且,比起以靜寂方式以及顯式地檢查每個(gè)數(shù)據(jù)庫(kù)調(diào)用的返回值,異常模式需要的代碼/嵌套也更少。 PDO 定制了使用 SQL-92 SQLSTATE 錯(cuò)誤代碼字符串的標(biāo)準(zhǔn);不同 PDO 驅(qū)動(dòng)程序負(fù)責(zé)將它們本地代碼映射為適當(dāng)?shù)?SQLSTATE 代碼。例如,SQLSTATE 是用于 DB2(以及通常的 ODBC)的本地錯(cuò)誤代碼格式,這是多么方便!errorCode() 方法返回一個(gè) SQLSTATE 代碼。如果您需要關(guān)于一個(gè)錯(cuò)誤的更多特定的信息,PDO 還提供了一個(gè) errorInfo() 方法,該方法將返回一個(gè)數(shù)組,其中包含 SQLSTATE 代碼、特定于驅(qū)動(dòng)程序的錯(cuò)誤代碼以及特定于驅(qū)動(dòng)程序的錯(cuò)誤字符串。 分頁(yè)數(shù)據(jù)、滾動(dòng)游標(biāo)和定位更新在 Web 應(yīng)用程序中,一種常見(jiàn)的范例是對(duì)查詢結(jié)果進(jìn)行分頁(yè)。如果您使用一個(gè) Internet 搜索引擎,那么很可能每天都會(huì)做這樣的事。您輸入搜索詞,然后得到前 10-20 個(gè)匹配項(xiàng)。如果您想看到更多搜索結(jié)果,可以單擊 "next page" 鏈接。如果想回頭看前面看過(guò)的結(jié)果,可以單擊 "previous page" 鏈接。記得在幾年前,當(dāng)我第一次在 Web 上使用這樣的東西時(shí),我對(duì)自己說(shuō):“為什么我不能通過(guò)滾動(dòng)查看所有數(shù)據(jù)呢?” 問(wèn)題的答案說(shuō)簡(jiǎn)單也簡(jiǎn)單,說(shuō)復(fù)雜也復(fù)雜 —— 我只想說(shuō),HTTP 不會(huì)智能地使數(shù)據(jù)庫(kù)上的可滾動(dòng)游標(biāo)一直處于開放狀態(tài),即便如此,需要大量傳輸?shù)?Web 應(yīng)用程序也會(huì)很快地消耗掉大量開放的可滾動(dòng)游標(biāo)。因此,最簡(jiǎn)單的解決方案是為用戶顯示所有的匹配項(xiàng) —— 但是用戶很容易迷失在大量的結(jié)果當(dāng)中。比較符合邏輯的措施是人工地將數(shù)據(jù)格式化到多個(gè)頁(yè)面上,使用戶可以每次查看一部分可以管理的數(shù)據(jù)。 所以人們編寫可以取所有數(shù)據(jù)的 PHP 應(yīng)用程序,然后只顯示前 10 行。根據(jù)下一次請(qǐng)求,應(yīng)用程序又顯示 11-20 行,依此類推。這對(duì)于只返回少量數(shù)據(jù)的查詢來(lái)說(shuō)很不錯(cuò),但是,如果有很多匹配項(xiàng)(比如多于 100),那么先取全部數(shù)據(jù)然后丟棄其中的 90%,這種做法很浪費(fèi)。PHP 的創(chuàng)始人 Rasmus Lerdorf 就這種情形特地為 MySQL 發(fā)明了一個(gè)特殊的 "LIMIT, OFFSET" 子句。它允許您通知數(shù)據(jù)庫(kù),您只對(duì)一小部分行感興趣,這樣它就不會(huì)取其他不需要的行了。其語(yǔ)法(或非常類似的東西)已經(jīng)被其他流行的開放源代碼數(shù)據(jù)庫(kù)采納,但并不是所有數(shù)據(jù)庫(kù)都提供了相同的語(yǔ)法。 Troels Arvin 收集了一些非常有用的信息,對(duì)不同 RDBMS 所支持的語(yǔ)法進(jìn)行了比較。 如果您想在以 DB2 為后臺(tái)數(shù)據(jù)庫(kù)的 PHP 應(yīng)用程序中實(shí)現(xiàn)分頁(yè)結(jié)果,那么可以(也應(yīng)該)使用下面示例中的語(yǔ)法。這里我們假設(shè)有一個(gè) books 表,表中包含書名和作者,我們現(xiàn)在想要每次在一頁(yè)中顯示 10 個(gè)以上結(jié)果: 清單 8. 使用 SQL Standard "Window Functions" 實(shí)現(xiàn)數(shù)據(jù)分頁(yè)            $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 說(shuō)明在撰寫本文之際,Cloudscape 在其 SQL 實(shí)現(xiàn)中還不支持 ROW_NUMBER(),所以需要使用可滾動(dòng)游標(biāo)。 現(xiàn)在,如果您要編寫一個(gè)更通用的應(yīng)用程序,并希望實(shí)現(xiàn)分頁(yè)的結(jié)果集,但是不想專門編寫很多的代碼,并且也不想使用更重量級(jí)的抽象層,Troels Arvin 的非常有幫助的 RDBMS 信息建議,您可以使用游標(biāo)作為更輕便(稍微慢一點(diǎn))的方案。碰巧的是,PDO 具有這方面的 API 級(jí)的支持。下面將談到如何使用這種支持來(lái)達(dá)到與上面示例相同的效果: 清單 9. 使用滾動(dòng)游標(biāo)實(shí)現(xiàn)數(shù)據(jù)分頁(yè)            $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);            }            }            需要強(qiáng)調(diào)的是,雖然滾動(dòng)游標(biāo)對(duì)于更冗長(zhǎng)的 window 函數(shù)方案來(lái)說(shuō)是一個(gè)很方便的替代方案,但這種方案要慢很多。如果在一個(gè)傳輸量比較少的環(huán)境中進(jìn)行測(cè)試,您可能發(fā)現(xiàn)不了速度上的差異,但當(dāng)規(guī)模擴(kuò)大時(shí),您就會(huì)開始發(fā)現(xiàn)速度降慢帶來(lái)的痛苦。 定位更新可滾動(dòng)游標(biāo)的另一個(gè)用途是,基于 SQL 中無(wú)法表達(dá)的重大標(biāo)準(zhǔn)驅(qū)動(dòng)更新。如果您有一個(gè) Web 頁(yè)面鏈接的表,并且需要在每晚的批處理過(guò)程中更新那個(gè)表,以反映 Web 頁(yè)面當(dāng)前大小,那么可以編寫如下代碼: 清單 10. 使用滾動(dòng)游標(biāo)作出定位更新            $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));            }            }            大型對(duì)象在應(yīng)用程序中的某個(gè)地方,您可能發(fā)現(xiàn)需要在數(shù)據(jù)庫(kù)中存儲(chǔ)“大型(large)”數(shù)據(jù)。大型通常意味著“大約 4kb 或 4kb 以上”,盡管在沒(méi)有“大型”數(shù)據(jù)之前 DB2 最大可以處理 32kb 的數(shù)據(jù)。 大型對(duì)象可以是文本的,也可以是二進(jìn)制的。PDO 允許在 bindParam() 或 bindColumn() 調(diào)用中通過(guò)使用 PDO_PARAM_LOB 類型代碼來(lái)使用大型數(shù)據(jù)類型。PDO_PARAM_LOB 告訴 PDO 將數(shù)據(jù)映射為流,所以可以使用 PHP Streams API 來(lái)操縱這樣的數(shù)據(jù)。下面是一個(gè)示例: 清單 11. 從數(shù)據(jù)庫(kù)取一副圖像            $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);            上面的介紹很簡(jiǎn)明扼要,F(xiàn)在讓我們?cè)囋嚵硪幻,將上傳的圖像插入到一個(gè)數(shù)據(jù)庫(kù)中: 清單 12. 將圖像插入數(shù)據(jù)庫(kù)中            $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();            這兩個(gè)例子都是宏觀層次的。請(qǐng)記住,被取的大型對(duì)象是一個(gè)流,可以通過(guò)所有常規(guī)的流函數(shù)來(lái)使用它,例如 fgets()、fread()、fgetcsv() 和 stream_get_contents()。


本文來(lái)自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u/5652/showart_1687443.html
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過(guò)ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP