DB
[ class tree: DB ] [ index: DB ] [ all elements ]

Source for file ibase.php

Documentation is available at ibase.php

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * The PEAR DB driver for PHP's interbase extension
  7.  * for interacting with Interbase and Firebird databases
  8.  *
  9.  * While this class works with PHP 4, PHP's InterBase extension is
  10.  * unstable in PHP 4.  Use PHP 5.
  11.  *
  12.  * PHP versions 4 and 5
  13.  *
  14.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  15.  * that is available through the world-wide-web at the following URI:
  16.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  17.  * the PHP License and are unable to obtain it through the web, please
  18.  * send a note to license@php.net so we can mail you a copy immediately.
  19.  *
  20.  * @category   Database
  21.  * @package    DB
  22.  * @author     Sterling Hughes <sterling@php.net>
  23.  * @author     Daniel Convissor <danielc@php.net>
  24.  * @copyright  1997-2005 The PHP Group
  25.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  26.  * @version    CVS: $Id: ibase.php,v 1.113 2007/01/12 03:11:17 aharvey Exp $
  27.  * @link       http://pear.php.net/package/DB
  28.  */
  29.  
  30. /**
  31.  * Obtain the DB_common class so it can be extended from
  32.  */
  33. require_once 'DB/common.php';
  34.  
  35. /**
  36.  * The methods PEAR DB uses to interact with PHP's interbase extension
  37.  * for interacting with Interbase and Firebird databases
  38.  *
  39.  * These methods overload the ones declared in DB_common.
  40.  *
  41.  * While this class works with PHP 4, PHP's InterBase extension is
  42.  * unstable in PHP 4.  Use PHP 5.
  43.  *
  44.  * NOTICE:  limitQuery() only works for Firebird.
  45.  *
  46.  * @category   Database
  47.  * @package    DB
  48.  * @author     Sterling Hughes <sterling@php.net>
  49.  * @author     Daniel Convissor <danielc@php.net>
  50.  * @copyright  1997-2005 The PHP Group
  51.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  52.  * @version    Release: 1.7.10
  53.  * @link       http://pear.php.net/package/DB
  54.  * @since      Class became stable in Release 1.7.0
  55.  */
  56. class DB_ibase extends DB_common
  57. {
  58.     // {{{ properties
  59.  
  60.     
  61.     /**
  62.      * The DB driver type (mysql, oci8, odbc, etc.)
  63.      * @var string 
  64.      */
  65.     var $phptype = 'ibase';
  66.  
  67.     /**
  68.      * The database syntax variant to be used (db2, access, etc.), if any
  69.      * @var string 
  70.      */
  71.     var $dbsyntax = 'ibase';
  72.  
  73.     /**
  74.      * The capabilities of this DB implementation
  75.      *
  76.      * The 'new_link' element contains the PHP version that first provided
  77.      * new_link support for this DBMS.  Contains false if it's unsupported.
  78.      *
  79.      * Meaning of the 'limit' element:
  80.      *   + 'emulate' = emulate with fetch row by number
  81.      *   + 'alter'   = alter the query
  82.      *   + false     = skip rows
  83.      *
  84.      * NOTE: only firebird supports limit.
  85.      *
  86.      * @var array 
  87.      */
  88.     var $features = array(
  89.         'limit'         => false,
  90.         'new_link'      => false,
  91.         'numrows'       => 'emulate',
  92.         'pconnect'      => true,
  93.         'prepare'       => true,
  94.         'ssl'           => false,
  95.         'transactions'  => true,
  96.     );
  97.  
  98.     /**
  99.      * A mapping of native error codes to DB error codes
  100.      * @var array 
  101.      */
  102.     var $errorcode_map = array(
  103.         -104 => DB_ERROR_SYNTAX,
  104.         -150 => DB_ERROR_ACCESS_VIOLATION,
  105.         -151 => DB_ERROR_ACCESS_VIOLATION,
  106.         -155 => DB_ERROR_NOSUCHTABLE,
  107.         -157 => DB_ERROR_NOSUCHFIELD,
  108.         -158 => DB_ERROR_VALUE_COUNT_ON_ROW,
  109.         -170 => DB_ERROR_MISMATCH,
  110.         -171 => DB_ERROR_MISMATCH,
  111.         -172 => DB_ERROR_INVALID,
  112.         // -204 =>  // Covers too many errors, need to use regex on msg
  113.                 -205 => DB_ERROR_NOSUCHFIELD,
  114.         -206 => DB_ERROR_NOSUCHFIELD,
  115.         -208 => DB_ERROR_INVALID,
  116.         -219 => DB_ERROR_NOSUCHTABLE,
  117.         -297 => DB_ERROR_CONSTRAINT,
  118.         -303 => DB_ERROR_INVALID,
  119.         -413 => DB_ERROR_INVALID_NUMBER,
  120.         -530 => DB_ERROR_CONSTRAINT,
  121.         -551 => DB_ERROR_ACCESS_VIOLATION,
  122.         -552 => DB_ERROR_ACCESS_VIOLATION,
  123.         // -607 =>  // Covers too many errors, need to use regex on msg
  124.                 -625 => DB_ERROR_CONSTRAINT_NOT_NULL,
  125.         -803 => DB_ERROR_CONSTRAINT,
  126.         -804 => DB_ERROR_VALUE_COUNT_ON_ROW,
  127.         // -902 =>  // Covers too many errors, need to use regex on msg
  128.                 -904 => DB_ERROR_CONNECT_FAILED,
  129.         -922 => DB_ERROR_NOSUCHDB,
  130.         -923 => DB_ERROR_CONNECT_FAILED,
  131.         -924 => DB_ERROR_CONNECT_FAILED
  132.     );
  133.  
  134.     /**
  135.      * The raw database connection created by PHP
  136.      * @var resource 
  137.      */
  138.     var $connection;
  139.  
  140.     /**
  141.      * The DSN information for connecting to a database
  142.      * @var array 
  143.      */
  144.     var $dsn = array();
  145.  
  146.  
  147.     /**
  148.      * The number of rows affected by a data manipulation query
  149.      * @var integer 
  150.      * @access private
  151.      */
  152.     var $affected = 0;
  153.  
  154.     /**
  155.      * Should data manipulation queries be committed automatically?
  156.      * @var bool 
  157.      * @access private
  158.      */
  159.     var $autocommit = true;
  160.  
  161.     /**
  162.      * The prepared statement handle from the most recently executed statement
  163.      *
  164.      * {@internal  Mainly here because the InterBase/Firebird API is only
  165.      * able to retrieve data from result sets if the statemnt handle is
  166.      * still in scope.}}
  167.      *
  168.      * @var resource 
  169.      */
  170.     var $last_stmt;
  171.  
  172.     /**
  173.      * Is the given prepared statement a data manipulation query?
  174.      * @var array 
  175.      * @access private
  176.      */
  177.     var $manip_query = array();
  178.  
  179.  
  180.     // }}}
  181.     // {{{ constructor
  182.  
  183.     
  184.     /**
  185.      * This constructor calls <kbd>$this->DB_common()</kbd>
  186.      *
  187.      * @return void 
  188.      */
  189.     function DB_ibase()
  190.     {
  191.         $this->DB_common();
  192.     }
  193.  
  194.     // }}}
  195.     // {{{ connect()
  196.  
  197.     
  198.     /**
  199.      * Connect to the database server, log in and open the database
  200.      *
  201.      * Don't call this method directly.  Use DB::connect() instead.
  202.      *
  203.      * PEAR DB's ibase driver supports the following extra DSN options:
  204.      *   + buffers    The number of database buffers to allocate for the
  205.      *                 server-side cache.
  206.      *   + charset    The default character set for a database.
  207.      *   + dialect    The default SQL dialect for any statement
  208.      *                 executed within a connection.  Defaults to the
  209.      *                 highest one supported by client libraries.
  210.      *                 Functional only with InterBase 6 and up.
  211.      *   + role       Functional only with InterBase 5 and up.
  212.      *
  213.      * @param array $dsn         the data source name
  214.      * @param bool  $persistent  should the connection be persistent?
  215.      *
  216.      * @return int  DB_OK on success. A DB_Error object on failure.
  217.      */
  218.     function connect($dsn$persistent = false)
  219.     {
  220.         if (!PEAR::loadExtension('interbase')) {
  221.             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  222.         }
  223.  
  224.         $this->dsn = $dsn;
  225.         if ($dsn['dbsyntax']{
  226.             $this->dbsyntax = $dsn['dbsyntax'];
  227.         }
  228.         if ($this->dbsyntax == 'firebird'{
  229.             $this->features['limit''alter';
  230.         }
  231.  
  232.         $params = array(
  233.             $dsn['hostspec']
  234.                     ? ($dsn['hostspec'':' $dsn['database'])
  235.                     : $dsn['database'],
  236.             $dsn['username'$dsn['username': null,
  237.             $dsn['password'$dsn['password': null,
  238.             isset($dsn['charset']$dsn['charset': null,
  239.             isset($dsn['buffers']$dsn['buffers': null,
  240.             isset($dsn['dialect']$dsn['dialect': null,
  241.             isset($dsn['role'])    $dsn['role': null,
  242.         );
  243.  
  244.         $connect_function $persistent 'ibase_pconnect' 'ibase_connect';
  245.  
  246.         $this->connection = @call_user_func_array($connect_function$params);
  247.         if (!$this->connection{
  248.             return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
  249.         }
  250.         return DB_OK;
  251.     }
  252.  
  253.     // }}}
  254.     // {{{ disconnect()
  255.  
  256.     
  257.     /**
  258.      * Disconnects from the database server
  259.      *
  260.      * @return bool  TRUE on success, FALSE on failure
  261.      */
  262.     function disconnect()
  263.     {
  264.         $ret @ibase_close($this->connection);
  265.         $this->connection = null;
  266.         return $ret;
  267.     }
  268.  
  269.     // }}}
  270.     // {{{ simpleQuery()
  271.  
  272.     
  273.     /**
  274.      * Sends a query to the database server
  275.      *
  276.      * @param string  the SQL query string
  277.      *
  278.      * @return mixed  + a PHP result resrouce for successful SELECT queries
  279.      *                 + the DB_OK constant for other successful queries
  280.      *                 + a DB_Error object on failure
  281.      */
  282.     function simpleQuery($query)
  283.     {
  284.         $ismanip $this->_checkManip($query);
  285.         $this->last_query = $query;
  286.         $query $this->modifyQuery($query);
  287.         $result @ibase_query($this->connection$query);
  288.  
  289.         if (!$result{
  290.             return $this->ibaseRaiseError();
  291.         }
  292.         if ($this->autocommit && $ismanip{
  293.             @ibase_commit($this->connection);
  294.         }
  295.         if ($ismanip{
  296.             $this->affected $result;
  297.             return DB_OK;
  298.         else {
  299.             $this->affected = 0;
  300.             return $result;
  301.         }
  302.     }
  303.  
  304.     // }}}
  305.     // {{{ modifyLimitQuery()
  306.  
  307.     
  308.     /**
  309.      * Adds LIMIT clauses to a query string according to current DBMS standards
  310.      *
  311.      * Only works with Firebird.
  312.      *
  313.      * @param string $query   the query to modify
  314.      * @param int    $from    the row to start to fetching (0 = the first row)
  315.      * @param int    $count   the numbers of rows to fetch
  316.      * @param mixed  $params  array, string or numeric data to be used in
  317.      *                          execution of the statement.  Quantity of items
  318.      *                          passed must match quantity of placeholders in
  319.      *                          query:  meaning 1 placeholder for non-array
  320.      *                          parameters or 1 placeholder per array element.
  321.      *
  322.      * @return string  the query string with LIMIT clauses added
  323.      *
  324.      * @access protected
  325.      */
  326.     function modifyLimitQuery($query$from$count$params = array())
  327.     {
  328.         if ($this->dsn['dbsyntax'== 'firebird'{
  329.             $query preg_replace('/^([\s(])*SELECT/i',
  330.                                   "SELECT FIRST $count SKIP $from"$query);
  331.         }
  332.         return $query;
  333.     }
  334.  
  335.     // }}}
  336.     // {{{ nextResult()
  337.  
  338.     
  339.     /**
  340.      * Move the internal ibase result pointer to the next available result
  341.      *
  342.      * @param valid fbsql result resource
  343.      *
  344.      * @access public
  345.      *
  346.      * @return true if a result is available otherwise return false
  347.      */
  348.     function nextResult($result)
  349.     {
  350.         return false;
  351.     }
  352.  
  353.     // }}}
  354.     // {{{ fetchInto()
  355.  
  356.     
  357.     /**
  358.      * Places a row from the result set into the given array
  359.      *
  360.      * Formating of the array and the data therein are configurable.
  361.      * See DB_result::fetchInto() for more information.
  362.      *
  363.      * This method is not meant to be called directly.  Use
  364.      * DB_result::fetchInto() instead.  It can't be declared "protected"
  365.      * because DB_result is a separate object.
  366.      *
  367.      * @param resource $result    the query result resource
  368.      * @param array    $arr       the referenced array to put the data in
  369.      * @param int      $fetchmode how the resulting array should be indexed
  370.      * @param int      $rownum    the row number to fetch (0 = first row)
  371.      *
  372.      * @return mixed  DB_OK on success, NULL when the end of a result set is
  373.      *                  reached or on failure
  374.      *
  375.      * @see DB_result::fetchInto()
  376.      */
  377.     function fetchInto($result&$arr$fetchmode$rownum = null)
  378.     {
  379.         if ($rownum !== null{
  380.             return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
  381.         }
  382.         if ($fetchmode DB_FETCHMODE_ASSOC{
  383.             if (function_exists('ibase_fetch_assoc')) {
  384.                 $arr @ibase_fetch_assoc($result);
  385.             else {
  386.                 $arr get_object_vars(ibase_fetch_object($result));
  387.             }
  388.             if ($this->options['portability'DB_PORTABILITY_LOWERCASE && $arr{
  389.                 $arr array_change_key_case($arrCASE_LOWER);
  390.             }
  391.         else {
  392.             $arr @ibase_fetch_row($result);
  393.         }
  394.         if (!$arr{
  395.             return null;
  396.         }
  397.         if ($this->options['portability'DB_PORTABILITY_RTRIM{
  398.             $this->_rtrimArrayValues($arr);
  399.         }
  400.         if ($this->options['portability'DB_PORTABILITY_NULL_TO_EMPTY{
  401.             $this->_convertNullArrayValuesToEmpty($arr);
  402.         }
  403.         return DB_OK;
  404.     }
  405.  
  406.     // }}}
  407.     // {{{ freeResult()
  408.  
  409.     
  410.     /**
  411.      * Deletes the result set and frees the memory occupied by the result set
  412.      *
  413.      * This method is not meant to be called directly.  Use
  414.      * DB_result::free() instead.  It can't be declared "protected"
  415.      * because DB_result is a separate object.
  416.      *
  417.      * @param resource $result  PHP's query result resource
  418.      *
  419.      * @return bool  TRUE on success, FALSE if $result is invalid
  420.      *
  421.      * @see DB_result::free()
  422.      */
  423.     function freeResult($result)
  424.     {
  425.         return is_resource($result? ibase_free_result($result: false;
  426.     }
  427.  
  428.     // }}}
  429.     // {{{ freeQuery()
  430.  
  431.     
  432.     function freeQuery($query)
  433.     {
  434.         return is_resource($query? ibase_free_query($query: false;
  435.     }
  436.  
  437.     // }}}
  438.     // {{{ affectedRows()
  439.  
  440.     
  441.     /**
  442.      * Determines the number of rows affected by a data maniuplation query
  443.      *
  444.      * 0 is returned for queries that don't manipulate data.
  445.      *
  446.      * @return int  the number of rows.  A DB_Error object on failure.
  447.      */
  448.     function affectedRows()
  449.     {
  450.         if (is_integer($this->affected)) {
  451.             return $this->affected;
  452.         }
  453.         return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
  454.     }
  455.  
  456.     // }}}
  457.     // {{{ numCols()
  458.  
  459.     
  460.     /**
  461.      * Gets the number of columns in a result set
  462.      *
  463.      * This method is not meant to be called directly.  Use
  464.      * DB_result::numCols() instead.  It can't be declared "protected"
  465.      * because DB_result is a separate object.
  466.      *
  467.      * @param resource $result  PHP's query result resource
  468.      *
  469.      * @return int  the number of columns.  A DB_Error object on failure.
  470.      *
  471.      * @see DB_result::numCols()
  472.      */
  473.     function numCols($result)
  474.     {
  475.         $cols @ibase_num_fields($result);
  476.         if (!$cols{
  477.             return $this->ibaseRaiseError();
  478.         }
  479.         return $cols;
  480.     }
  481.  
  482.     // }}}
  483.     // {{{ prepare()
  484.  
  485.     
  486.     /**
  487.      * Prepares a query for multiple execution with execute().
  488.      *
  489.      * prepare() requires a generic query as string like <code>
  490.      *    INSERT INTO numbers VALUES (?, ?, ?)
  491.      * </code>.  The <kbd>?</kbd> characters are placeholders.
  492.      *
  493.      * Three types of placeholders can be used:
  494.      *   + <kbd>?</kbd>  a quoted scalar value, i.e. strings, integers
  495.      *   + <kbd>!</kbd>  value is inserted 'as is'
  496.      *   + <kbd>&</kbd>  requires a file name.  The file's contents get
  497.      *                     inserted into the query (i.e. saving binary
  498.      *                     data in a db)
  499.      *
  500.      * Use backslashes to escape placeholder characters if you don't want
  501.      * them to be interpreted as placeholders.  Example: <code>
  502.      *    "UPDATE foo SET col=? WHERE col='over \& under'"
  503.      * </code>
  504.      *
  505.      * @param string $query query to be prepared
  506.      * @return mixed DB statement resource on success. DB_Error on failure.
  507.      */
  508.     function prepare($query)
  509.     {
  510.         $tokens   preg_split('/((?<!\\\)[&?!])/'$query-1,
  511.                                PREG_SPLIT_DELIM_CAPTURE);
  512.         $token    = 0;
  513.         $types    = array();
  514.         $newquery '';
  515.  
  516.         foreach ($tokens as $key => $val{
  517.             switch ($val{
  518.                 case '?':
  519.                     $types[$token++DB_PARAM_SCALAR;
  520.                     break;
  521.                 case '&':
  522.                     $types[$token++DB_PARAM_OPAQUE;
  523.                     break;
  524.                 case '!':
  525.                     $types[$token++DB_PARAM_MISC;
  526.                     break;
  527.                 default:
  528.                     $tokens[$keypreg_replace('/\\\([&?!])/'"\\1"$val);
  529.                     $newquery .= $tokens[$key'?';
  530.             }
  531.         }
  532.  
  533.         $newquery substr($newquery0-1);
  534.         $this->last_query = $query;
  535.         $newquery $this->modifyQuery($newquery);
  536.         $stmt @ibase_prepare($this->connection$newquery);
  537.  
  538.         if ($stmt === false{
  539.             $stmt $this->ibaseRaiseError();
  540.         else {
  541.             $this->prepare_types[(int)$stmt$types;
  542.             $this->manip_query[(int)$stmt]   DB::isManip($query);
  543.         }
  544.  
  545.         return $stmt;
  546.     }
  547.  
  548.     // }}}
  549.     // {{{ execute()
  550.  
  551.     
  552.     /**
  553.      * Executes a DB statement prepared with prepare().
  554.      *
  555.      * @param resource  $stmt  a DB statement resource returned from prepare()
  556.      * @param mixed  $data  array, string or numeric data to be used in
  557.      *                       execution of the statement.  Quantity of items
  558.      *                       passed must match quantity of placeholders in
  559.      *                       query:  meaning 1 for non-array items or the
  560.      *                       quantity of elements in the array.
  561.      * @return object  new DB_Result or a DB_Error when fail
  562.      * @see DB_ibase::prepare()
  563.      * @access public
  564.      */
  565.     function &execute($stmt$data = array())
  566.     {
  567.         $data = (array)$data;
  568.         $this->last_parameters = $data;
  569.  
  570.         $types =$this->prepare_types[(int)$stmt];
  571.         if (count($types!= count($data)) {
  572.             $tmp =$this->raiseError(DB_ERROR_MISMATCH);
  573.             return $tmp;
  574.         }
  575.  
  576.         $i = 0;
  577.         foreach ($data as $key => $value{
  578.             if ($types[$i== DB_PARAM_MISC{
  579.                 /*
  580.                  * ibase doesn't seem to have the ability to pass a
  581.                  * parameter along unchanged, so strip off quotes from start
  582.                  * and end, plus turn two single quotes to one single quote,
  583.                  * in order to avoid the quotes getting escaped by
  584.                  * ibase and ending up in the database.
  585.                  */
  586.                 $data[$keypreg_replace("/^'(.*)'$/""\\1"$data[$key]);
  587.                 $data[$keystr_replace("''""'"$data[$key]);
  588.             elseif ($types[$i== DB_PARAM_OPAQUE{
  589.                 $fp @fopen($data[$key]'rb');
  590.                 if (!$fp{
  591.                     $tmp =$this->raiseError(DB_ERROR_ACCESS_VIOLATION);
  592.                     return $tmp;
  593.                 }
  594.                 $data[$keyfread($fpfilesize($data[$key]));
  595.                 fclose($fp);
  596.             }
  597.             $i++;
  598.         }
  599.  
  600.         array_unshift($data$stmt);
  601.  
  602.         $res call_user_func_array('ibase_execute'$data);
  603.         if (!$res{
  604.             $tmp =$this->ibaseRaiseError();
  605.             return $tmp;
  606.         }
  607.         /* XXX need this?
  608.         if ($this->autocommit && $this->manip_query[(int)$stmt]) {
  609.             @ibase_commit($this->connection);
  610.         }*/
  611.         $this->last_stmt = $stmt;
  612.         if ($this->manip_query[(int)$stmt|| $this->_next_query_manip{
  613.             $this->_last_query_manip = true;
  614.             $this->_next_query_manip = false;
  615.             $tmp DB_OK;
  616.         else {
  617.             $this->_last_query_manip = false;
  618.             $tmp =new DB_result($this$res);
  619.         }
  620.         return $tmp;
  621.     }
  622.  
  623.     /**
  624.      * Frees the internal resources associated with a prepared query
  625.      *
  626.      * @param resource $stmt           the prepared statement's PHP resource
  627.      * @param bool     $free_resource  should the PHP resource be freed too?
  628.      *                                   Use false if you need to get data
  629.      *                                   from the result set later.
  630.      *
  631.      * @return bool  TRUE on success, FALSE if $result is invalid
  632.      *
  633.      * @see DB_ibase::prepare()
  634.      */
  635.     function freePrepared($stmt$free_resource = true)
  636.     {
  637.         if (!is_resource($stmt)) {
  638.             return false;
  639.         }
  640.         if ($free_resource{
  641.             @ibase_free_query($stmt);
  642.         }
  643.         unset($this->prepare_tokens[(int)$stmt]);
  644.         unset($this->prepare_types[(int)$stmt]);
  645.         unset($this->manip_query[(int)$stmt]);
  646.         return true;
  647.     }
  648.  
  649.     // }}}
  650.     // {{{ autoCommit()
  651.  
  652.     
  653.     /**
  654.      * Enables or disables automatic commits
  655.      *
  656.      * @param bool $onoff  true turns it on, false turns it off
  657.      *
  658.      * @return int  DB_OK on success.  A DB_Error object if the driver
  659.      *                doesn't support auto-committing transactions.
  660.      */
  661.