在 PHP 尚未內建 PDO 或現代資料庫抽象層的年代(PHP 3/4 時代),開發者必須自行撰寫資料庫連接類別。本文展示一個經典的 DB_Sql 類別,專為 Oracle 8 設計,展現了早期 PHP 連接 Oracle 的典型模式與挑戰。
該類別封裝了 Oracle 連接、查詢執行、結果集游標管理、錯誤處理、交易控制與中繼資料查詢等核心功能。值得注意的幾個歷史細節:
PutEnv 設定 ORACLE_HOME 與 ORACLE_SID,這在早期 Apache + PHP 模組模式下是必要步驟,否則易導致 Apache 進程變成殭屍程序。Remote 模式(使用 user/pass@sid 字串)與本地模式(分開傳遞帳號密碼),對應不同部署架構。query() 方法嘗試重用 Query_ID 以節省開啟游標數量,並提醒調整 init.ora 的 max_open_cursors 參數。ora_commiton() 開啟自動提交,並提供 begin_transaction()/end_transaction() 手動控制交易。function query($Query_String) {
$this->connect();
$this->Query_ID = ora_open($this->Link_ID);
if (!@ora_parse($this->Query_ID, $Query_String)) {
$this->halt("ora_parse() failed: $Query_String");
} elseif (!@ora_exec($this->Query_ID)) {
$this->halt("ora_exec() failed: $Query_String");
}
return $this->Query_ID;
}
使用 ora_parse + ora_exec 兩階段執行,錯誤時透過 halt() 輸出 SQL 與 Oracle 錯誤碼,方便除錯。
function next_record() {
if (0 == ora_fetch($this->Query_ID)) {
$this->Row++;
for ($ix=0; $ix<ora_numcols($this->Query_ID); $ix++) {
$col = strtolower(ora_columnname($this->Query_ID, $ix));
$this->Record[$col] = ora_getcolumn($this->Query_ID, $ix);
}
return 1;
}
$this->disconnect();
return 0;
}
將欄位名轉為小寫鍵值,同時保留數字索引,相容不同使用習慣。
metadata() 方法查詢 ALL_TAB_COLUMNS 與 ALL_IND_COLUMNS(使用 Oracle 專有的 (+) 外部聯結語法),回傳欄位型別、長度、精度、是否為 NULL、索引名稱等資訊,並對應 PHP 型別(string/int/double/blob)。
function nextid($seq_name) {
$Query_ID = ora_open($this->Link_ID);
if (!@ora_parse($Query_ID, "SELECT $seq_name.NEXTVAL FROM DUAL")) {
@ora_parse($Query_ID, "CREATE SEQUENCE $seq_name");
@ora_exec($Query_ID);
@ora_parse($Query_ID, "SELECT $seq_name.NEXTVAL FROM DUAL");
}
@ora_exec($Query_ID);
@ora_fetch($Query_ID);
return ora_getcolumn($Query_ID, 0);
}
若序列不存在則自動建立,展現「自助式」資料庫物件管理思維。
雖然 ora_* 函數早已在 PHP 5.1 移除(改用 OCI8 或 PDO_OCI),但此類別展示了資料庫抽象層的核心設計模式:連接池管理、游標生命週期、錯誤封裝、交易控制、中繼資料反射。現代框架(Laravel Eloquent、Doctrine DBAL)在架構哲學上仍可見其影子。
遷移建議:維護舊專案時,建議以 PDO_OCI 或 OCI8 重寫資料存取層,並採用參數綁定防止 SQL 注入,利用現代連接池(如 PHP-FPM + OCI8 持久連接)提升效能。