How to connect from PHP to an Oracle database using OID (Oracle Internet Directory):
OID is like a lookup index that contains connection strings for connecting to various databases. Without OID, a database connection string would be stored directly in the code settings and used by the PHP code to connect to a database. With OID, a lookup can be made to the OID LDAP to acquire the database connection string. Then the PHP code will use the acquired database connection string to connect to the database as before.
OID allows a DBA to manage/change which database server that an application uses without having to change any database settings in the PHP application itself or on the application server.
Here are the basic steps for PHP to connect to a database via OID:
- The DBA should provide you the connection information for the OID LDAP as well as the username/password for the database connection.
- Connect to the OID LDAP using the provided information
- Search for the appropriate LDAP record
- Get the connection string data from the record attribute, \"orclnetdescstring\"
- Close the LDAP connection
- Use the acquired connection string data to connect to the database as usual using the provided database username/password.
Here is basic sample code to do this:
// Get connection string from OID LDAP
$ds=ldap_connect($servername,$serverport); // Connect to ldap
$r=ldap_bind($ds); // Bind to ldap
$sr = ldap_search($ds, \"cn=OracleContext,dc=___,dc=___,dc=___\", \"cn=$sid\"); // Run query
$info = ldap_get_entries($ds, $sr); // Get entries
ldap_close($ds); // Close connection
$dbconnectstring = $info[0][\"orclnetdescstring\"][0]; // Extract db connect string from ldap search result array
// Connect to database using acquired connection string from OID
$dbconnection = oci_connect ($username,$password,$dbconnectstring);
oci_connect
(PHP 5, PECL OCI8 >= 1.1.0)
oci_connect — Connect to an Oracle database
설명
Returns a connection identifier needed for most other OCI8 operations.
See Connection Handling for general information on connection management and connection pooling.
From PHP 5.1.2 (PECL OCI8 1.1) oci_close() can be used to close the connection.
The second and subsequent calls to oci_connect() with the same parameters will return the connection handle returned from the first call. This means that transactions in one handle are also in the other handles, because they use the same underlying database connection. If two handles need to be transactionally isolated from each other, use oci_new_connect() instead.
인수
- username
-
The Oracle user name.
- password
-
The password for username.
- connection_string
-
접속을 위해 오라클 인스턴스를 포함합니다. » Easy Connect string이나 tnsnames.ora 파일의 Connect Name, 로컬 오라클 인스턴스의 이름일 수 있습니다.
지정하지 않으면, PHP는 접속할 오라클 인스턴스를 찾기 위해 TWO_TASK(리눅스)나 LOCAL(윈도우), 그리고 ORACLE_SID 등의 환경 변수를 사용합니다.
Easy Connect naming 방식을 사용하려면, PHP가 오라클 10g 이상의 클라이언트 라이브러리를 사용해야 합니다. 오라클 10g에서 Easy Connect string 형식은: [//]host_name[:port][/service_name] 입니다. 오라클 11g에서는: [//]host_name[:port][/service_name][:server_type][/instance_name] 입니다. 서비스 이름은 데이터베이스 서버에서 오라클 유틸리티 lsnrctl status를 실행하여 찾을 수 있습니다.
tnsnames.ora 파일은 오라클 넷 검색 경로에 있을 수 있습니다. $ORACLE_HOME/network/admin과 /etc 등을 포함합니다. TNS_ADMIN을 설정하여 $TNS_ADMIN/tnsnames.ora를 읽게 할 수도 있습니다. 웹 데몬이 이 파일에 읽기 권한을 가지도록 설정하십시오.
- character_set
-
오라클 서버 버전 9.2 이상을 사용하면, 새로운 연결에 사용할 charset 인수를 지정할 수 있습니다. 오라클 서버 < 9.2를 사용한다면, 이 인수는 무시되고, 대신 NLS_LANG 환경 변수를 사용합니다.
- session_mode
-
This parameter is available since version PHP 5 (PECL OCI8 1.1) and accepts the following values: OCI_DEFAULT, OCI_SYSOPER and OCI_SYSDBA. If either OCI_SYSOPER or OCI_SYSDBA were specified, this function will try to establish privileged connection using external credentials. Privileged connections are disabled by default. To enable them you need to set oci8.privileged_connect to On.
PHP 5.3 (PECL OCI8 1.3.4) introduced the OCI_CRED_EXT mode value. This tells Oracle to use External or OS authentication, which must be configured in the database. The OCI_CRED_EXT flag can only be used with username of "/" and a empty password. oci8.privileged_connect may be On or Off.
OCI_CRED_EXT may be combined with the OCI_SYSOPER or OCI_SYSDBA modes.
OCI_CRED_EXT is not supported on Windows for security reasons.
반환값
Returns a connection identifier or FALSE on error.
예제
Example #1 Basic oci_connect() using Easy Connect syntax
<?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";
?>
Example #2 Basic oci_connect() using a Network Connect name
<?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";
?>
Example #3 oci_connect() with an explicit character set
<?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";
?>
Example #4 Using multiple calls to 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
?>
주의
Note:
An incorrectly installed or configured OCI8 extension will often manifest itself as a connection problem or error. See Installing/Configuring for troubleshooting information.
Note:
In PHP versions before 5.0.0 use ocilogon() instead. The old function name can still be used in current versions, however it is deprecated and not recommended.
참고
- oci_pconnect() - Connect to an Oracle database using a persistent connection
- oci_new_connect() - Connect to the Oracle server using a unique connection
- oci_close() - Closes an Oracle connection
I started getting "ORA-12514: TNS:listener does not currently know of service requested in connect descriptor" errors when I upgraded my database instance from 10.2 to 11.2. For some reason it would not resolve the oci_connect calls with the Easy Connect syntax (easy connect calls from sqlplus were fine). I was able to workaround the issue by passing a tns connect string:
<?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);
?>
If you upgrades the OCI8, be sure to use the latest oracle instantclient version, otherwise you can receive an "ORA-24315: illegal attribute type" when trying to connect due to incompatibility with some versions.
HTH,
Javier Tacón
If you have issues connecting to an Oracle database from a script that's run via cron, make sure that your ORACLE_HOME environment variable is set correctly.
Add
ORACLE_HOME=/path/to/oracle/client
to the top of your cron file
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).
In order to use UTF8 charset in Oracle, just indicate the bespoke charset in the connection function.
Example:
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE', 'AL32UTF8');
?>
Please look on the manual page of oci_connect for details.
If you also want to on the fly localize your app (date formatting, decimal separator, and so on) additionally use NLS values by alter session statements.
Never forget to indicate the app's charset to the browser for proper display.
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
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()); }
?>
From PHP 5.3 onwards:
A new OCI_CRED_EXT flag can be passed as the "session_mode" parameter
to oci_connect(), oci_new_connect() and oci_pconnect().
$c1 = oci_connect("/", "", $db, null, OCI_CRED_EXT);
This tells Oracle to do external or OS authentication, if configured
in the database.
OCI_CRED_EXT can only be used with username of "/" and a empty
password. Oci8.privileged_connection may be On or Off.
OCI_CRED_EXT is not supported on Windows for security reasons.
The new flag may be combined with the existing OCI_SYSOPER or
OCI_SYSDBA modes (note: oci8.privileged_connection needs to be On for
OCI_SYSDBA and OCI_SYSOPER), e.g.:
$c1 = oci_connect("/", "", $db, null, OCI_CRED_EXT+OCI_SYSOPER);
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.
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
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.
