Oracle 資料庫在企業級應用廣泛使用,PHP 透過 OCI8 擴展 提供高效、穩定的連接與操作介面。本文詳細說明安裝配置、連接模式、預處理語句、LOB 處理、效能優化與常見問題排查。
# 1. 安裝 Oracle Instant Client
yum install oracle-instantclient19.3-basic oracle-instantclient19.3-devel
# 2. 安裝 PHP OCI8 擴展 (PECL)
pecl install oci8-3.0.1 # PHP 8.0+ 對應版本
# 提示輸入 Instant Client 路徑:instantclient,/usr/lib/oracle/19.3/client64/lib
# 3. 啟用擴展
echo "extension=oci8.so" > /etc/php.d/oci8.ini
# 4. 設定環境變數 (Apache/PHP-FPM 啟動腳本)
export ORACLE_HOME=/usr/lib/oracle/19.3/client64
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
C:\instantclient_19_3,加入系統 PATHphp.ini 啟用 extension=oci8(PHP 7.4+ 內建)| 模式 | 函數 | 特點 | 適用場景 |
|---|---|---|---|
| 標準連接 | oci_connect() | 同一腳本同一憑證重複呼叫返回相同資源 | 一般 Web 請求 |
| 新連接 | oci_new_connect() | 強制建立新連接,隔離交易 | 需獨立交易場景 |
| 持久連接 | oci_pconnect() | 進程間重用,減少連接開銷 | 高併發、長運行腳本 |
<?php
// 連接
$conn = oci_connect('username', 'password', '//host:1521/service_name', 'AL32UTF8');
if (!$conn) {
$e = oci_error(); trigger_error(htmlentities($e['message']), E_USER_ERROR);
}
// 準備預處理語句 (防 SQL 注入、提升效能)
$sql = 'SELECT employee_id, first_name, last_name FROM employees WHERE department_id = :dept_id';
$stid = oci_parse($conn, $sql);
$deptId = 80;
oci_bind_by_name($stid, ':dept_id', $deptId);
// 執行
oci_execute($stid);
// 取得結果
while ($row = oci_fetch_assoc($stid)) {
echo "ID: {$row['EMPLOYEE_ID']}, Name: {$row['FIRST_NAME']} {$row['LAST_NAME']}\n";
}
// 釋放資源
oci_free_statement($stid);
oci_close($conn);
// IN 參數 (輸入)
oci_bind_by_name($stid, ':name', $name, 50); // VARCHAR2
oci_bind_by_name($stid, ':salary', $salary, 22); // NUMBER (長度 22 足夠)
oci_bind_by_name($stid, ':hire_date', $date, 32, SQLT_DAT); // DATE
// OUT 參數 (輸出/存儲過程)
oci_bind_by_name($stid, ':out_id', $outId, 22, SQLT_INT);
oci_bind_by_name($stid, ':out_msg', $outMsg, 200, SQLT_CHR);
// IN OUT 參數
oci_bind_by_name($stid, ':io_val', $ioVal, 100, SQLT_CHR);
// LOB 綁定
$lob = oci_new_descriptor($conn, OCI_D_LOB);
oci_bind_by_name($stid, ':clob_data', $lob, -1, OCI_B_CLOB);
// 寫入 LOB
$lob->writeTemporary($largeText, OCI_TEMP_CLOB);
// 讀取 LOB
$lob->load(); $content = $lob->read($lob->size()); $lob->free();
oci_execute($stid, OCI_NO_AUTO_COMMIT); // 手動控制
// ... 多個操作 ...
if ($allOk) {
oci_commit($conn);
} else {
oci_rollback($conn);
}
// 寫入 BLOB (圖片)
$blob = oci_new_descriptor($conn, OCI_D_LOB);
$stid = oci_parse($conn, 'INSERT INTO images (id, data) VALUES (seq.nextval, :blob) RETURNING data INTO :blob');
oci_bind_by_name($stid, ':blob', $blob, -1, OCI_B_BLOB);
$blob->writeTemporary(file_get_contents('photo.jpg'), OCI_TEMP_BLOB);
oci_execute($stid, OCI_NO_AUTO_COMMIT);
oci_commit($conn);
$blob->free();
// 讀取 CLOB (長文本)
$stid = oci_parse($conn, 'SELECT content FROM articles WHERE id = :id');
oci_bind_by_name($stid, ':id', $id);
oci_execute($stid);
$row = oci_fetch_assoc($stid);
$clob = $row['CONTENT']; // 自動為 LOB 描述符
$text = $clob->load(); $content = $clob->read($clob->size()); $clob->free();
| 策略 | 實作方式 | 效益 |
|---|---|---|
| 語句快取 | oci8.statement_cache_size = 100 (php.ini) | 減少解析開銷 |
| 陣列綁定 (批量操作) | oci_bind_array_by_name() + oci_execute($stid, OCI_BATCH_MODE) | 批量 INSERT/UPDATE 速度提升 10x |
| 預取行數 | oci_set_prefetch($stid, 1000) | 減少網路往返 |
| 連線池 (DRCP) | Oracle 11g+ oci_pconnect() + 伺服器端 POOL 設定 | 大幅降低連接建立成本 |
| 結果快取 | Oracle 11g+ /*+ RESULT_CACHE */ 提示 | 重複查詢零負載 |
ORA-12154: TNS:could not resolve the connect identifier → 檢查 tnsnames.ora、Easy Connect 字串格式、NLS_LANG 設定ORA-24408: could not generate unique server group name → 重啟 PHP-FPM/Apache 釋放持久連接OCIEnvNlsCreate() failed → Instant Client 版本與 PHP OCI8 不匹配,重新編譯或對齊版本AL32UTF8,資料庫字元集為 AL32UTF8,PHP 內部使用 UTF-8oci_free_statement()、oci_close() 及時釋放composer require yajra/laravel-oci8,設定 config/database.php oci8 驅動doctrine/dbal 原生支援 oci8 驅動OCI8 資料庫驅動yiisoft/yii2-oracle 擴展OCI8 是 PHP 操作 Oracle 的標準介面,掌握預處理、LOB、交易控制與效能調優,是企業級 PHP 開發的核心技能。