如果您的 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);
希望对某些人有所帮助。
(PHP 5, PHP 7, PHP 8, PECL OCI8 >= 1.1.0)
oci_connect — 连接到 Oracle 数据库
$username
,$password
,$connection_string
= null
,$encoding
= "",$session_mode
= OCI_DEFAULT
返回大多数其他 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_DEFAULT
、OCI_SYSOPER
和 OCI_SYSDBA
。如果指定了 OCI_SYSOPER
或 OCI_SYSDBA
,此函数将尝试使用外部凭据建立特权连接。默认情况下,特权连接被禁用。要启用它们,你需要将 oci8.privileged_connect 设置为 On
。
PHP 5.3(PECL OCI8 1.3.4)引入了 OCI_CRED_EXT
模式值。这告诉 Oracle 使用外部或操作系统身份验证,这必须在数据库中配置。OCI_CRED_EXT
标志只能与用户名为 "/" 和空密码一起使用。 oci8.privileged_connect 可以是 On
或 Off
。
OCI_CRED_EXT
可以与 OCI_SYSOPER
或 OCI_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) : " ") . "</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) : " ") . "</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) : " ") . "</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 扩展安装或配置错误,通常会表现为连接问题或错误。查看 安装/配置 页面以获取故障排除信息。
如果您的 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);
希望对某些人有所帮助。
如果要指定连接超时时间,以防出现网络问题,可以编辑客户端(例如 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 文件相同的目录中。
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()); }
?>
当使用 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");
如果没有它,您将获得问号(倒置)、方块……而不是大多数带重音符号的字符。
不要忘记在写入和读取时都使用它。
在 PHP 中使用 OCI_CRED_EXT 时
如果 ENV $ORACLE_SID 已设置,则不需要显式指定 DB,除非您在创建连接时提供 NULL DB 值,否则连接将失败。
连接时,$ORACLE_SID 将覆盖 TNS 名称查找。因此,即使在 DB 参数中使用手动连接字符串也会失败。
因此,当设置 $ORACLE_SID 环境变量时,传递 NULL 而不是数据库名称就可以成功连接。
希望这能帮你在迁移到 %.3 和 OS 身份验证时节省一些头发。
关于文档中的以下语句
"对 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)。
使用 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。
在 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
使用 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。
如果你升级 OCI8,请确保使用最新的 Oracle instantclient 版本,否则由于与某些版本不兼容,你可能会收到“ORA-24315: illegal attribute type”错误,该错误会在尝试连接时出现。
希望有帮助,
Javier Tacón
当我将数据库实例从 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);
?>
如何使用 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);