PHP Conference Japan 2024

oci_connect

(PHP 5, PHP 7, PHP 8, PECL OCI8 >= 1.1.0)

oci_connect連線到 Oracle 資料庫

說明

oci_connect(
    字串 $username,
    字串 $password,
    ?字串 $connection_string = null,
    字串 $encoding = "",
    整數 (int) $session_mode = OCI_DEFAULT
): 資源 (resource)|false

傳回大多數其他 OCI8 操作所需的連線識別碼。

為了效能考量,大多數應用程式應該使用 oci_pconnect() 建立持久連線,而不是使用 oci_connect()。有關連線管理和連線池的一般資訊,請參閱 連線處理

使用相同參數第二次及後續呼叫 oci_connect() 將傳回第一次呼叫所傳回的連線控制代碼。這表示一個控制代碼中的交易也存在於其他控制代碼中,因為它們使用相同的底層資料庫連線。如果兩個控制代碼需要彼此間的交易隔離,請改用 oci_new_connect()

參數

username (使用者名稱)

Oracle 使用者名稱。

password (密碼)

username 的密碼。

connection_string (連線字串)

包含要連線的 Oracle 執行個體。它可以是 » 簡易連線字串 (Easy Connect string),或是 tnsnames.ora 檔案中的連線名稱,或是本機 Oracle 執行個體的名稱。

如果未指定或為 null,PHP 會使用環境變數,例如 TWO_TASK(在 Linux 上)或 LOCAL(在 Windows 上)和 ORACLE_SID 來決定要連線的 Oracle 執行個體

要使用簡易連線命名方法,PHP 必須與 Oracle 10g 或更高版本的用戶端程式庫連結。Oracle 10g 的簡易連線字串格式為:_ [//]host_name[:port][/service_name] _。從 Oracle 11g 開始,語法為:_ [//]host_name[:port][/service_name][:server_type][/instance_name] _。Oracle 19c 引入了更多選項,包括逾時和保持連線設定。請參閱 Oracle 文件。服務名稱可以透過在資料庫伺服器機器上執行 Oracle 公用程式 lsnrctl status 來找到。

tnsnames.ora 檔案可以在 Oracle Net 搜尋路徑中,其中包括 /your/path/to/instantclient/network/admin$ORACLE_HOME/network/admin/etc。或者,設定 TNS_ADMIN 以便讀取 $TNS_ADMIN/tnsnames.ora。請確保 Web 常駐程式具有檔案的讀取權限。

encoding (編碼)

決定 Oracle 用戶端程式庫使用的字元集。字元集不需要與資料庫使用的字元集相符。如果不相符,Oracle 會盡力將資料轉換成資料庫字元集或從資料庫字元集轉換。根據字元集的不同,這可能無法產生可用的結果。轉換也會增加一些時間成本。

如果未指定,Oracle 用戶端程式庫會從 NLS_LANG 環境變數決定字元集。

傳遞此參數可以減少連線所需的時間。

session_mode (工作階段模式)

此參數自 PHP 5 (PECL OCI8 1.1) 版本起可用,並接受下列值:OCI_DEFAULTOCI_SYSOPEROCI_SYSDBA。如果指定了 OCI_SYSOPEROCI_SYSDBA,此函式將嘗試使用外部憑證建立特權連線。特權連線預設為停用。要啟用它們,您需要將 oci8.privileged_connect 設定為 On

PHP 5.3 (PECL OCI8 1.3.4) 引入了 OCI_CRED_EXT 模式值。這會告知 Oracle 使用外部或作業系統驗證,這必須在資料庫中設定。OCI_CRED_EXT 旗標只能與使用者名稱 "/" 和空密碼一起使用。oci8.privileged_connect 可以是 OnOff

OCI_CRED_EXT 可以與 OCI_SYSOPEROCI_SYSDBA 模式組合使用。

基於安全性考量,Windows 不支援 OCI_CRED_EXT

返回值

傳回連線識別碼,或在發生錯誤時傳回 false

更新日誌

版本 說明
8.0.0, PECL OCI8 3.0.0 connection_string 現在可以為 null。

範例

範例 #1 使用 Easy Connect 語法的基本 oci_connect()

<?php

// 連線到位於 "localhost" 機器上的 XE 服務 (即資料庫)
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}

$stid = oci_parse($conn, 'SELECT * FROM employees');
oci_execute($stid);

echo
"<table border='1'>\n";
while (
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {
echo
"<tr>\n";
foreach (
$row as $item) {
echo
" <td>" . ($item !== null ? htmlentities($item, ENT_QUOTES) : "&nbsp;") . "</td>\n";
}
echo
"</tr>\n";
}
echo
"</table>\n";

?>

範例 #2 使用網路連線名稱進行基本的 oci_connect() 連線

<?php

// 連線到 tnsnames.ora 檔案中描述的 MYDB 資料庫,
// MYDB 的一個 tnsnames.ora 範例項目可能是:
// MYDB =
// (DESCRIPTION =
// (ADDRESS = (PROTOCOL = TCP)(HOST = mymachine.oracle.com)(PORT = 1521))
// (CONNECT_DATA =
// (SERVER = DEDICATED)
// (SERVICE_NAME = XE)
// )
// )

$conn = oci_connect('hr', 'welcome', 'MYDB');
if (!
$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}

$stid = oci_parse($conn, 'SELECT * FROM employees');
oci_execute($stid);

echo
"<table border='1'>\n";
while (
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {
echo
"<tr>\n";
foreach (
$row as $item) {
echo
" <td>" . ($item !== null ? htmlentities($item, ENT_QUOTES) : "&nbsp;") . "</td>\n";
}
echo
"</tr>\n";
}
echo
"</table>\n";

?>

範例 #3 使用明確字元集的 oci_connect()

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/XE', 'AL32UTF8');
if (!
$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}

$stid = oci_parse($conn, 'SELECT * FROM employees');
oci_execute($stid);

echo
"<table border='1'>\n";
while (
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {
echo
"<tr>\n";
foreach (
$row as $item) {
echo
" <td>" . ($item !== null ? htmlentities($item, ENT_QUOTES) : "&nbsp;") . "</td>\n";
}
echo
"</tr>\n";
}
echo
"</table>\n";

?>

範例 #4 使用多次呼叫 oci_connect()

<?php

$c1
= oci_connect("hr", "welcome", 'localhost/XE');
$c2 = oci_connect("hr", "welcome", 'localhost/XE');

// Both $c1 and $c2 show the same PHP resource id meaning they use the
// same underlying database connection
echo "c1 is $c1<br>\n";
echo
"c2 is $c2<br>\n";

function
create_table($conn)
{
$stmt = oci_parse($conn, "create table hallo (test varchar2(64))");
oci_execute($stmt);
echo
"Created table<br>\n";
}

function
drop_table($conn)
{
$stmt = oci_parse($conn, "drop table hallo");
oci_execute($stmt);
echo
"Dropped table<br>\n";
}

function
insert_data($connname, $conn)
{
$stmt = oci_parse($conn, "insert into hallo
values(to_char(sysdate,'DD-MON-YY HH24:MI:SS'))"
);
oci_execute($stmt, OCI_DEFAULT);
echo
"$connname inserted row without committing<br>\n";
}

function
rollback($connname, $conn)
{
oci_rollback($conn);
echo
"$connname rollback<br>\n";
}

function
select_data($connname, $conn)
{
$stmt = oci_parse($conn, "select * from hallo");
oci_execute($stmt, OCI_DEFAULT);
echo
"$connname ----selecting<br>\n";
while (
oci_fetch($stmt)) {
echo
" " . oci_result($stmt, "TEST") . "<br>\n";
}
echo
"$connname ----done<br>\n";
}

create_table($c1);

insert_data('c1', $c1); // Insert a row using c1
sleep(2); // sleep to show a different timestamp for the 2nd row
insert_data('c2', $c2); // Insert a row using c2

select_data('c1', $c1); // Results of both inserts are returned
select_data('c2', $c2); // Results of both inserts are returned

rollback('c1', $c1); // Rollback using c1

select_data('c1', $c1); // Both inserts have been rolled back
select_data('c2', $c2);

drop_table($c1);

// Closing one of the connections makes the PHP variable unusable, but
// the other could be used
oci_close($c1);
echo
"c1 is $c1<br>\n";
echo
"c2 is $c2<br>\n";


// Output is:
// c1 is Resource id #5
// c2 is Resource id #5
// Created table
// c1 inserted row without committing
// c2 inserted row without committing
// c1 ----selecting
// 09-DEC-09 12:14:43
// 09-DEC-09 12:14:45
// c1 ----done
// c2 ----selecting
// 09-DEC-09 12:14:43
// 09-DEC-09 12:14:45
// c2 ----done
// c1 rollback
// c1 ----selecting
// c1 ----done
// c2 ----selecting
// c2 ----done
// Dropped table
// c1 is
// c2 is Resource id #5

?>

注意事項

注意:

安裝或設定不正確的 OCI8 擴充功能通常會表現為連線問題或錯誤。請參閱安裝/設定以取得疑難排解資訊。

另請參閱

新增註釋

使用者貢獻的註釋 9 則註釋

M0no at ethonfusino dot com
22 年前
如果您的 Oracle 資料庫位於您區域網路中的遠端系統上,而且您不想擔心 tnsnames 檔案,您可以嘗試這樣做。

$db = "(DESCRIPTION=(ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.XX.XXX)(PORT = 1521)))(CONNECT_DATA=(SID=XXXX)))";

$c1 = ocilogon("name","password",$db);

希望這對某些人有所幫助。
Leandro da Cunha Campos
15 年前
連線到 Oracle RAC「Real Application Clusters」的另一種方法

<?php
$dbstr
="(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST =ip1)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = banco)
(INSTANCE_NAME = banco1)))"
;

$dbstr1 ="(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST =ip2)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = banco)
(INSTANCE_NAME = banco2)))"
;

if(!@(
$conn = oci_connect('user','password',$dbstr1)))
{
$conn = oci_connect('user','password',$dbstr) or die (ocierror()); }
?>
sixd at php dot net
16 年前
如果想要在網路問題發生時指定連線逾時,您可以編輯用戶端(例如 PHP 端)的 sqlnet.ora 檔案並設定 SQLNET.OUTBOUND_CONNECT_TIMEOUT。這會設定建立連線到資料庫的最長時間限制,包括嘗試連線到其他服務的時間。它從 Oracle 10.2.0.3 版開始提供。

在 Oracle 11.1 中,引入了更輕量級的解決方案 TCP.CONNECT_TIMEOUT。它也是 sqlnet.ora 參數。它僅限制 TCP 連線建立時間,這通常是出現連線問題的地方。

用戶端 sqlnet.ora 檔案應與 tnsnames.ora 檔案放在同一個目錄中。
sebastien.barbieri _at_ gmail dot com
18 年前
當您使用 Oracle 9.2+ 時,我認為您必須使用 CHARSET 參數。

當然,在出現 accented 字元之前您不會注意到它... 所以只需指定它,就可以避免一個大麻煩。

例如,以下是我們的 Oracle 內部設定
select * from nls_database_parameters;

參數 值
------------------------------ ----------------------------------------

NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_ISO_CURRENCY AMERICA
NLS_CHARACTERSET WE8ISO8859P15


以及我們的 oci_connect 呼叫

$dbch=ocilogon($user,$pass,$connectString,"WE8ISO8859P15");

如果沒有這個,您會得到問號(反轉)、方塊…而不是大多數 accented 字元。

寫入和讀取時都別忘了使用它。
blake dot lewis at gmail dot com
11 年前
使用 ldap 進行 Oracle 名稱解析

除非使用預設位置,否則網頁伺服器將需要環境變數 TNS_ADMIN='tnsname.ora 所在目錄'。我使用 '/etc/tns_admin'。使用 phpinfo() 確認。

TNS_ADMIN 位置需要三個檔案:tnsnames.ora、sqlnet.ora 和 ldap.ora。如果您只使用 ldap,則不需要 tnsnames.ora。

在 sqlnet.ora 中加入
NAMES.DIRECTORY_PATH=(TNSNAMES,LDAP)

在 ldap.ora 中加入
DIRECTORY_SERVERS=(ldap_server_fqdn:port)
DEFAULT_ADMIN_CONTEXT=""
DIRECTORY_SERVER_TYPE=OID

若要快速又簡便地設定 ldap tnsnames 伺服器,請使用 Dave Berry 開發的 tnsManager。也可以使用 Oracle OID 或 Openldap,但設定較為複雜。tnsManager 非常容易上手。快速的部分:提供一個 tnsnames.ora 檔案並啟動它。簡便的部分:我無法讓 Toad 和 SQLDeveloper 與它一起使用,它會忽略網域名稱,而且它已不再維護。

sqlnet.ora 中 NAMES.DIRECTORY_PATH 的值順序決定了使用哪個查詢「轉接器」,在本例中,它是 tnsnames.ora 檔案,然後是 ldap。我使用 ldap 作為一般用途,並使用 tnsnames.ora 檔案覆蓋 ldap 或非供一般用途的項目。

如果您有完整的 Oracle 用戶端,則可以使用 tnsping。『tnsping ORACLE_SID』會告訴您正在使用哪個轉接器:『已使用 LDAP 轉接器來解析別名』。

<?php
echo system("/PATH/tnsping ".$ORACLE_SID." 2>&1")."<br />";
echo
'TNS_ADMIN='.getenv('TNS_ADMIN');
?>

問題
如果僅使用 ORACLE_SID 而不是 ORACLE_SID.DB_DOMAIN 進行連線,則會附加 sqlnet.ora 中 NAMES.DEFAULT_DOMAIN 的值,然後由於某些原因,PHP 會嘗試使用 HOSTNAME 轉接器,如果資料庫名稱在 DNS 中可以解析,則使用資料庫名稱作為主機名稱的連線將會失敗,因為 SID 和 SERVICE_NAME 都未定義。
如果使用 tnsManager,請在 $ORACLE_SID 後面附加 '.ANY_DOMAIN' 以解決上述問題。

我已使用以下版本測試過
11.1.0.7 完整用戶端和 PHP 5.1.6
11.2.0.2 完整用戶端和 PHP 5.4.11

我聽說 LDAP 查詢無法與較舊的 instantclient 搭配使用。
drew dot carmichael at gmail dot com
15 年前
在 php 中使用 OCI_CRED_EXT 時
如果設定了環境變數 $ORACLE_SID,則不需要明確指定資料庫,除非在建立連線時提供 NULL 的資料庫值,否則連線將會失敗。

$ORACLE_SID 的優先順序高於連線的 TNS 名稱查詢。因此,即使在 DB 參數中手動設定連線字串也會失敗。

因此,當設定了 $ORACLE_SID 環境變數時,傳遞 NULL 而不是資料庫名稱即可成功連線。

希望這可以讓您在遷移到 %.3 和作業系統驗證時少掉一些煩惱。
Jonathon Robinson
14 年前
關於文件中以下的敘述
「使用相同參數第二次以及後續呼叫 oci_connect() 將會傳回第一次呼叫所傳回的連線控制代碼。」

這裡有一個需要注意的地方。只有在保留對原始控制代碼的參考時,後續呼叫 oci_connect() 才會傳回與第一次呼叫相同的連線控制代碼。

例如,以下程式碼將會產生 *一個* 連線控制代碼

<?php
$dbh
= oci_connect($username, $password, $conn_info);
// 執行一些操作
$dbh = oci_connect($username, $password, $conn_info);
// 執行更多操作

以下程式碼將會產生*兩個* 連線控制代碼:
getData();
// 執行一些操作
getData();
// 執行更多操作

getData() {
$dbh = oci_connect($username, $password, $conn_info);
// 執行一些操作
}
?>

這是 PHP 在方法作用域結束時對控制代碼進行垃圾回收的結果。

如果您想透過函式呼叫來隔離您的資料庫層,但仍然想利用 oci_connect 可以返回相同控制代碼的特性,只需保留對控制代碼的參考,如下所示

<?php
getData
($username, $password, $conn_info) {
$dbh = oci_connect($username, $password, $conn_info);
$key = hash('md5', "$username|$password|$conn_info");
$GLOBALS[$key] = $dbh;
// 執行一些操作
}
?>

我原本將此記錄為一個錯誤,但顯然這是預期的行為,可能是因為 oci_close($dbh) 只是呼叫 unset($dbh)。
ben at onshop dot co dot uk
19 年前
David Sklar 和 Adam Trachtenberg 編寫的《PHP Cookbook》(O'Reilly)中提供了一個保護連線資訊的有效解決方案。他們建議在 Apache 設定中使用「SetEnv」,然後在腳本中使用 $_SERVER 存取這些值。

遺憾的是,使用「SetEnv」解決方案會將您的連線資訊暴露給該虛擬主機的所有使用者。如果他們執行 phpinfo.php 或顯示 $_SERVER,我發現他們將會看到該虛擬主機根目錄下任何檔案的密碼。

要將暴露範圍限制在特定目錄或特定檔案

1. 首先在 httpd.conf 中加入一個「Include」到秘密檔案。例如

Include "/web/private/secret.txt"

2. 在密碼檔案中,使用 'SetEnvIf' 指令,僅針對特定目錄或檔案啟用環境變數。例如:

- 適用於目錄中的所有檔案

SetEnvIf Request_URI "/path/to/my/directory" ORACLE_PASS=5gHj790j

- 適用於目錄中的特定檔案

SetEnvIf Request_URI "/path/to/my/directory/connection.oracle.php" ORACLE_PASS=5gHj790j
blake dot lewis at gmail dot com
11 年前
使用 ldap 進行 Oracle 名稱解析

網頁伺服器需要環境變數 TNS_ADMIN='tnsname.ora 所在目錄'。我使用的是 '/etc/tns_admin'。請使用 phpinfo() 函式確認。

TNS_ADMIN 位置需要三個檔案:tnsnames.ora、sqlnet.ora 和 ldap.ora。如果您只使用 ldap,則不需要 tnsnames.ora。

在 sqlnet.ora 中加入
NAMES.DIRECTORY_PATH=(TNSNAMES,LDAP)

在 ldap.ora 中加入
DIRECTORY_SERVERS=(ldap_fqdn_hostname:1575)
DEFAULT_ADMIN_CONTEXT=""
DIRECTORY_SERVER_TYPE=OID

若要快速設定 ldap tnsnames 伺服器,可以使用 Dave Berry 開發的 tnsManager。也可以使用 Oracle OID 或 Openldap,但設定較為複雜。tnsManager 則非常簡單易用。預設埠號為 1575。

sqlnet.ora 中 NAMES.DIRECTORY_PATH 的值順序決定了優先使用的查詢「配接器」,在此例中,首先使用的是 tnsnames.ora 檔案,然後是 ldap。我使用 ldap 作為一般用途,並使用 tnsnames.ora 檔案覆蓋 ldap 或不供一般用途的項目。

如果您有完整的 Oracle 用戶端,則可以使用 tnsping。『tnsping ORACLE_SID』會告訴您正在使用哪個轉接器:『已使用 LDAP 轉接器來解析別名』。

<?php
echo system("/PATH/tnsping ".$ORACLE_SID." 2>&1")."<br />";
echo
'TNS_ADMIN='.getenv('TNS_ADMIN');
?>

問題
基於某些原因,PHP 會先嘗試 HOSTNAME 配接器,如果資料庫名稱在 DNS 中可以解析,它會嘗試使用資料庫名稱作為主機名稱進行連線,而不定義 SID 或 SERVICE_NAME。我用過的所有其他 Oracle 用戶端都不會嘗試使用 HOSTNAME 配接器,除非它列在 NAMES.DIRECTORY_PATH 中。
我聽說 LDAP 查詢不適用於較舊版本的 Instant Client。
To Top