PHP,即“PHP: Hypertext Preprocessor”,是一種廣泛用於 Open Source(開放原始碼)並可以嵌入 HTML 的多用途指令碼語言。它的文法接近 C、Java 和 Perl,易於學習。該語言的主要目標是讓 Web 開發人員快速的書寫動態產生的網頁,然而,PHP 的功能並不局限於此。PHP普遍被認為可以更快和更有效地實現複雜的編程任務,而且正是因為它的更穩定以及佔用更少資源的優點成為開發B/S結構系統的必備的WEB指令碼設計語言,扮演著類似中介軟體的角色,即文法解析與執行。
ORACLE LOB資料模型
在B/S(Browser/Server,瀏覽器/伺服器)應用系統中,需要儲存的已不僅僅是簡單的文字資訊,同時還包括一些圖片和音像資料或者是超長的文本。比如開發一套公文系統,公文中的圖表、附件等二進位檔案或超長文本將無法使用普通的字元或其他類型的資料描述,這就要求後台資料庫要有儲存這些資料的能力。運用Oracle LOB對象可實現該功能。
Oracle LOB是用來儲存大量的二進位和文本資料的一種資料類型(一個LOB欄位可儲存可多達4GB的資料)。目前,它又分為兩種類型:內部LOB和外部LOB。內部LOB將資料以位元組流的形式儲存在資料庫的內部。因而,內部LOB的許多操作都可以參與事務,也可以像處理普通資料一樣對其進行備份和恢複操作。Oracle8i支援三種類型的內部LOB:BLOB(位元據)、CLOB(單位元組字元資料)、NCLOB(多位元組國家字元資料)。其中CLOB和NCLOB類型適用於儲存超長的文本資料,BLOB欄位適用於儲存大量的位元據,像、視頻、音頻等。目前,Oracle8i只支援一種外部LOB類型,即BFILE類型。在資料庫內,該類型僅儲存資料在作業系統中的位置資訊,而資料的實體以外部檔案的形式存在於作業系統的檔案系統中。因而,該類型所表示的資料是唯讀,不參與事務。該類型可協助使用者管理大量的由外部程式訪問的檔案。
PHP Oracle 8 函數分析
PHP中有兩套ORACLE函數擴充庫,其中的ORACLE8函數允許訪問 Oracle8 和 Oracle7 資料庫,這些函數使用了Oracle8 Call-Interface(OCI8),支援向 Oracle 位置標誌符綁定局部和全域 PHP 變數,全面支援 LOB、FILE 和 ROWID,允許使用者使用使用者自訂的變數,即使用者資料庫的自訂對象類。
Oracle8函數庫中函數OCIFetchInto用於取回一行資料記錄放入數組中,該函數的文法描述如下:
int OCIFetchInto(array &result, int [mode])
其中,參數mode可省略,內定值為OCI_NUM。在訪問Oracle LOB時,如果希望返回LOB對象,則mode應設為OCI_ASSOC+OCI_RETURN_LOBS。
函數OCIBindByName用於將PHP變數與Oracle對象進行綁定,從而建立PHP與Oracle之間的資料通訊,該函數文法描述為:
boolean OCIBindByName(int stmt, string ph_name, mixed &variable, int length, int [type])
其中:參數stmt是經過Oracle解析函數OCIParse解析後的字串指標。參數ph_name即需綁定的ORACLE返回變數名稱;參數variable前面一定要加&符號,表PHP變數地址。參數length為變數的長度,若設為-1則使用指定的variable變數的最大值;參數type可省略,其值有OCI_B_FILE(二進位檔案)、OCI_B_CFILE(文字檔)、OCI_B_CLOB(文字LOB)、OCI_B_BLOB(位元LOB)及OCI_B_ROWID(ROWID)等數種。值得注意的是,如使用Oracle8中特有的新資料類型LOB/ROWID/BFILE時,需要先執行 OCINewDescriptor()函數,同時必須要將length參數設成 -1。
函數OCINewDescriptor用於初始化新的LOB/FILE描述。該函數文法描述為:
string OCINewDescriptor(int connection , int [type])
其中的type同OCIBindByName函數中的type定義。
必須的環境配置
使用PHP的ORACLE8函數庫需要Oracle8用戶端庫。在使用這個擴充之前,請確認你已經為Oracle 使用者和WEB daemon 使用者正確設定了 Oracle 環境變數。下面列出了需要設定的環境變數:
ORACLE_HOME #ORACLE安裝路徑
ORACLE_SID # ORACLE資料庫ID
LD_LIBRARY_PATH #LD聯結庫路徑
NLS_LANG #ORALCE地區(語言)設定
ORA_NLS33 # ORA_NLS33路徑
為Linux環境下驗證以上變數是否正確,最佳的辦法就是分別在oracle使用者與nobody下執行:
# env
根據輸出的結果,判斷上述環境變數是否一致。
在為WEB 伺服器使用者佈建環境變數之後,你還需要將WEB 伺服器使用者(nobody、 www)加到oracle組中。
有關ORACLE8用戶端和PHP安裝設定的詳細說明,請參考相關的技術手冊。
應用範例
在PHP中上傳並將檔案儲存體在ORACLE LOB欄位中的應用範例如下:
//LOB對象初始化,擷取PHP變數指標
$lob = OCINewDescriptor($conn, OCI_D_LOB);
//向有關的檔案記錄表添加紀錄,ORACLE SQL文法解釋
$stmt = OCIParse($conn,"update T_FILE set FILENAME='".$_FILES['FJ1']['name'].
"',FILETYPE='".$_FILES['F1']['type']."',FILES=EMPTY_BLOB() where FILEID=".$newid." returning FILES into :lob");
//綁定LOB變數
OCIBindByName($stmt, ':lob', &$lob, -1, OCI_B_BLOB);
OCIExecute($stmt, OCI_DEFAULT);//執行語句
if($lob->savefile($F1)){//將表單提交的檔案通過lob指標存入ORACLE資料庫
OCICommit($conn);
//成功上傳
}else{
//未能成功上傳
}
OCIFreeDesc($lob);
OCIFreeStatement($stmt);
//結束
在PHP中將檔案從資料庫中提取並下載的應用範例如下:
$stmt = OCIParse($conn,"SELECT * FROM T_ FILE WHERE FILEID =$ID");
OCIExecute($stmt);
if(OCIFetchInto($stmt,$result,OCI_ASSOC+OCI_RETURN_LOBS)){
//輸出檔案類型資訊供瀏覽器判斷
header("Content-type: ".$result[FILETYPE]);
//輸出檔案名,瀏覽器可提示是否開啟或下載該檔案
header("Content-Disposition: attachment; filename=".$result[FILENAME]);
//以上關於header函數的使用可參考PHP manual,HTML的檔案頭資訊請參考Internet RFC 2616。
//輸出檔案流,在此,瀏覽器擷取檔案內容,出現正在下載或直接開啟檔案的提示
echo $result[FILES];
}
OCIFreeStatement($stmt);
//結束
以上範例僅是應用的關鍵語句,並在Linux+PHP+Apache+Oracle8i平台上通過了驗證,讀者可根據自身需要進行完善和補充。
結束語
本文探討PHP對ORACLE LOB訪問的實現,僅是PHP在B/S結構系統中基本運用技術所涉及的一個方面。筆者曾對比了JDBC與PHP-OCI兩者的執行效率,以JDBC為訪問引擎的OAS HTTP Server(HTTP Server為apache2.0)的響應速度要遜於建立在Linux+Aache+PHP-OCI平台之上的WEB Server,這其中當然有Linux的出色表現,但不可否認的是,PHP與OCI的組合是非常優秀的。
由於缺乏ORACLE LOB與PHP ORACLE8函數庫相結合運用的嘗試,在開發資訊管理系統中,為實現對大型資料檔案的管理,PHP在此方面的功能並未得到有效利用,希望本文對從事PHP資料庫技術卻遇到此方面難題的程式員會有所協助。