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 = "",
    整数 $session_mode = OCI_DEFAULT
): 资源|false

返回大多数其他 OCI8 操作所需的连接标识符。

为了提高性能,大多数应用程序应该使用 oci_pconnect() 而不是 oci_connect() 来进行持久连接。有关连接管理和连接池的常规信息,请参阅 连接处理

使用相同参数对 oci_connect() 的第二次及后续调用将返回第一次调用返回的连接句柄。这意味着一个句柄中的事务也在其他句柄中,因为它们使用的是相同的底层数据库连接。如果两个句柄需要彼此之间进行事务隔离,请改用 oci_new_connect()

参数

username

Oracle 用户名。

password

username 的密码。

connection_string

包含要连接到的 Oracle 实例。它可以是 » Easy Connect 字符串tnsnames.ora 文件中的连接名称或本地 Oracle 实例的名称。

如果未指定或为 null,则 PHP 使用环境变量(例如 Linux 上的 TWO_TASK 或 Windows 上的 LOCALORACLE_SID)来确定要连接到的 Oracle 实例

要使用 Easy Connect 命名方法,PHP 必须与 Oracle 10g 或更高版本的客户端库链接。Oracle 10g 的 Easy Connect 字符串格式为:[//]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 使用 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');

// $c1 和 $c2 显示相同的 PHP 资源 ID,这意味着它们使用相同的底层数据库连接
// 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
"创建表<br>\n";
}

function
drop_table($conn)
{
$stmt = oci_parse($conn, "drop table hallo");
oci_execute($stmt);
echo
"删除表<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 插入行但不提交<br>\n";
}

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

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

create_table($c1);

insert_data('c1', $c1); // 使用 c1 插入一行
sleep(2); // 休眠以显示第二行的不同时间戳
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 变量无法使用,但另一个可以继续使用
// the other could be used
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
// 创建表
// c1 插入行但不提交
// c2 插入行但不提交
// c1 ----选择
// 09-DEC-09 12:14:43
// 09-DEC-09 12:14:45
// c1 ----完成
// c2 ----选择
// 09-DEC-09 12:14:43
// 09-DEC-09 12:14:45
// c2 ----完成
// c1 回滚
// c1 ----选择
// c1 ----完成
// c2 ----选择
// c2 ----完成
// 删除表
// 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 参数。

当然,除非遇到带重音符号的字符,否则您不会注意到它... 所以只需指定它,您就可以避免很多麻烦。

例如,这是我们 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");

如果没有它,您将得到问号(反向)、方块... 而不是大多数带重音符号的字符。

不要忘记在写入和读取时都使用它。
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,请将 '.ANY_DOMAIN' 附加到 $ORACLE_SID 以解决上述问题。

我已使用以下版本测试过
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 时
如果设置了 ENV $ORACLE_SID,则无需显式指定数据库,并且连接将失败,除非在创建连接时提供 NULL 数据库值。

$ORACLE_SID 优先于 TNS 名称查找连接。因此,即使在数据库参数中使用手动连接字符串也会失败。

因此,当设置了 $ORACLE_SID Env 时,传递 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 名称解析

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 客户端,除非在 NAMES.DIRECTORY_PATH 中列出,否则不会尝试使用 HOSTNAME 适配器。
我听说 LDAP 查找不适用于旧版本的 instantclients。
To Top