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
oci_connect
(PHP 5, PECL OCI8 >= 1.1.0)
oci_connect — Conecta a una base de datos de Oracle
Descripción
$username
, string $password
[, string $connection_string
[, string $character_set
[, int $session_mode
]]] )Devuelve un identificador de conexión necesario para la mayoría de las operaciones de OCI8.
Véase Manejo de conexiones para obtener información general sobre la gestión y agrupamiento de conexiones.
Desde PHP 5.1.2 (PECL OCI8 1.1), se puede usar oci_close() para cerrar conexiones.
La segunda y subsiguientes llamadas a oci_connect() con los mismos parámetros devolverán el gestor de conexión devuelto desde la primera llamada. Esto significa que las transacciones de un gestor también son las de los demás gestores, ya que usan la misma conexión subyacente a la base de datos. Si dos gestores necesitan ser aislados transaccionalmente uno de otro, use oci_new_connect() en su lugar.
Parámetros
-
username -
El nombre de usuario de Oracle.
-
password -
La contraseña para
username. -
connection_string -
Contiene la instancia de Oracle a la que conectarse. Puede ser una » cadena de Conexión Sencilla (Easy Connect), o un Nombre de Conexión del fichero tnsnames.ora, o el nombre de una instancia local de Oracle.
Si no se especifica, PHP utiliza variables de entorno como
TWO_TASK(en Linux) oLOCAL(en Windows) yORACLE_SIDpara determinar la instancia de Oracle a la que conectarse.Para utilizar el método de nomenclatura de Conexión Sencila, PHP debe estar vinculado con las bibliotecas cliente de Oracle 10g o superiores. La cadena de Conexión Sencilla para Oracle 10g se utiliza de la siguiente forma: [//]nombre_host[:puerto][/nombre_servicio]. Con Oracle 11g, la sintaxis es: [//]nombre_host[:puerto][/nombre_servicio][:tipo_servicio][/nombre_instancia]. Los nombres de servicio pueden encontrarse ejecuntado la utilidad de Oracle lsnrctl status en el servidor donde se encuentra la base de datos.
El fichero tnsnames.ora puede estar en la ruta de búsqueda de Oracle Net, la cual incluye $ORACLE_HOME/network/admin y /etc. De forma alternativa, establezca TNS_ADMIN para que $TNS_ADMIN/tnsnames.ora sea leído. Asegúrse que el demonio web tiene acceso de lectura al fichero.
-
character_set -
Determina el juego de caracteres usado por las bibliotecas cliente de Oracle. El juego de caracteres no tiene por que coinicidir con el utilizado en la base de datos. Si no coinicide, Oracle intentará hacer lo posible para convertir los datos entre los dos juegos de caracteres. Dependiendo del juego de caracteres que se esté usando, podría no proporcionarse resultados útiles. La conversión también añade algo de carga extra de tiempo.
Si no se especifica, las bibliotacas cliente de Oracle determinarán el juego de caracteres desde la variable de entorno
NLS_LANG.Pasar este parámetro puede reducir el tiempo de conexión.
-
session_mode -
Este parámetro está disponible desde la versión 5 de PHP (PECL OCI8 1.1) y acepta los siguientes valores:
OCI_DEFAULT,OCI_SYSOPERyOCI_SYSDBA. Si se especificaOCI_SYSOPERoOCI_SYSDBA, esta función intentará establecer una conexión privilegiada con las credenciales externas. Las conexiones privilegiadas están desactivadas por omisión. Para activarlas hay que establecer oci8.privileged_connect a On.En PHP 5.3 (PECL OCI8 1.3.4) se introdujo el valor de modo
OCI_CRED_EXT. Esto indica a Oracle que utilice la autenticación Externa o del SO, la cual debe estar configurada en la base de datos. La banderaOCI_CRED_EXTsólo puede usarse con el nombre de usuario "/" y la contraseña en blanco. oci8.privileged_connect podría ser On o Off.OCI_CRED_EXTpodría estar en combinación con los modosOCI_SYSOPERoOCI_SYSDBA.OCI_CRED_EXTno se admite en Windows por razones de seguridad.
Valores devueltos
Devuelve un identificador de conexión, o FALSE en caso de error.
Ejemplos
Ejemplo #1 Uso básico de oci_connect() utilizando la sintaxis de Easy Connect
<?php
// Conectar al servicio XE (es deicr, la base de datos) en la máquina "localhost"
$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";
?>
Ejemplo #2 Uso básico de oci_connect() utilizando un nombre de Network Connect
<?php
// Conectar a la base de datos MYDB descrita en el fichero tnsnames.ora,
// Un ejemplo de una entrada de tnsnames.ora para MYDB podría ser:
// 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";
?>
Ejemplo #3 oci_connect() con un conjunto de caracteres explícito
<?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";
?>
Ejemplo #4 Usar múltiples llamadas a oci_connect()
<?php
$c1 = oci_connect("hr", "welcome", 'localhost/XE');
$c2 = oci_connect("hr", "welcome", 'localhost/XE');
// $c1 y $c2 muestran el mismo id de recurso de PHP, ya que usan la
// misma conexión subyacente a la base de datos
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); // Insertar una fila usando c1
sleep(2); // Dormir para mostrar una marca de tiempo diferente en la 2ª fila
insert_data('c2', $c2); // Insertar una fila usando c2
select_data('c1', $c1); // Son devueltas ambas inserciones
select_data('c2', $c2); // Son devueltas ambas inserciones
rollback('c1', $c1); // Revertir usando c1
select_data('c1', $c1); // Ambas inserciones han sido revertidas
select_data('c2', $c2);
drop_table($c1);
// El cierre de una de las conexiones hace que la variable de PHP sea inutilizable, aunque
// se podría utilizar la otra
oci_close($c1);
echo "c1 is $c1<br>\n";
echo "c2 is $c2<br>\n";
// La salida es:
// 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
?>
Notas
Nota:
Una instalación o configuración incorrecta de la extensión OCI8 a menudo se manifestará como un problema o error de conexión. Véase la sección Instalación/Configuración para obtener información sobre la solución de problemas.
Nota:
En versiones de PHP anteriores a la 5.0.0 use ocilogon() en su lugar. El nombre antiguo de la función todavía puede ser utilizado en versiones actuales, sin embargo es obsoleto y no se recomienda.
Ver también
- oci_pconnect() - Conecta a una base de datos de Oracle usando una conexión persistente
- oci_new_connect() - Conecta al servidor de Oracle usando una conexión única
- oci_close() - Cierra una conexión a Oracle
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);
?>
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.
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()); }
?>
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.
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);
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).
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
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.
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.
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.
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
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.
