oci_connect
(PHP 5, PHP 7, PHP 8, PECL OCI8 >= 1.1.0)
oci_connect — 建立一个到 Oracle 服务器的连接
说明
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 实例
。可以是 » Easy Connect 串,或是 tnsnames.ora 文件中的连接名,或是本地 Oracle 实例名。如果不指定或者为
null
,PHP 使用环境变量来确定连接的Oracle 实例
,诸如TWO_TASK
(Linux 下)或LOCAL
(Windows 下)与ORACLE_SID
等。要使用 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 加入类更多选项,例如 timeout 和 keep-alive 设置。 请参考 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
模式组合使用。OCI_CRED_EXT
由于安全的原因不支持 Windows 系统。
返回值
返回连接标识符或出错时为 false
。
更新日志
版本 | 说明 |
---|---|
8.0.0、PECL OCI8 3.0.0 |
connection_string 现在可为 null。
|
示例
示例 #1 使用 Easy Connect 语法的基础 oci_connect()
<?php
// Connects to the XE service (i.e. database) on the "localhost" machine
$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 使用 Network Connect 名称的基础 oci_connect()
<?php
// Connects to the MYDB database described in tnsnames.ora file,
// One example tnsnames.ora entry for MYDB could be:
// 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');
// 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 扩展通常会表现为连接问题或错误。有关故障排除信息,请参阅安装/配置。
参见
- oci_pconnect() - 使用持久连接连,连接到 Oracle 数据库
- oci_new_connect() - 使用唯一连接,连接到 Oracle 服务器
- oci_close() - 关闭 Oracle 连接
用户贡献的备注 9 notes
If your oracle database is on a remote system within your local network and you don't want to worry about the tnsnames file you can try this.
$db = "(DESCRIPTION=(ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.XX.XXX)(PORT = 1521)))(CONNECT_DATA=(SID=XXXX)))";
$c1 = ocilogon("name","password",$db);
Hope this helps someone.
ONE ALTERNATIVE OF CONNECT IN 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()); }
?>
If you want to specify a connection timeout in case there is network problem, you can edit the client side (e.g. PHP side) sqlnet.ora file and set SQLNET.OUTBOUND_CONNECT_TIMEOUT. This sets the upper time limit for establishing a connection right through to the DB, including the time for attempts to connect to other services. It is available from Oracle 10.2.0.3 onwards.
In Oracle 11.1, a slightly lighter-weight solution TCP.CONNECT_TIMEOUT was introduced. It also is a sqlnet.ora parameter. It bounds just the TCP connection establishment time, which is mostly where connection problem are seen.
The client sqlnet.ora file should be put in the same directory as the tnsnames.ora file.
When you are using Oracle 9.2+ I would say that you MUST use the CHARSET parameter.
Of course, you will not notice it until there is accented character... so just specify it and you will avoid a big headache.
So for example here is our Oracle internal conf:
select * from nls_database_parameters;
PARAMETER VALUE
------------------------------ ----------------------------------------
…
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_ISO_CURRENCY AMERICA
NLS_CHARACTERSET WE8ISO8859P15
…
And there our oci_connect call:
$dbch=ocilogon($user,$pass,$connectString,"WE8ISO8859P15");
Without that, you will get question mark (inversed), squares… instead of most accented character.
Don’t forget to use that for writing as well as for reading.
Using ldap for Oracle name resolution:
The web server will need the environmental variable TNS_ADMIN='Directory of tnsname.ora' unless the default location is used. I use '/etc/tns_admin'. Confirm using the phpinfo().
There are three files needed in the TNS_ADMIN location: tnsnames.ora, sqlnet.ora and ldap.ora. If you are only using ldap, tnsnames.ora is not needed.
To sqlnet.ora add:
NAMES.DIRECTORY_PATH=(TNSNAMES,LDAP)
To ldap.ora add:
DIRECTORY_SERVERS=(ldap_server_fqdn:port)
DEFAULT_ADMIN_CONTEXT=""
DIRECTORY_SERVER_TYPE=OID
For a quick and dirty ldap tnsnames server use tnsManager by Dave Berry. Oracle OID or Openldap can used, but are complicated to setup. tnsManager is a no brainer. The quick part: give it a tnsnames.ora file and start it up. The dirty parts: I can't get Toad and SQLDeveloper to work with it, it ignores the domain and it is no longer being maintained.
The order of values in NAMES.DIRECTORY_PATH in sqlnet.ora determines which look up 'adapter' is used, in this case it is tnsnames.ora file and then ldap. I use ldap for general consumption and tnsnames.ora file to override ldap or entries that are not for general consumption.
If you have the full Oracle client you have tnsping. 'tnsping ORACLE_SID' will tell you what adapter you are using: 'Used LDAP adapter to resolve the alias'.
<?php
echo system("/PATH/tnsping ".$ORACLE_SID." 2>&1")."<br />";
echo 'TNS_ADMIN='.getenv('TNS_ADMIN');
?>
ISSUE:
If connecting with only ORACLE_SID and not ORACLE_SID.DB_DOMAIN, the value of NAMES.DEFAULT_DOMAIN from sqlnet.ora is appended, then for some reason PHP tries the HOSTNAME adapter, and if the database name resolves in DNS, it will fail connecting using the database name as the hostname, because neither SID nor SERVICE_NAME are defined.
If using tnsManager append '.ANY_DOMAIN' to $ORACLE_SID to hack around the issue above.
I have tested with:
11.1.0.7 full client and PHP 5.1.6
11.2.0.2 full client and PHP 5.4.11
I have heard that LDAP lookup does not work with older instantclients.
When using the OCI_CRED_EXT in php
if the ENV $ORACLE_SID is set the DB does not need to be specified explicitly and the connection will fail unless you provide a NULL DB value when creating the connection.
The $ORACLE_SID trumps the TNS name look up for the connection. So even a manual connection string in the DB parameter will fail.
So when the $ORACLE_SID Env is set a NULL passed instead of the DB name connects successfully.
Hope this saves some hair pulling when moving to %.3 and OS Authentications
Regarding the following statement in the documentation:
"The second and subsequent calls to oci_connect() with the same parameters will return the connection handle returned from the first call."
There's one caveat here. Subsequent calls to oci_connect() will only return the same connection handle as the first call as long as a reference is held to the original handle.
For example, the following code will generate *one* connection handle:
<?php
$dbh = oci_connect($username, $password, $conn_info);
// Do stuff
$dbh = oci_connect$username, $password, $conn_info);
// Do more stuff
The follow code will generate *two* connection handles:
getData();
// Do stuff
getData();
// Do more stuff
getData() {
$dbh = oci_connect($username, $password, $conn_info);
// Do stuff
}
?>
This is the result of PHP garbage collecting the handle at the end of the method scope.
If you want to isolate your DB layer through function calls but still want to leverage the fact that oci_connect can return the same handle, just keep a reference to the handle like so:
<?php
getData($username, $password, $conn_info) {
$dbh = oci_connect($username, $password, $conn_info);
$key = hash('md5', "$username|$password|$conn_info");
$GLOBALS[$key] = $dbh;
// Do stuff
}
?>
I originally logged this as a bug but apparently this is the expected behaviour, likely because oci_close($dbh) just calls unset($dbh).
There is a useful solution to the problem of securing connection information in the PHP Cookbook (O'Reilly) by David Sklar and Adam Trachtenberg. They propose using 'SetEnv' in the Apache configuration and then accessing the values from within a script using $_SERVER.
Unfortunately using the 'SetEnv' solution exposes your connection information to all users of that virtual host. If they run phpinfo.php or display $_SERVER, I found that they will see the password from any file under the root of that virtual host.
To restrict exposure to a particular directory or specific file:
1. First put an 'Include' to the secret file in httpd.conf. For example:
Include "/web/private/secret.txt"
2. In the password file, use the 'SetEnvIf' directive to enable the Environment variables by directory only or within a specific file. For example:
- For all files in the directory:
SetEnvIf Request_URI "/path/to/my/directory" ORACLE_PASS=5gHj790j
- For a specific file in the directory
SetEnvIf Request_URI "/path/to/my/directory/connection.oracle.php" ORACLE_PASS=5gHj790j
Using ldap for Oracle name resolution:
The web server needs the environmental variable TNS_ADMIN='Directory of tnsname.ora'. I use '/etc/tns_admin'. Confirm using the phpinfo() function.
There are three files needed in the TNS_ADMIN location: tnsnames.ora, sqlnet.ora and ldap.ora. If you are only using ldap, tnsnames.ora is not needed.
To sqlnet.ora add:
NAMES.DIRECTORY_PATH=(TNSNAMES,LDAP)
To ldap.ora add:
DIRECTORY_SERVERS=(ldap_fqdn_hostname:1575)
DEFAULT_ADMIN_CONTEXT=""
DIRECTORY_SERVER_TYPE=OID
For a quick and dirty ldap tnsnames server use tnsManager by Dave Berry. Oracle OID or Openldap can used, but are complicated to setup. tnsManager is a no brainer. The default port is 1575.
The order of values in NAMES.DIRECTORY_PATH from sqlnet.ora determines which look up 'adapter' is used first, in this case it is tnsnames.ora file and then ldap. I use ldap for general consumption and tnsnames.ora file to override ldap or entries that are not for general consumption.
If you have the full Oracle client you have tnsping. 'tnsping ORACLE_SID' will tell you what adapter you are using: 'Used LDAP adapter to resolve the alias'.
<?php
echo system("/PATH/tnsping ".$ORACLE_SID." 2>&1")."<br />";
echo 'TNS_ADMIN='.getenv('TNS_ADMIN');
?>
ISSUE:
For some reason PHP tries the HOSTNAME adapter first, and if the database name resolves in DNS, it will try connecting using the database name as the hostname with no SID or SERVICE_NAME defined. All other Oracle clients I have used will not try the HOSTNAME adapter unless it is listed in NAMES.DIRECTORY_PATH.
I have heard that LDAP look up does not work with older instantclients.