| 
               
            If Oracle support is enabled in PHP, but the Oracle client libraries 
              can not be found, you will get an error when trying to start Apache. 
              For example, on Windows if php.ini has extension=php_oci8.dll, but 
              the Oracle home cannot be found, the alert "The dynamic link library 
              OCI.dll could not be found in the specified path" is displayed. 
             
            Make sure the Oracle directory is readable by the OS user starting 
              the Web server.  
            Make sure the Oracle environment variables are set correctly before 
              starting Web server (see the next topic).  
            Linux users might see an Apache error about being unable to load 
              libclntsh.so, but are more likely to notice the problem earlier 
              when compiling PHP. The compiler will fail with the error "Cannot 
              find file ocidfn.h" or "Cannot find file oci.h." If you have 
              Oracle installed but are missing the Oracle header files, do a "Client" 
              install of Oracle. In Oracle 9i, download the Oracle9i Database 
              Release 2, run the installer, and choose the Client option. In 
              Oracle 10g, download the Oracle Database 10g Client 
              Release, run the installer, and choose the Administrator option. 
             
            Also see the OTN articles Installing 
              Oracle, PHP, and Apache on Windows 2000/XP and Installing 
              Oracle, PHP, and Apache on Linux.  
            
            
            Setting all Oracle environment variables before the Web server 
              starts is the only safe way to get PHP talking to Oracle. Setting 
              the variables in PHP scripts or the httpd.conf file mostly does 
              not work. There are numerous mail list and forum posts because of 
              general confusion about the environment. The behavior on Windows 
              and Linux also differs.  
            The environment needs to be set so PHP can find Oracle's libraries 
              and message files. Environment variables may also need to be set 
              to identify the default database or to locate configuration files 
              for remote database access.  
            An example shell script for starting Apache is start_apache 
              :  
                    #!/bin/sh
        ORACLE_HOME=/u01/app/oracle/product/10.1.2
        ORACLE_SID=orcl
        export ORACLE_HOME ORACLE_SID
        export LD_LIBRARY_PATH=${ORACLE_HOME}/lib:${LD_LIBRARY_PATH}
        echo "Oracle Home: $ORACLE_HOME"
        echo "Oracle SID:  $ORACLE_SID"
        echo Starting Apache
        #export > /tmp/envvars
        ./apachectl start Depending on where your database 
              server is and how you connect to it, you may also want set TWO_TASK 
              or TNS_ADMIN (see the next topic).  
            Apache, PHP, and Oracle error messages that use phrases like "could 
              not load" or "could not resolve" all point to environment setup 
              problems. If you are having problems with the environment, try uncommenting 
              the "export" line in start_apache. After the script is run, the 
              file /tmp/envvars will contain the environment variables Apache 
              knows about. This is useful to solve problems starting Apache at 
              machine boot time—the environment may be very different from starting 
              Apache in your user shell.  
            If Apache starts but gives errors about Oracle libraries, and/or 
              OCI8 function calls fail, try looking at PHP's environment. Create 
              the following script "phpinfo.php" where your Web server can read 
              it and load it in a browser:  
                    <?php
          phpinfo();
        ?> Triple-check the environment 
              and path configuration. On UNIX, check that LD_LIBRARY_PATH (or equivalent) 
              contains $ORACLE_HOME/lib. On Windows, the PATH variable may need 
              to contain %ORACLE_HOME%\bin.  
            Some messages such as the run-time "Warning: ocilogon(): _oci_open_session: 
              OCIHandleAlloc? OCI_HTYPE_SVCCTX: OCI_INVALID_HANDLE" are also symptoms 
              of an incorrect ORACLE_HOME.  
            If you have compiled PHP with an ORACLE_HOME that is different from  the one 
              being used at run time, be sure to follow the steps carefully as described in Using PHP with OHS. If OCILogon() 
              gives "ORA-12705 The NLS parameter value specified in an alter session 
              statement is not valid or not implemented", try setting ORA_NLS33 
              to the NLS data file directory in the ORACLE_HOME used to compile 
              PHP. In Using PHP with OHS, this is called $RDBMS_ORACLE_HOME so 
              set ORA_NLS33 to $RDBMS_ORACLE_HOME/ocommon/nls/admin/data.  
            The author shares his experience:  
            I did some tests to see what effect the basic environment in a 
              standard installation had on a PHP login call:  
                  $mycon = OCILogon("myusername", "mypassword", "MYDB"); I used Red Hat Linux AS 2.1, 
              Apache 1.3, and PHP 4.3.3.  
            
              - With no ORACLE_HOME set prior to "apachectl start" and no "putenv('ORACLE_HOME=/usr/oracle/MYDB')" 
                in the PHP script I got: 
            
  
             
                    Warning: ocilogon(): _oci_open_server: Error while trying to retrieve text for error ORA-12154 
              This shows the connection failed, and the message files (which 
                are located under the Oracle home directory) could not be found. 
               
             
            
              -  
                
With "putenv('ORACLE_HOME=/usr/oracle/MYDB')" in the PHP script 
                  but no ORACLE_HOME set, I got:  
                 
                  Warning: ocilogon(): _oci_open_server: ORA-12154: TNS:could not resolve service name 
                 
                The connection still failed, but now the correct message text 
                  could be read from the message file.  
               -  
                
With ORACLE_HOME set prior to "apachectl start" but without 
                  "putenv()" the connection succeeded. This is the recommended 
                  configuration.  
               - With ORACLE_HOME set correctly prior to "apachectl start" and 
                with "putenv()" using an invalid ORACLE_HOME directory, the connection 
                succeeded. I also tried this on Windows. This time the connection 
                failed with the same message as my first test above. 
 
             
             
              I had a similar set of results when I replaced the PHP putenv() 
                call with an Apache httpd.conf directive "setenv ORACLE_HOME /usr/oracle/MYDB". 
               
              However some variables can be set in your PHP scripts. After 
                setting ORACLE_HOME correctly before starting Apache the following 
                code changed my default connection and connected to MYDB:  
                    putenv("TWO_TASK=MYDB");
      $mycon = OCILogon("myusername", "mypassword");
              The environment variables TNS_ADMIN, NLS_DATE_FORMAT (and there 
                may be others) can be set this way too.  
             
            Back to Top 
            
            
              -  
                
A user-chosen net service name is often used to identify which 
                  database to connect to. By default, it is read from the environment 
                  variable ORACLE_SID or it can be explicitly given in the connection 
                  call. The net service name MYDB could be used in Oracle's command-line 
                  SQL*Plus utility, for example:  
                       sqlplus myusername/mypassword@MYDB 
                or used in PHP like:  
                       $mycon = OCILogon("myusername", "mypassword", "MYDB");
                The net service name is commonly mapped to an actual database 
                  by an entry in a tnsnames.ora file :  
                       MYDB =
          (DESCRIPTION =
            (ADDRESS = (PROTOCOL = TCP)(HOST = mymachine.mydomain)(PORT = 1521))
            (CONNECT_DATA =
              (SERVER = DEDICATED)
              (SERVICE_NAME = MYDB)
            )
          )
                The default file location is $ORACLE_HOME/network/admin/tnsnames.ora. 
                  On some operating systems other locations will be checked if 
                  this does not exist. If your tnsnames.ora is in a non-standard 
                  location, you can set the environment variable TNS_ADMIN to the 
                  directory containing it. For example, if you are using /home/myuser/tnsnames.ora, 
                  add these lines to start_apache (see the previous topic): 
                 
                        TNS_ADMIN=/home/myuser
        export TNS_ADMIN
                If the net service name used in OCILogon() cannot be found 
                  in the tnsnames.ora, or the tnsnames.ora is not found at all 
                  by PHP, you may get an error when logging in:  
                        Warning: ocilogon(): _oci_open_server: ORA-12154: TNS:could not resolve service name 
                The error ORA-12154 can also occur if you have a file $ORACLE_HOME/network/admin/sqlnet.ora 
                  and it specifies a NAMES.DEFAULT_DOMAIN value. Unqualified net 
                  service names in OCILogon() calls will have sqlnet.ora's value 
                  for NAMES.DEFAULT_DOMAIN appended to the alias. For example 
                  if sqlnet.ora had:  
                        NAMES.DEFAULT_DOMAIN = au.oracle.com 
                then 'OCILogon("myusername", "mypassword", "mydb")' causes 
                  Oracle to look for the alias "MYDB.AU.ORACLE.COM = . . ." in 
                  tnsnames.ora. A quick solution is to change the tnsnames.ora 
                  entry to:  
                        MYDB.AU.ORACLE.COM =
          (DESCRIPTION =
            (ADDRESS = (PROTOCOL = TCP)(HOST = mymachine.mydomain)(PORT = 1521))
            (CONNECT_DATA =
              (SERVER = DEDICATED)
              (SERVICE_NAME = MYDB)
            )
          )
               - If Oracle 10g libraries are used by PHP, try the new 
                Easy Connect syntax. No tnsnames.ora file is required. To connect 
                to the MYDB database service running on mymachine: 
                
 
                     $c = OCILogon('myusername', 'mypassword', '//mymachine.mydomain/MYDB');
                 
                This will only work if the PHP-side libraries are from 10g. 
                  If the database server is 10g but PHP is using Oracle 
                  9i libraries, it will not work. See Oracle's Using 
                  the Easy Connect Naming Method documentation for the Easy 
                  Connect syntax.  
               -  
                
If Easy Connect has been disabled or you are using Oracle 
                  8i or 9i libraries, another alternative is to use the full connection 
                  string:  
                         $db = "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)
                      (HOST = mymachine.mydomain)(PORT=1521))
                      (CONNECT_DATA=(SERVER=DEDICATED)
                      (SERVICE_NAME=MYDB)))";
         $mycon = OCILogon("myusername", "mypassword", $db);
                 Back 
                  to Top  
              
             
             
            Optimizing database connections in a frequently used script can 
              improve performance and prevent resource problems. Some tips are: 
             
            
              - Avoid multiple connections to Oracle in a single script. 
 
              - If you have to make multiple connections, use different tnsnames.ora 
                aliases, even when you want to connect to the same Oracle instance 
                with a different user. This will avoid PHP context conflicts since 
                each connection will use a distinct TCP link to the database. 
              
 
              - Using the persistent OCIPLogon() call minimizes the number 
                of physical connection requests to the database. It has some limitations. 
                If the DBA terminates the session with ALTER SYSTEM KILL SESSION 
                or (worse) terminates the Oracle shadow process with an operating 
                system kill command, OCIPLogon() may succeed, but subsequent OCI8 
                calls may still fail. This can be timing dependent. When reusing 
                a persistent connection, always check its validity at the start 
                of the script with an explicit query. If you receive an Oracle 
                error then the connection is no longer valid and you may need 
                to logoff and login again. Some developers give the user a message 
                asking them to retry later and call PHP's apache_child_terminate() 
                to clean up the session. 
 
              - Use Oracle Database Resource Manager (which replaces User Profiles) 
                to limit the resources used by any one connection. 
 
              - Use Oracle Net configuration parameters such as SQLNET.EXPIRE_TIME 
                to free up unused database resources. 
 
              -  Refresh the Apache processes after they have managed a certain 
                number of requests, depending on site load. An Apache graceful 
                restart nightly will clean up connections. 
 
             
            Back to Top 
               
            Getting no, or incorrect, results is surprisingly often due to 
              executing the wrong statement. During development, echo each complete 
              SQL statement from PHP to check it is well formed and all variables 
              are correctly expanded. Quoting errors or misunderstandings with 
              PHP's variable syntax in strings can cause incorrect statements 
              to be executed.  
            Testing SQL statements in SQL*Plus before executing them in PHP 
              also helps confirm correctness.  
            When statements are entered in tools like SQL*Plus, a semi-colon 
              is often used to tell the tool that the statement is complete and 
              can now be executed. However the semi-colon is not considered part 
              of the statement and is not sent to the database. In PHP, do not 
              add a semi-colon to a SQL statement—otherwise an Oracle error will 
              occur. This example is a valid query:  
                    $sql = "SELECT * FROM EMP_DETAILS_VIEW";  Oracle's inbuilt scripting language 
              PL/SQL has  syntax that is different from  SQL and requires a final semi-colon: 
             
                    $plsql = "BEGIN DBMS_OUTPUT.PUT_LINE('hi'); END;"; Back 
              to Top 
              
            Test all return values from OCI8 functions to prevent hidden problems 
              and misleading results.  
            During development, set php.ini's error_reporting 
              directive to E_ALL. Also set display_errors 
              on, or configure the error log file (and remember to check it!). 
              This will show Oracle errors as they occur, unless you have explicitly 
              prefixed Oracle functions calls with http://www.php.net/manual/en/language.operators.errorcontrol.php. 
             
            Back to Top 
             
              
            Inserting strings containing single quotes can be handled in several 
              ways:  
            
              - Use bind variables. This also protects against SQL Injection 
                security issues: 
                
        $name = "O'Reilly";
        $stmt = 'INSERT INTO CUSTOMERS (NAME) VALUES (:nm)';
        $stid = OCIParse($mycon, $stmt);
        OCIBindByName($stid, ':nm', $name, -1);
        OCIExecute($stid);
                This is the recommended method.  
               
              - Double every single quote: 
                
        $name = "O'Reilly";
        $name = str_replace("'", "''", $name);
        $stmt = "INSERT INTO CUSTOMERS (NAME) VALUES ('".$name."')";
               
              - Turn on magic_quotes_sybase 
                in php.ini and use addslashes(): 
                
        $name = addslashes("O'Reilly");
        $stmt = "INSERT INTO CUSTOMERS (NAME) VALUES ('".$name."')";
                This is not recommended for portability reasons.  
               
             
            Back to Top 
              
            Oracle column names are cases insensitive by default, but when 
              fetching into associative arrays, specify the names in upper case: 
             
                    $query = 'select dname from dept';
        $stid = OCIParse($conn, $query);
        OCIExecute($stid);
        while (OCIFetchInto($stid, $row, OCI_ASSOC)) {
          echo $row['DNAME']."<br>\n";
        } It is possible to create a table with case-sensitive column names. Use var_dump($row) 
              to see the column names and data structure actually returned by 
              OCIFetchInto(). PHP has an array_change_key_case() 
              function to change the case of array keys if needed.  
            Table name prefixes are not included in associative array keys. 
              If a column name is used more than once in a query, use column aliases 
              to distinguish the columns. Otherwis, there will only be one associative 
              array entry. If the aliases T1N and T2N were not used in the example 
              below, only results from one of the NAME columns would be returned: 
             
                    $query = 'select myt1.name as t1n, myt2.name as t2n from myt1, myt2';
        $stid = OCIParse($conn, $query);
        OCIExecute($stid);
        while (OCIFetchInto($stid, $row, OCI_ASSOC)) {
          echo $row['T1N']." ".$row['T2N']."<br>\n"; Back 
              to Top 
              
            There is no array fetch capability in PHP, but setting the prefetch 
              row count with OCISetPrefetch() 
              is similar and can dramatically improve performance. The advantage 
              of setting the prefetch row count instead of the array size is that 
              Oracle does the caching for you. Your data structures only have 
              to handle one row at a time, and your code can often be simpler. 
             
            Here's an example that sets the prefetch row count to 100:  
            
        <?php
        $conn = OCILogon('myusername', 'mypassword', 'mydb');
        $query = 'SELECT * FROM EMP_DETAILS_VIEW';
        $stid = OCIParse($conn, $query);
        OCIExecute($stid);
        OCISetPrefetch($stid, 100);
        while ($succ = OCIFetchInto($stid, $row)) {
          foreach ($row as $item) {
            echo $item." ";
          }
          echo "<br>\n";
        }
        OCILogoff($conn);
        ?> Back 
              to Top 
              
            Bind variables are generally recommended because they help database 
              performance.  
            
              - Problems with binding are often caused because the data value 
                needs to be accessible at the time when OCIExecute() is called, 
                not just when OCIBindByName() is executed. 
                
If OCIBindByName() is called inside a wrapper function or 
                  method, and the PHP variable passed to OCIBindByName() is local 
                  to the wrapper function, there may be a problem. The variable 
                  needs to be in scope when OCIExecute() is later called. Otherwise 
                  an Oracle error such as "OCIStmtExecute: ORA-01460: unimplemented 
                  or unreasonable conversion requested" may occur or, confusingly, 
                  it can appear as if no value was set for an OUT variable. 
                 
                The sample below is a variation on this. The variable $val 
                  is local to the "foreach" command. The code returns no records: 
                 
                        <?php
        $qs = 'SELECT * FROM DEPARTMENTS WHERE DEPARTMENT_NAME = :dname AND LOCATION_ID = :loc';
        $dn = 'IT Support';
        $lc = '1700';
        $ba = array(':dname' => $dn, ':loc' => $lc);
        $conn = OCILogon('myusername', 'mypassword', 'mydb');
        $stmt = OCIParse($conn, $qs);
        foreach ($ba as $key => $val)
        {
          OCIBindByName($stmt, $key, $val, -1);
        }
        OCIExecute($stmt);
        while ($succ = OCIFetchInto($stmt, $o)) {
          foreach ($o as $mv) {
            echo $mv." ";
          }
          echo "<br>\n";
        }
        ?>
                Changing the OCIBindByName() call fixes the problem:  
                       . . .
       foreach ($ba as $key => $val)
       {
         OCIBindByName($stmt, $key, $ba[$key], -1);
       }
       . . .
               
              -  PHP numbers are converted to and from strings when they are 
                bound. This means the length parameter must generally be given 
                to OCIBindByName() when returning a numeric data value in a bind 
                variable. The length is the number of digits that will be returned. 
              
 
             
            Back to Top 
              
            Some sample code to upload a BLOB is given in the OCINewDescriptor() 
              manual entry.  
            Watch out for size limitations either in the HTML script:  
                    <input type="hidden" name="MAX_FILE_SIZE" value="3000">  or in the LimitRequestBody 
              directive in httpd.conf.  
            An example that uploads into a CLOB is:  
                    <?php
        //
        // Sample form to upload and insert data into an ORACLE CLOB column
        // using PHP's Oracle 8 API.
        //
        // Based on http://www.php.net/manual/en/function.oci-new-descriptor.php
        // modified to work on CLOBs and use register_globals = Off.
        //
        // Before running this script, execute these statements in SQL*Plus:
        //   drop table myclobtab;
        //   create table myclobtab (c1 number, c2 clob);
        //
        // Tested with PHP 4.3.3 against Oracle 9.2
        //
        if (!isset($_FILES['lob_upload'])) {
        ?>
        <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST" enctype="multipart/form-data">
        Upload file: <input type="file" name="lob_upload">
        <input type="submit" value="Upload">
        </form>
        <?php
        }
        else {
          $myid = 1; // should really be a unique id e.g. a sequence number
          $conn = OCILogon('myusername', 'mypassword', 'mydb');
          // Delete any existing CLOB so the query at the bottom
          // displays the new data
          $query = 'DELETE FROM MYCLOBTAB';
          $stmt = OCIParse ($conn, $query);
          OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS);
          OCIFreeStatement($stmt);
          // Insert the CLOB from PHP's temporary upload area
          $lob = OCINewDescriptor($conn, OCI_D_LOB);
          $stmt = OCIParse($conn, 'INSERT INTO MYCLOBTAB (C1, C2) VALUES('.
                           $myid . ', EMPTY_CLOB()) RETURNING C2 INTO :C2');
          OCIBindByName($stmt, ':C2', $lob, -1, OCI_B_CLOB);
          OCIExecute($stmt, OCI_DEFAULT);
          // The function $lob->savefile(...) reads from the uploaded file.
          // If the data was already in a PHP variable $myv, the
          // $lob->save($myv) function could be used instead.
          if ($lob->savefile($_FILES['lob_upload']['tmp_name'])) {
            OCICommit($conn);
            echo "CLOB successfully uploaded\n";
          }
          else {
            echo "Could not upload CLOB\n";
          }
          $lob->free();
          OCIFreeStatement($stmt);
          // Now query the uploaded CLOB and display it
          $query = 'SELECT C2 FROM MYCLOBTAB WHERE C1 = '.$myid;
          $stmt = OCIParse ($conn, $query);
          OCIExecute($stmt, OCI_DEFAULT);
          OCIFetchInto($stmt, $arr, OCI_ASSOC);
          $result = $arr['C2']->load();
          echo '<pre>';
          echo $result;
          echo '</pre>';
          OCIFreeStatement($stmt);
          OCILogoff($conn);
        }
        ?> 
            Back to Top 
             
             
            
              - Despite ongoing debate in the PHP community about how and if 
                function names should be standardized, the OCI8 extension function 
                names have been changed in PHP 5. Both old and new names can be 
                used. Most changes were the addition of underscores; for example 
                OCIBindByName() is now oci_bind_by_name(). A couple of function 
                names are very different: OCILogon() is now an alias for oci_connect(), 
                and the preferred name for OCILogoff() is now oci_close(). All 
                names are still case insensitive but are documented in lowercase 
                by convention. 
                
The OCI8 
                  Documentation was updated in March 2004 and shows only the 
                  new names. If documentation on a particular function does not 
                  seem to exist, look at the PHP_FALIAS macros in PHP's oci8.c 
                  to see if the name you are using is an alias for another function. 
                 
                Note: This FAQ uses the old function names so examples work with 
                  PHP 4 and PHP 5.  
               
              -  Another name that has changed in PHP 5 on Windows is the name 
                of the directory containing the extension DLLs. If starting the 
                Web server gives you the error "Unable to load dynamic library 
                'c:\PHP\extension\php_oci8.dll'" edit your php.ini file and change 
                the value of the extension_dir directive from "extension" to 
                "ext". 
 
             
            Back to Top 
              
            The PEAR 
              DB interface is a database abstraction layer that uses the same 
              syntax for different database brands. The standard PEAR DB error 
              function $db->getMessage() returns a simple PEAR error description. 
              For example, if connection fails for any reason, the message is always: 
             
                    DB Error: connect failed  You can get the exact Oracle error 
              number and message by using:  
                    $db->getDebugInfo()  It includes the Oracle error and 
              the complete statement, for example:  
                    [nativecode=ORA-01017: invalid username/password; logon denied ] ** oci8://myusername:wrongpassword@mydb  The Oracle message text can be extracted 
              from this string with a wrapper function, for example:  
                    require_once('DB.php');
        PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'PrintOCIError');
        // Display PEAR DB error
        function PrintOCIError($e)
        {
          if (is_object($e)) {
            $s = preg_match('/.*\[nativecode=(.*)/', $e->getDebugInfo(), $r);
            $etxt = $s ? $r[1] : $e->getDebugInfo();
          }
          else {
            $etxt = "Unknown Error";
          }
          echo "<p><b>Error</b>:</p>\n<pre>" .
            htmlspecialchars($etxt)."</pre>\n";
        }
        $db = DB::connect("oci8://myusername:wrongpassword@mydb");
        if (!DB::isError($db)) {
          .
          .
          .
        }  
            The output from PrintPEARDBError() is:  
                    Error: 
               
                    ORA-01017: invalid username/password; logon denied
  Back 
              to Top 
              
            The PHP libraries were made OCI Thread-safe in a bug fixed in 
              PHP 4.3.5. In practice, it appears that only loaded servers had 
              encountered the problem, which exhibited random behavior including crashing. 
              See PHP 
              bug 26558 and PHP 
              Bug 26393  
            Back to Top 
              
            Allowing externally authenticated database connections over the 
              Web would be a potential security risk for most configurations. 
              Luckily PHP's OCI8 extension will not allow external authentication 
              where the username is "/" and the password an empty string. The 
              call in PHP's oci8.c to Oracle's OCISessionBegin() always sets the 
              credential flag to OCI_CRED_RDBMS. To support operating system authentication, 
              the PHP source code would have to be changed to pass Oracle the 
              OCI_CRED_EXT flag when appropriate.  
            Back to Top 
              
            It is not possible to connect AS SYSDBA or AS SYSOPER in PHP. 
             
            The call in PHP's oci8.c to Oracle's OCISessionBegin() function 
              always sets the mode flag to OCI_DEFAULT. To allow privileged connections, 
              this would need to be changed to OCI_SYSDBA or OCI_SYSOPER. But 
              this simple solution opens a potential security hole, see Re: 
              suggestion about php ocilogon() oracle OCI 
              FUNCTION 
            Back to Top 
              
            There is no NCHAR or NCLOB support in PHP.  
            The current PHP implementations of oci8.c always use SQLCS_IMPLICIT 
              for the character set form when it calls Oracle's OCI. The character 
              set form would need to be SQLCS_NCHAR to support NCHAR and NCLOB, 
              and other data-handling changes may be needed in PHP's code.  
            Back to Top   |