首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 連接 Oracle 資料庫完整指南:OCI8 與 PDO_OCI 實作

     編輯:PHP 綜合知識

前言

企業級應用常需 PHP 串接 Oracle Database。PHP 提供兩種主流介面:OCI8 擴展(原生、功能最全)與 PDO_OCI(統一 PDO 介面、可攜性佳)。本文涵蓋環境建置、連線參數、CRUD 操作、常見錯誤排查與最佳實務。

環境建置

1. 安裝 Oracle Instant Client

下載對應作業系統、架構的 Basic Package + SDK Package:

  • Linux: instantclient-basic-linux.x64-19.x.rpm, instantclient-sdk-linux.x64-19.x.rpm
  • Windows: instantclient-basic-windows.x64-19.x.zip, instantclient-sdk-windows.x64-19.x.zip

安裝後設定環境變數:

# Linux /etc/profile.d/oracle.sh
export ORACLE_HOME=/usr/lib/oracle/19/client64
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH
export PATH=$ORACLE_HOME/bin:$PATH

# Windows 系統環境變數
ORACLE_HOME=C:\instantclient_19_x
PATH=%ORACLE_HOME%;%PATH%

2. 編譯/啟用 PHP 擴展

Linux (編譯):

# 安裝開發套件
yum install php-devel php-pear gcc make

# 編譯 OCI8
pecl install oci8
# 提示輸入: instantclient,/usr/lib/oracle/19/client64/lib

# 編譯 PDO_OCI (較少用,建議優先 OCI8)
pecl install pdo_oci
# 提示輸入: instantclient,/usr/lib/oracle/19/client64/lib

Windows:php.ini 啟用預編譯 dll:

extension=oci8_12c  ; PHP 7.x
extension=oci8      ; PHP 8.x
extension=pdo_oci

重啟 Web 伺服器,php -m | grep -i oci 確認載入。

連線字串 (DSN) 格式

格式範例適用場景
Easy Connect192.168.1.100:1521/ORCL無需 tnsnames.ora,最簡單
TNS 別名ORCL已配置 $ORACLE_HOME/network/admin/tnsnames.ora
完整描述(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.1.100)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORCL)))複雜網路、負載均衡、無 tnsnames

OCI8 核心操作範例

<?php
// 連線
$conn = oci_connect('scott', 'tiger', '192.168.1.100:1521/ORCL', 'AL32UTF8');
if (!$conn) {
    $e = oci_error();
    die("連線失敗:" . $e['message']);
}

// 建立資料表 (示範)
$ddl = "CREATE TABLE php_test (id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, name VARCHAR2(50), created_at DATE DEFAULT SYSDATE)";
$stid = oci_parse($conn, $ddl);
oci_execute($stid);
echo "資料表建立成功<br>";

// 新增 (綁定變數)
$sql = "INSERT INTO php_test (name) VALUES (:name)";
$stid = oci_parse($conn, $sql);
$names = ['Alice', 'Bob', '中文測試'];
foreach ($names as $name) {
    oci_bind_by_name($stid, ':name', $name);
    oci_execute($stid, OCI_COMMIT_ON_SUCCESS);
}
echo "新增完成<br>";

// 查詢
$sql = "SELECT id, name, created_at FROM php_test ORDER BY id";
$stid = oci_parse($conn, $sql);
oci_execute($stid);

echo "<table border=1><tr><th>ID</th><th>Name</th><th>Created</th></tr>";
while ($row = oci_fetch_array($stid, OCI_ASSOC + OCI_RETURN_NULLS)) {
    echo "<tr>";
    foreach ($row as $item) {
        echo "<td>" . ($item !== null ? htmlspecialchars($item) : " ") . "</td>";
    }
    echo "</tr>";
}
echo "</table>";

// 更新
$sql = "UPDATE php_test SET name = :name WHERE id = :id";
$stid = oci_parse($conn, $sql);
oci_bind_by_name($stid, ':name', 'Alice Updated');
oci_bind_by_name($stid, ':id', 1);
oci_execute($stid, OCI_COMMIT_ON_SUCCESS);

// 刪除
$sql = "DELETE FROM php_test WHERE id = :id";
$stid = oci_parse($conn, $sql);
oci_bind_by_name($stid, ':id', 2);
oci_execute($stid, OCI_COMMIT_ON_SUCCESS);

// 釋放資源
oci_free_statement($stid);
oci_close($conn);
?>

PDO_OCI 範例 (可攜性優先)

<?php
$dsn = 'oci:dbname=192.168.1.100:1521/ORCL;charset=AL32UTF8';
$user = 'scott';
$pass = 'tiger';

try {
    $pdo = new PDO($dsn, $user, $pass, [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false,
        PDO::ATTR_STRINGIFY_FETCHES => false, // 保持數字型別
    ]);

    // 查詢
    $stmt = $pdo->query("SELECT id, name FROM php_test");
    foreach ($stmt as $row) {
        echo "{$row['ID']} - {$row['NAME']}<br>"; // Oracle 欄位名預設大寫
    }

    // 新增 (Named placeholder)
    $stmt = $pdo->prepare("INSERT INTO php_test (name) VALUES (:name)");
    $stmt->execute([':name' => 'PDO User']);

} catch (PDOException $e) {
    die("PDO 錯誤:" . $e->getMessage());
}
?>

關鍵最佳實務

1. 永遠使用綁定變數

// OCI8
oci_bind_by_name($stid, ':id', $id, -1, SQLT_INT); // 明確指定型別

// PDO
$stmt->bindValue(':id', $id, PDO::PARAM_INT);

防止 SQL 注入、提升解析重用效能、避免隱性型別轉換錯誤。

2. 字元集統一為 AL32UTF8

  • 連線參數指定 AL32UTF8(Oracle UTF-8 實作)。
  • 資料庫字元集確認:SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER='NLS_CHARACTERSET';
  • PHP 內部編碼 mb_internal_encoding('UTF-8');。

3. 交易控制

// OCI8
oci_execute($stid, OCI_NO_AUTO_COMMIT); // 手動控制
oci_commit($conn);
oci_rollback($conn);

// PDO
$pdo->beginTransaction();
$pdo->commit();
$pdo->rollBack();

4. 大物件 (LOB) 處理

// 寫入 CLOB
$lob = oci_new_descriptor($conn, OCI_D_LOB);
$stid = oci_parse($conn, "INSERT INTO docs (content) VALUES (EMPTY_CLOB()) RETURNING content INTO :lob");
oci_bind_by_name($stid, ':lob', $lob, -1, OCI_B_CLOB);
oci_execute($stid, OCI_NO_AUTO_COMMIT);
$lob->save($largeText);
$lob->free();
oci_commit($conn);

// 讀取 CLOB
$stid = oci_parse($conn, "SELECT content FROM docs WHERE id = :id");
oci_bind_by_name($stid, ':id', $id);
oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC);
$content = $row['CONTENT']->load(); // LOB 物件需呼叫 load()

5. 連線池與持久連線

// OCI8 持久連線 (慎用,需配合 Oracle DRCP)
$conn = oci_pconnect('user', 'pass', 'db', 'AL32UTF8');

// PDO 持久連線
$pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_PERSISTENT => true]);

高併發建議配合 Oracle Database Resident Connection Pooling (DRCP) 使用 oci_pconnect,否則易耗盡資料庫程序。

常見錯誤與排查

錯誤訊息原因解決
ORA-12154: TNS:could not resolve the connect identifierDSN 格式錯誤、tnsnames.ora 未配置改用 Easy Connect 格式、檢查 tnsnames.ora 路徑權限
ORA-12541: TNS:no listenerListener 未啟動、IP/埠錯誤、防火牆阻擋tnsping 測試、檢查 1521 埠連通性
ORA-28040: No matching authentication protocolOracle 12c+ 預設禁用舊版認證sqlnet.ora 加入 SQLNET.ALLOWED_LOGON_VERSION_SERVER=8
OCIEnvNlsCreate failedInstant Client 未正確安裝/環境變數缺失檢查 LD_LIBRARY_PATH、ORACLE_HOME、重啟 php-fpm
Character set mismatch連線字元集與資料庫不符統一指定 AL32UTF8

總結

  • 新專案首選 OCI8:功能完整、支援 LOB/集合/物件型別、效能最佳。
  • 跨資料庫專案用 PDO_OCI:犧牲部分 Oracle 進階功能,換取程式碼可攜性。
  • 必做三件事:綁定變數、字元集 AL32UTF8、錯誤模式設為 Exception。
  • 生產環境:配合 DRCP 使用持久連線、啟用慢查詢日誌、定期分析 AWR 報告。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved