oci_connect

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

oci_connect连接到 Oracle 数据库

说明

oci_connect(
    string $username,
    string $password,
    ?string $connection_string = null,
    string $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 实例。它可以是 » 简单连接字符串,或 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 现在可以为空。

范例

范例 #1 使用简单连接语法的基本 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');

// 两个变量 $c1 和 $c2 都是同一个 PHP 资源 ID,表示它们使用相同的底层数据库连接
//
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); // 使用 c1 插入一行数据
sleep(2); // 睡眠 2 秒,以便第二行数据的 timestamp 与第一行不同
insert_data('c2', $c2); // 使用 c2 插入一行数据

select_data('c1', $c1); // 返回两个插入的数据的结果
select_data('c2', $c2); // 返回两个插入的数据的结果

rollback('c1', $c1); // 使用 c1 回滚操作

select_data('c1', $c1); // 两个插入的数据都被回滚了
select_data('c2', $c2);

drop_table($c1);

// 关闭其中一个连接,PHP 变量将不可用,但另一个连接仍然可以被使用
oci_close($c1);
echo
"c1 is $c1<br>\n";
echo
"c2 is $c2<br>\n";


// 输出结果如下:
// 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 扩展安装或配置错误,通常会表现为连接问题或错误。查看 安装/配置 页面以获取故障排除信息。

参见

添加备注

用户贡献的笔记 12 笔记

M0no at ethonfusino dot com
21 年前
如果您的 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);

希望对某些人有所帮助。
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 文件相同的目录中。
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()); }
?>
sebastien.barbieri _at_ gmail dot com
17 年前
当使用 Oracle 9.2+ 时,可以说您必须使用 CHARSET 参数。

当然,您只有在遇到带重音符号的字符时才会注意到它……因此,只需指定它,您就可以避免很大的麻烦。

例如,这是我们的 Oracle 内部配置
select * from nls_database_parameters;

PARAMETER VALUE
------------------------------ ----------------------------------------

NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_ISO_CURRENCY AMERICA
NLS_CHARACTERSET WE8ISO8859P15


我们的 oci_connect 调用如下

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

如果没有它,您将获得问号(倒置)、方块……而不是大多数带重音符号的字符。

不要忘记在写入和读取时都使用它。
drew dot carmichael at gmail dot com
15 年前
在 PHP 中使用 OCI_CRED_EXT 时
如果 ENV $ORACLE_SID 已设置,则不需要显式指定 DB,除非您在创建连接时提供 NULL DB 值,否则连接将失败。

连接时,$ORACLE_SID 将覆盖 TNS 名称查找。因此,即使在 DB 参数中使用手动连接字符串也会失败。

因此,当设置 $ORACLE_SID 环境变量时,传递 NULL 而不是数据库名称就可以成功连接。

希望这能帮你在迁移到 %.3 和 OS 身份验证时节省一些头发。
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 在方法范围结束时对句柄进行垃圾回收的结果。

如果你想通过函数调用隔离你的 DB 层,但仍然想利用 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)。
blake dot lewis at gmail dot com
11 年前
使用 LDAP 进行 Oracle 名称解析

除非使用默认位置,否则 Web 服务器需要环境变量 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 查找不适用于旧的 instantclients。
ben at onshop dot co dot uk
19 年前
在 David Sklar 和 Adam Trachtenberg 编写的《PHP Cookbook》(O'Reilly)中,有一个针对在 PHP 中保护连接信息的问题的有用解决方案。他们建议在 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 名称解析

Web 服务器需要环境变量 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 查找不适用于旧的 instantclients。
jtacon at php dot net
13 年前
如果你升级 OCI8,请确保使用最新的 Oracle instantclient 版本,否则由于与某些版本不兼容,你可能会收到“ORA-24315: illegal attribute type”错误,该错误会在尝试连接时出现。

希望有帮助,
Javier Tacón
peter at goldthorp dot com
13 年前
当我将数据库实例从 10.2 升级到 11.2 时,我开始收到“ORA-12514: TNS:listener does not currently know of service requested in connect descriptor”错误。由于某种原因,它无法解析使用 Easy Connect 语法进行的 oci_connect 调用(来自 sqlplus 的 Easy Connect 调用没有问题)。我能够通过传递一个 tns 连接字符串来解决此问题

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

$c1 = oci_connect("name","password",$db);
?>
Anonymous
12 年前
如何使用 OID(Oracle Internet Directory)从 PHP 连接到 Oracle 数据库

OID 就像一个查找索引,其中包含连接到各种数据库的连接字符串。没有 OID,数据库连接字符串将直接存储在代码设置中,并由 PHP 代码用于连接到数据库。使用 OID,可以对 OID LDAP 进行查找以获取数据库连接字符串。然后,PHP 代码将使用获取的数据库连接字符串像以前一样连接到数据库。

OID 允许 DBA 管理/更改应用程序使用的数据库服务器,而无需更改 PHP 应用程序本身或应用程序服务器上的任何数据库设置。

以下是 PHP 通过 OID 连接到数据库的基本步骤

- DBA 应向你提供 OID LDAP 的连接信息,以及数据库连接的用户名/密码。
- 使用提供的 information 连接到 OID LDAP
- 搜索适当的 LDAP 记录
- 从记录属性“orclnetdescstring”中获取连接字符串数据。
- 关闭 LDAP 连接
- 使用获取的连接字符串数据,使用提供的数据库用户名/密码像往常一样连接到数据库。

以下是一些基本示例代码来执行此操作

// 从 OID LDAP 获取连接字符串

$ds=ldap_connect($servername,$serverport); // 连接到 ldap
$r = ldap_bind($ds); // 连接到 ldap
$sr = ldap_search($ds, "cn=OracleContext,dc=___,dc=___,dc=___", "cn=$sid"); // 执行查询
$info = ldap_get_entries($ds, $sr); // 获取条目
ldap_close($ds); // 关闭连接

$dbconnectstring = $info[0]["orclnetdescstring"][0]; // 从 ldap 搜索结果数组中提取数据库连接字符串

// 使用从 OID 获取的连接字符串连接到数据库

$dbconnection = oci_connect($username, $password, $dbconnectstring);
To Top