Rev 776 | Blame | Compare with Previous | Last modification | View Log | RSS feed
<?php/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */// +----------------------------------------------------------------------+// | PHP Version 4 |// +----------------------------------------------------------------------+// | Copyright (c) 1997-2004 The PHP Group |// +----------------------------------------------------------------------+// | This source file is subject to version 2.02 of the PHP license, |// | that is bundled with this package in the file LICENSE, and is |// | available at through the world-wide-web at |// | http://www.php.net/license/2_02.txt. |// | If you did not receive a copy of the PHP license and are unable to |// | obtain it through the world-wide-web, please send a note to |// | license@php.net so we can mail you a copy immediately. |// +----------------------------------------------------------------------+// | Author: Stig Bakken <ssb@php.net> |// | Tomas V.V.Cox <cox@idecnet.com> |// | Maintainer: Daniel Convissor <danielc@php.net> |// +----------------------------------------------------------------------+//// $Id$/*** DB_common is a base class for DB implementations, and must be* inherited by all such** @package DB* @version $Id$* @category Database* @author Stig Bakken <ssb@php.net>* @author Tomas V.V.Cox <cox@idecnet.com>*/class DB_common extends PEAR{// {{{ properties/*** assoc of capabilities for this DB implementation* $features['limit'] => 'emulate' => emulate with fetch row by number* 'alter' => alter the query* false => skip rows* @var array*/var $features = array();/*** assoc mapping native error codes to DB ones* @var array*/var $errorcode_map = array();/*** DB type (mysql, oci8, odbc etc.)* @var string*/var $phptype;/*** @var string*/var $prepare_tokens;/*** @var string*/var $prepare_types;/*** @var string*/var $prepared_queries;/*** @var integer*/var $prepare_maxstmt = 0;/*** @var string*/var $last_query = '';/*** @var integer*/var $fetchmode = DB_FETCHMODE_ORDERED;/*** @var string*/var $fetchmode_object_class = 'stdClass';/*** Run-time configuration options.** The 'optimize' option has been deprecated. Use the 'portability'* option instead.** @see DB_common::setOption()* @var array*/var $options = array('persistent' => false,'ssl' => false,'debug' => 0,'seqname_format' => '%s_seq','autofree' => false,'portability' => DB_PORTABILITY_NONE,'optimize' => 'performance', // Deprecated. Use 'portability'.);/*** DB handle* @var resource*/var $dbh;// }}}// {{{ toString()/*** String conversation** @return string* @access private*/function toString(){$info = strtolower(get_class($this));$info .= ': (phptype=' . $this->phptype .', dbsyntax=' . $this->dbsyntax .')';if ($this->connection) {$info .= ' [connected]';}return $info;}// }}}// {{{ constructor/*** Constructor*/function DB_common(){$this->PEAR('DB_Error');}// }}}// {{{ quoteString()/*** DEPRECATED: Quotes a string so it can be safely used within string* delimiters in a query** @return string quoted string** @see DB_common::quoteSmart(), DB_common::escapeSimple()* @deprecated Deprecated in release 1.2 or lower* @internal*/function quoteString($string){$string = $this->quote($string);if ($string{0} == "'") {return substr($string, 1, -1);}return $string;}// }}}// {{{ quote()/*** DEPRECATED: Quotes a string so it can be safely used in a query** @param string $string the input string to quote** @return string The NULL string or the string quotes* in magic_quote_sybase style** @see DB_common::quoteSmart(), DB_common::escapeSimple()* @deprecated Deprecated in release 1.6.0* @internal*/function quote($string = null){return ($string === null) ? 'NULL' : "'".str_replace("'", "''", $string)."'";}// }}}// {{{ quoteIdentifier()/*** Quote a string so it can be safely used as a table or column name** Delimiting style depends on which database driver is being used.** NOTE: just because you CAN use delimited identifiers doesn't mean* you SHOULD use them. In general, they end up causing way more* problems than they solve.** Portability is broken by using the following characters inside* delimited identifiers:* + backtick (<kbd>`</kbd>) -- due to MySQL* + double quote (<kbd>"</kbd>) -- due to Oracle* + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access** Delimited identifiers are known to generally work correctly under* the following drivers:* + mssql* + mysql* + mysqli* + oci8* + odbc(access)* + odbc(db2)* + pgsql* + sqlite* + sybase** InterBase doesn't seem to be able to use delimited identifiers* via PHP 4. They work fine under PHP 5.** @param string $str identifier name to be quoted** @return string quoted identifier string** @since 1.6.0* @access public*/function quoteIdentifier($str){return '"' . str_replace('"', '""', $str) . '"';}// }}}// {{{ quoteSmart()/*** Format input so it can be safely used in a query** The output depends on the PHP data type of input and the database* type being used.** @param mixed $in data to be quoted** @return mixed the format of the results depends on the input's* PHP type:** <ul>* <li>* <kbd>input</kbd> -> <samp>returns</samp>* </li>* <li>* <kbd>null</kbd> -> the string <samp>NULL</samp>* </li>* <li>* <kbd>integer</kbd> or <kbd>double</kbd> -> the unquoted number* </li>* <li>* &type.bool; -> output depends on the driver in use* Most drivers return integers: <samp>1</samp> if* <kbd>true</kbd> or <samp>0</samp> if* <kbd>false</kbd>.* Some return strings: <samp>TRUE</samp> if* <kbd>true</kbd> or <samp>FALSE</samp> if* <kbd>false</kbd>.* Finally one returns strings: <samp>T</samp> if* <kbd>true</kbd> or <samp>F</samp> if* <kbd>false</kbd>. Here is a list of each DBMS,* the values returned and the suggested column type:* <ul>* <li>* <kbd>dbase</kbd> -> <samp>T/F</samp>* (<kbd>Logical</kbd>)* </li>* <li>* <kbd>fbase</kbd> -> <samp>TRUE/FALSE</samp>* (<kbd>BOOLEAN</kbd>)* </li>* <li>* <kbd>ibase</kbd> -> <samp>1/0</samp>* (<kbd>SMALLINT</kbd>) [1]* </li>* <li>* <kbd>ifx</kbd> -> <samp>1/0</samp>* (<kbd>SMALLINT</kbd>) [1]* </li>* <li>* <kbd>msql</kbd> -> <samp>1/0</samp>* (<kbd>INTEGER</kbd>)* </li>* <li>* <kbd>mssql</kbd> -> <samp>1/0</samp>* (<kbd>BIT</kbd>)* </li>* <li>* <kbd>mysql</kbd> -> <samp>1/0</samp>* (<kbd>TINYINT(1)</kbd>)* </li>* <li>* <kbd>mysqli</kbd> -> <samp>1/0</samp>* (<kbd>TINYINT(1)</kbd>)* </li>* <li>* <kbd>oci8</kbd> -> <samp>1/0</samp>* (<kbd>NUMBER(1)</kbd>)* </li>* <li>* <kbd>odbc</kbd> -> <samp>1/0</samp>* (<kbd>SMALLINT</kbd>) [1]* </li>* <li>* <kbd>pgsql</kbd> -> <samp>TRUE/FALSE</samp>* (<kbd>BOOLEAN</kbd>)* </li>* <li>* <kbd>sqlite</kbd> -> <samp>1/0</samp>* (<kbd>INTEGER</kbd>)* </li>* <li>* <kbd>sybase</kbd> -> <samp>1/0</samp>* (<kbd>TINYINT(1)</kbd>)* </li>* </ul>* [1] Accommodate the lowest common denominator because not all* versions of have <kbd>BOOLEAN</kbd>.* </li>* <li>* other (including strings and numeric strings) ->* the data with single quotes escaped by preceeding* single quotes, backslashes are escaped by preceeding* backslashes, then the whole string is encapsulated* between single quotes* </li>* </ul>** @since 1.6.0* @see DB_common::escapeSimple()* @access public*/function quoteSmart($in){if (is_int($in) || is_double($in)) {return $in;} elseif (is_bool($in)) {return $in ? 1 : 0;} elseif (is_null($in)) {return 'NULL';} else {return "'" . $this->escapeSimple($in) . "'";}}// }}}// {{{ escapeSimple()/*** Escape a string according to the current DBMS's standards** In SQLite, this makes things safe for inserts/updates, but may* cause problems when performing text comparisons against columns* containing binary data. See the* {@link http://php.net/sqlite_escape_string PHP manual} for more info.** @param string $str the string to be escaped** @return string the escaped string** @since 1.6.0* @see DB_common::quoteSmart()* @access public*/function escapeSimple($str) {return str_replace("'", "''", $str);}// }}}// {{{ provides()/*** Tell whether a DB implementation or its backend extension* supports a given feature** @param array $feature name of the feature (see the DB class doc)* @return bool whether this DB implementation supports $feature* @access public*/function provides($feature){return $this->features[$feature];}// }}}// {{{ errorCode()/*** Map native error codes to DB's portable ones** Requires that the DB implementation's constructor fills* in the <var>$errorcode_map</var> property.** @param mixed $nativecode the native error code, as returned by the* backend database extension (string or integer)** @return int a portable DB error code, or DB_ERROR if this DB* implementation has no mapping for the given error code.** @access public*/function errorCode($nativecode){if (isset($this->errorcode_map[$nativecode])) {return $this->errorcode_map[$nativecode];}// Fall back to DB_ERROR if there was no mapping.return DB_ERROR;}// }}}// {{{ errorMessage()/*** Map a DB error code to a textual message. This is actually* just a wrapper for DB::errorMessage()** @param integer $dbcode the DB error code** @return string the corresponding error message, of false* if the error code was unknown** @access public*/function errorMessage($dbcode){return DB::errorMessage($this->errorcode_map[$dbcode]);}// }}}// {{{ raiseError()/*** Communicate an error and invoke error callbacks, etc** Basically a wrapper for PEAR::raiseError without the message string.** @param mixed integer error code, or a PEAR error object (all* other parameters are ignored if this parameter is* an object** @param int error mode, see PEAR_Error docs** @param mixed If error mode is PEAR_ERROR_TRIGGER, this is the* error level (E_USER_NOTICE etc). If error mode is* PEAR_ERROR_CALLBACK, this is the callback function,* either as a function name, or as an array of an* object and method name. For other error modes this* parameter is ignored.** @param string Extra debug information. Defaults to the last* query and native error code.** @param mixed Native error code, integer or string depending the* backend.** @return object a PEAR error object** @access public* @see PEAR_Error*/function &raiseError($code = DB_ERROR, $mode = null, $options = null,$userinfo = null, $nativecode = null){// The error is yet a DB error objectif (is_object($code)) {// because we the static PEAR::raiseError, our global// handler should be used if it is setif ($mode === null && !empty($this->_default_error_mode)) {$mode = $this->_default_error_mode;$options = $this->_default_error_options;}$tmp = PEAR::raiseError($code, null, $mode, $options, null, null, true);return $tmp;}if ($userinfo === null) {$userinfo = $this->last_query;}if ($nativecode) {$userinfo .= ' [nativecode=' . trim($nativecode) . ']';}$tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo,'DB_Error', true);return $tmp;}// }}}// {{{ setFetchMode()/*** Sets which fetch mode should be used by default on queries* on this connection** @param integer $fetchmode DB_FETCHMODE_ORDERED or* DB_FETCHMODE_ASSOC, possibly bit-wise OR'ed with* DB_FETCHMODE_FLIPPED.** @param string $object_class The class of the object* to be returned by the fetch methods when* the DB_FETCHMODE_OBJECT mode is selected.* If no class is specified by default a cast* to object from the assoc array row will be done.* There is also the posibility to use and extend the* 'DB_row' class.** @see DB_FETCHMODE_ORDERED* @see DB_FETCHMODE_ASSOC* @see DB_FETCHMODE_FLIPPED* @see DB_FETCHMODE_OBJECT* @see DB_row::DB_row()* @access public*/function setFetchMode($fetchmode, $object_class = 'stdClass'){switch ($fetchmode) {case DB_FETCHMODE_OBJECT:$this->fetchmode_object_class = $object_class;case DB_FETCHMODE_ORDERED:case DB_FETCHMODE_ASSOC:$this->fetchmode = $fetchmode;break;default:return $this->raiseError('invalid fetchmode mode');}}// }}}// {{{ setOption()/*** Set run-time configuration options for PEAR DB** Options, their data types, default values and description:* <ul>* <li>* <var>autofree</var> <kbd>boolean</kbd> = <samp>false</samp>* <br />should results be freed automatically when there are no* more rows?* </li><li>* <var>debug</var> <kbd>integer</kbd> = <samp>0</samp>* <br />debug level* </li><li>* <var>persistent</var> <kbd>boolean</kbd> = <samp>false</samp>* <br />should the connection be persistent?* </li><li>* <var>portability</var> <kbd>integer</kbd> = <samp>DB_PORTABILITY_NONE</samp>* <br />portability mode constant (see below)* </li><li>* <var>seqname_format</var> <kbd>string</kbd> = <samp>%s_seq</samp>* <br />the sprintf() format string used on sequence names. This* format is applied to sequence names passed to* createSequence(), nextID() and dropSequence().* </li><li>* <var>ssl</var> <kbd>boolean</kbd> = <samp>false</samp>* <br />use ssl to connect?* </li>* </ul>** -----------------------------------------** PORTABILITY MODES** These modes are bitwised, so they can be combined using <kbd>|</kbd>* and removed using <kbd>^</kbd>. See the examples section below on how* to do this.** <samp>DB_PORTABILITY_NONE</samp>* turn off all portability features** This mode gets automatically turned on if the deprecated* <var>optimize</var> option gets set to <samp>performance</samp>.*** <samp>DB_PORTABILITY_LOWERCASE</samp>* convert names of tables and fields to lower case when using* <kbd>get*()</kbd>, <kbd>fetch*()</kbd> and <kbd>tableInfo()</kbd>** This mode gets automatically turned on in the following databases* if the deprecated option <var>optimize</var> gets set to* <samp>portability</samp>:* + oci8*** <samp>DB_PORTABILITY_RTRIM</samp>* right trim the data output by <kbd>get*()</kbd> <kbd>fetch*()</kbd>*** <samp>DB_PORTABILITY_DELETE_COUNT</samp>* force reporting the number of rows deleted** Some DBMS's don't count the number of rows deleted when performing* simple <kbd>DELETE FROM tablename</kbd> queries. This portability* mode tricks such DBMS's into telling the count by adding* <samp>WHERE 1=1</samp> to the end of <kbd>DELETE</kbd> queries.** This mode gets automatically turned on in the following databases* if the deprecated option <var>optimize</var> gets set to* <samp>portability</samp>:* + fbsql* + mysql* + mysqli* + sqlite*** <samp>DB_PORTABILITY_NUMROWS</samp>* enable hack that makes <kbd>numRows()</kbd> work in Oracle** This mode gets automatically turned on in the following databases* if the deprecated option <var>optimize</var> gets set to* <samp>portability</samp>:* + oci8*** <samp>DB_PORTABILITY_ERRORS</samp>* makes certain error messages in certain drivers compatible* with those from other DBMS's** + mysql, mysqli: change unique/primary key constraints* DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT** + odbc(access): MS's ODBC driver reports 'no such field' as code* 07001, which means 'too few parameters.' When this option is on* that code gets mapped to DB_ERROR_NOSUCHFIELD.* DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD*** <samp>DB_PORTABILITY_NULL_TO_EMPTY</samp>* convert null values to empty strings in data output by get*() and* fetch*(). Needed because Oracle considers empty strings to be null,* while most other DBMS's know the difference between empty and null.*** <samp>DB_PORTABILITY_ALL</samp>* turn on all portability features** -----------------------------------------** Example 1. Simple setOption() example* <code> <?php* $dbh->setOption('autofree', true);* ?></code>** Example 2. Portability for lowercasing and trimming* <code> <?php* $dbh->setOption('portability',* DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM);* ?></code>** Example 3. All portability options except trimming* <code> <?php* $dbh->setOption('portability',* DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM);* ?></code>** @param string $option option name* @param mixed $value value for the option** @return int DB_OK on success. DB_Error object on failure.** @see DB_common::$options*/function setOption($option, $value){if (isset($this->options[$option])) {$this->options[$option] = $value;/** Backwards compatibility check for the deprecated 'optimize'* option. Done here in case settings change after connecting.*/if ($option == 'optimize') {if ($value == 'portability') {switch ($this->phptype) {case 'oci8':$this->options['portability'] =DB_PORTABILITY_LOWERCASE |DB_PORTABILITY_NUMROWS;break;case 'fbsql':case 'mysql':case 'mysqli':case 'sqlite':$this->options['portability'] =DB_PORTABILITY_DELETE_COUNT;break;}} else {$this->options['portability'] = DB_PORTABILITY_NONE;}}return DB_OK;}return $this->raiseError("unknown option $option");}// }}}// {{{ getOption()/*** Returns the value of an option** @param string $option option name** @return mixed the option value*/function getOption($option){if (isset($this->options[$option])) {return $this->options[$option];}return $this->raiseError("unknown option $option");}// }}}// {{{ prepare()/*** Prepares a query for multiple execution with execute()** Creates a query that can be run multiple times. Each time it is run,* the placeholders, if any, will be replaced by the contents of* execute()'s $data argument.** Three types of placeholders can be used:* + <kbd>?</kbd> scalar value (i.e. strings, integers). The system* will automatically quote and escape the data.* + <kbd>!</kbd> value is inserted 'as is'* + <kbd>&</kbd> requires a file name. The file's contents get* inserted into the query (i.e. saving binary* data in a db)** Example 1.* <code> <?php* $sth = $dbh->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');* $data = array(* "John's text",* "'it''s good'",* 'filename.txt'* );* $res = $dbh->execute($sth, $data);* ?></code>** Use backslashes to escape placeholder characters if you don't want* them to be interpreted as placeholders:* <pre>* "UPDATE foo SET col=? WHERE col='over \& under'"* </pre>** With some database backends, this is emulated.** {@internal ibase and oci8 have their own prepare() methods.}}** @param string $query query to be prepared** @return mixed DB statement resource on success. DB_Error on failure.** @see DB_common::execute()* @access public*/function prepare($query){$tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,PREG_SPLIT_DELIM_CAPTURE);$token = 0;$types = array();$newtokens = array();foreach ($tokens as $val) {switch ($val) {case '?':$types[$token++] = DB_PARAM_SCALAR;break;case '&':$types[$token++] = DB_PARAM_OPAQUE;break;case '!':$types[$token++] = DB_PARAM_MISC;break;default:$newtokens[] = preg_replace('/\\\([&?!])/', "\\1", $val);}}$this->prepare_tokens[] = &$newtokens;end($this->prepare_tokens);$k = key($this->prepare_tokens);$this->prepare_types[$k] = $types;$this->prepared_queries[$k] = implode(' ', $newtokens);return $k;}// }}}// {{{ autoPrepare()/*** Automaticaly generate an insert or update query and pass it to prepare()** @param string $table name of the table* @param array $table_fields ordered array containing the fields names* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE)* @param string $where in case of update queries, this string will be put after the sql WHERE statement* @return resource handle for the query* @see DB_common::prepare(), DB_common::buildManipSQL()* @access public*/function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT, $where = false){$query = $this->buildManipSQL($table, $table_fields, $mode, $where);return $this->prepare($query);}// }}}// {{{ autoExecute()/*** Automaticaly generate an insert or update query and call prepare()* and execute() with it** @param string $table name of the table* @param array $fields_values assoc ($key=>$value) where $key is a field name and $value its value* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE)* @param string $where in case of update queries, this string will be put after the sql WHERE statement* @return mixed a new DB_Result or a DB_Error when fail* @see DB_common::autoPrepare(), DB_common::buildManipSQL()* @access public*/function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT, $where = false){$sth = $this->autoPrepare($table, array_keys($fields_values), $mode, $where);$ret =& $this->execute($sth, array_values($fields_values));$this->freePrepared($sth);return $ret;}// }}}// {{{ buildManipSQL()/*** Make automaticaly an sql query for prepare()** Example : buildManipSQL('table_sql', array('field1', 'field2', 'field3'), DB_AUTOQUERY_INSERT)* will return the string : INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)* NB : - This belongs more to a SQL Builder class, but this is a simple facility* - Be carefull ! If you don't give a $where param with an UPDATE query, all* the records of the table will be updated !** @param string $table name of the table* @param array $table_fields ordered array containing the fields names* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE)* @param string $where in case of update queries, this string will be put after the sql WHERE statement* @return string sql query for prepare()* @access public*/function buildManipSQL($table, $table_fields, $mode, $where = false){if (count($table_fields) == 0) {$this->raiseError(DB_ERROR_NEED_MORE_DATA);}$first = true;switch ($mode) {case DB_AUTOQUERY_INSERT:$values = '';$names = '';foreach ($table_fields as $value) {if ($first) {$first = false;} else {$names .= ',';$values .= ',';}$names .= $value;$values .= '?';}return "INSERT INTO $table ($names) VALUES ($values)";case DB_AUTOQUERY_UPDATE:$set = '';foreach ($table_fields as $value) {if ($first) {$first = false;} else {$set .= ',';}$set .= "$value = ?";}$sql = "UPDATE $table SET $set";if ($where) {$sql .= " WHERE $where";}return $sql;default:$this->raiseError(DB_ERROR_SYNTAX);}}// }}}// {{{ execute()/*** Executes a DB statement prepared with prepare()** Example 1.* <code> <?php* $sth = $dbh->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');* $data = array(* "John's text",* "'it''s good'",* 'filename.txt'* );* $res =& $dbh->execute($sth, $data);* ?></code>** @param resource $stmt a DB statement resource returned from prepare()* @param mixed $data array, string or numeric data to be used in* execution of the statement. Quantity of items* passed must match quantity of placeholders in* query: meaning 1 placeholder for non-array* parameters or 1 placeholder per array element.** @return object a new DB_Result or a DB_Error when fail** {@internal ibase and oci8 have their own execute() methods.}}** @see DB_common::prepare()* @access public*/function &execute($stmt, $data = array()){$realquery = $this->executeEmulateQuery($stmt, $data);if (DB::isError($realquery)) {return $realquery;}$result = $this->simpleQuery($realquery);if (DB::isError($result) || $result === DB_OK) {return $result;} else {$tmp =& new DB_result($this, $result);return $tmp;}}// }}}// {{{ executeEmulateQuery()/*** Emulates the execute statement, when not supported** @param resource $stmt a DB statement resource returned from execute()* @param mixed $data array, string or numeric data to be used in* execution of the statement. Quantity of items* passed must match quantity of placeholders in* query: meaning 1 placeholder for non-array* parameters or 1 placeholder per array element.** @return mixed a string containing the real query run when emulating* prepare/execute. A DB error code is returned on failure.** @see DB_common::execute()* @access private*/function executeEmulateQuery($stmt, $data = array()){if (!is_array($data)) {$data = array($data);}if (count($this->prepare_types[$stmt]) != count($data)) {$this->last_query = $this->prepared_queries[$stmt];return $this->raiseError(DB_ERROR_MISMATCH);}$realquery = $this->prepare_tokens[$stmt][0];$i = 0;foreach ($data as $value) {if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) {$realquery .= $this->quoteSmart($value);} elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) {$fp = @fopen($value, 'rb');if (!$fp) {return $this->raiseError(DB_ERROR_ACCESS_VIOLATION);}$realquery .= $this->quoteSmart(fread($fp, filesize($value)));fclose($fp);} else {$realquery .= $value;}$realquery .= $this->prepare_tokens[$stmt][++$i];}return $realquery;}// }}}// {{{ executeMultiple()/*** This function does several execute() calls on the same* statement handle** $data must be an array indexed numerically* from 0, one execute call is done for every "row" in the array.** If an error occurs during execute(), executeMultiple() does not* execute the unfinished rows, but rather returns that error.** @param resource $stmt query handle from prepare()* @param array $data numeric array containing the* data to insert into the query** @return mixed DB_OK or DB_Error** @see DB_common::prepare(), DB_common::execute()* @access public*/function executeMultiple($stmt, $data){foreach ($data as $value) {$res =& $this->execute($stmt, $value);if (DB::isError($res)) {return $res;}}return DB_OK;}// }}}// {{{ freePrepared()/*** Free the resource used in a prepared query** @param $stmt The resurce returned by the prepare() function* @see DB_common::prepare()*/function freePrepared($stmt){// Free the internal prepared varsif (isset($this->prepare_tokens[$stmt])) {unset($this->prepare_tokens[$stmt]);unset($this->prepare_types[$stmt]);unset($this->prepared_queries[$stmt]);return true;}return false;}// }}}// {{{ modifyQuery()/*** This method is used by backends to alter queries for various* reasons** It is defined here to assure that all implementations* have this method defined.** @param string $query query to modify** @return the new (modified) query** @access private*/function modifyQuery($query) {return $query;}// }}}// {{{ modifyLimitQuery()/*** This method is used by backends to alter limited queries** @param string $query query to modify* @param integer $from the row to start to fetching* @param integer $count the numbers of rows to fetch** @return the new (modified) query** @access private*/function modifyLimitQuery($query, $from, $count, $params = array()){return $query;}// }}}// {{{ query()/*** Send a query to the database and return any results with a* DB_result object** The query string can be either a normal statement to be sent directly* to the server OR if <var>$params</var> are passed the query can have* placeholders and it will be passed through prepare() and execute().** @param string $query the SQL query or the statement to prepare* @param mixed $params array, string or numeric data to be used in* execution of the statement. Quantity of items* passed must match quantity of placeholders in* query: meaning 1 placeholder for non-array* parameters or 1 placeholder per array element.** @return mixed a DB_result object or DB_OK on success, a DB* error on failure** @see DB_result, DB_common::prepare(), DB_common::execute()* @access public*/function &query($query, $params = array()){if (sizeof($params) > 0) {$sth = $this->prepare($query);if (DB::isError($sth)) {return $sth;}$ret =& $this->execute($sth, $params);$this->freePrepared($sth);return $ret;} else {$result = $this->simpleQuery($query);if (DB::isError($result) || $result === DB_OK) {return $result;} else {$tmp =& new DB_result($this, $result);return $tmp;}}}// }}}// {{{ limitQuery()/*** Generates a limited query** @param string $query query* @param integer $from the row to start to fetching* @param integer $count the numbers of rows to fetch* @param mixed $params array, string or numeric data to be used in* execution of the statement. Quantity of items* passed must match quantity of placeholders in* query: meaning 1 placeholder for non-array* parameters or 1 placeholder per array element.** @return mixed a DB_Result object, DB_OK or a DB_Error** @access public*/function &limitQuery($query, $from, $count, $params = array()){$query = $this->modifyLimitQuery($query, $from, $count, $params);if (DB::isError($query)){return $query;}$result =& $this->query($query, $params);if (is_a($result, 'DB_result')) {$result->setOption('limit_from', $from);$result->setOption('limit_count', $count);}return $result;}// }}}// {{{ getOne()/*** Fetch the first column of the first row of data returned from* a query** Takes care of doing the query and freeing the results when finished.** @param string $query the SQL query* @param mixed $params array, string or numeric data to be used in* execution of the statement. Quantity of items* passed must match quantity of placeholders in* query: meaning 1 placeholder for non-array* parameters or 1 placeholder per array element.** @return mixed the returned value of the query. DB_Error on failure.** @access public*/function &getOne($query, $params = array()){settype($params, 'array');if (sizeof($params) > 0) {$sth = $this->prepare($query);if (DB::isError($sth)) {return $sth;}$res =& $this->execute($sth, $params);$this->freePrepared($sth);} else {$res =& $this->query($query);}if (DB::isError($res)) {return $res;}$err = $res->fetchInto($row, DB_FETCHMODE_ORDERED);$res->free();if ($err !== DB_OK) {return $err;}return $row[0];}// }}}// {{{ getRow()/*** Fetch the first row of data returned from a query** Takes care of doing the query and freeing the results when finished.** @param string $query the SQL query* @param array $params array to be used in execution of the statement.* Quantity of array elements must match quantity* of placeholders in query. This function does* NOT support scalars.* @param int $fetchmode the fetch mode to use** @return array the first row of results as an array indexed from* 0, or a DB error code.** @access public*/function &getRow($query,$params = array(),$fetchmode = DB_FETCHMODE_DEFAULT){// compat check, the params and fetchmode parameters used to// have the opposite orderif (!is_array($params)) {if (is_array($fetchmode)) {if ($params === null) {$tmp = DB_FETCHMODE_DEFAULT;} else {$tmp = $params;}$params = $fetchmode;$fetchmode = $tmp;} elseif ($params !== null) {$fetchmode = $params;$params = array();}}if (sizeof($params) > 0) {$sth = $this->prepare($query);if (DB::isError($sth)) {return $sth;}$res =& $this->execute($sth, $params);$this->freePrepared($sth);} else {$res =& $this->query($query);}if (DB::isError($res)) {return $res;}$err = $res->fetchInto($row, $fetchmode);$res->free();if ($err !== DB_OK) {return $err;}return $row;}// }}}// {{{ getCol()/*** Fetch a single column from a result set and return it as an* indexed array** @param string $query the SQL query* @param mixed $col which column to return (integer [column number,* starting at 0] or string [column name])* @param mixed $params array, string or numeric data to be used in* execution of the statement. Quantity of items* passed must match quantity of placeholders in* query: meaning 1 placeholder for non-array* parameters or 1 placeholder per array element.** @return array an indexed array with the data from the first* row at index 0, or a DB error code** @see DB_common::query()* @access public*/function &getCol($query, $col = 0, $params = array()){settype($params, 'array');if (sizeof($params) > 0) {$sth = $this->prepare($query);if (DB::isError($sth)) {return $sth;}$res =& $this->execute($sth, $params);$this->freePrepared($sth);} else {$res =& $this->query($query);}if (DB::isError($res)) {return $res;}$fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC;if (!is_array($row = $res->fetchRow($fetchmode))) {$ret = array();} else {if (!array_key_exists($col, $row)) {$ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD);} else {$ret = array($row[$col]);while (is_array($row = $res->fetchRow($fetchmode))) {$ret[] = $row[$col];}}}$res->free();if (DB::isError($row)) {$ret = $row;}return $ret;}// }}}// {{{ getAssoc()/*** Fetch the entire result set of a query and return it as an* associative array using the first column as the key** If the result set contains more than two columns, the value* will be an array of the values from column 2-n. If the result* set contains only two columns, the returned value will be a* scalar with the value of the second column (unless forced to an* array with the $force_array parameter). A DB error code is* returned on errors. If the result set contains fewer than two* columns, a DB_ERROR_TRUNCATED error is returned.** For example, if the table "mytable" contains:** <pre>* ID TEXT DATE* --------------------------------* 1 'one' 944679408* 2 'two' 944679408* 3 'three' 944679408* </pre>** Then the call getAssoc('SELECT id,text FROM mytable') returns:* <pre>* array(* '1' => 'one',* '2' => 'two',* '3' => 'three',* )* </pre>** ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:* <pre>* array(* '1' => array('one', '944679408'),* '2' => array('two', '944679408'),* '3' => array('three', '944679408')* )* </pre>** If the more than one row occurs with the same value in the* first column, the last row overwrites all previous ones by* default. Use the $group parameter if you don't want to* overwrite like this. Example:** <pre>* getAssoc('SELECT category,id,name FROM mytable', false, null,* DB_FETCHMODE_ASSOC, true) returns:** array(* '1' => array(array('id' => '4', 'name' => 'number four'),* array('id' => '6', 'name' => 'number six')* ),* '9' => array(array('id' => '4', 'name' => 'number four'),* array('id' => '6', 'name' => 'number six')* )* )* </pre>** Keep in mind that database functions in PHP usually return string* values for results regardless of the database's internal type.** @param string $query the SQL query* @param boolean $force_array used only when the query returns* exactly two columns. If true, the values* of the returned array will be one-element* arrays instead of scalars.* @param mixed $params array, string or numeric data to be used in* execution of the statement. Quantity of items* passed must match quantity of placeholders in* query: meaning 1 placeholder for non-array* parameters or 1 placeholder per array element.* @param int $fetchmode the fetch mode to use* @param boolean $group if true, the values of the returned array* is wrapped in another array. If the same* key value (in the first column) repeats* itself, the values will be appended to* this array instead of overwriting the* existing values.** @return array associative array with results from the query.* DB Error on failure.** @access public*/function &getAssoc($query, $force_array = false, $params = array(),$fetchmode = DB_FETCHMODE_DEFAULT, $group = false){settype($params, 'array');if (sizeof($params) > 0) {$sth = $this->prepare($query);if (DB::isError($sth)) {return $sth;}$res =& $this->execute($sth, $params);$this->freePrepared($sth);} else {$res =& $this->query($query);}if (DB::isError($res)) {return $res;}if ($fetchmode == DB_FETCHMODE_DEFAULT) {$fetchmode = $this->fetchmode;}$cols = $res->numCols();if ($cols < 2) {$tmp =& $this->raiseError(DB_ERROR_TRUNCATED);return $tmp;}$results = array();if ($cols > 2 || $force_array) {// return array values// XXX this part can be optimizedif ($fetchmode == DB_FETCHMODE_ASSOC) {while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) {reset($row);$key = current($row);unset($row[key($row)]);if ($group) {$results[$key][] = $row;} else {$results[$key] = $row;}}} elseif ($fetchmode == DB_FETCHMODE_OBJECT) {while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) {$arr = get_object_vars($row);$key = current($arr);if ($group) {$results[$key][] = $row;} else {$results[$key] = $row;}}} else {while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {// we shift away the first element to get// indices running from 0 again$key = array_shift($row);if ($group) {$results[$key][] = $row;} else {$results[$key] = $row;}}}if (DB::isError($row)) {$results = $row;}} else {// return scalar valueswhile (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {if ($group) {$results[$row[0]][] = $row[1];} else {$results[$row[0]] = $row[1];}}if (DB::isError($row)) {$results = $row;}}$res->free();return $results;}// }}}// {{{ getAll()/*** Fetch all the rows returned from a query** @param string $query the SQL query* @param array $params array to be used in execution of the statement.* Quantity of array elements must match quantity* of placeholders in query. This function does* NOT support scalars.* @param int $fetchmode the fetch mode to use** @return array an nested array. DB error on failure.** @access public*/function &getAll($query,$params = array(),$fetchmode = DB_FETCHMODE_DEFAULT){// compat check, the params and fetchmode parameters used to// have the opposite orderif (!is_array($params)) {if (is_array($fetchmode)) {if ($params === null) {$tmp = DB_FETCHMODE_DEFAULT;} else {$tmp = $params;}$params = $fetchmode;$fetchmode = $tmp;} elseif ($params !== null) {$fetchmode = $params;$params = array();}}if (sizeof($params) > 0) {$sth = $this->prepare($query);if (DB::isError($sth)) {return $sth;}$res =& $this->execute($sth, $params);$this->freePrepared($sth);} else {$res =& $this->query($query);}if (DB::isError($res) || $res === DB_OK) {return $res;}$results = array();while (DB_OK === $res->fetchInto($row, $fetchmode)) {if ($fetchmode & DB_FETCHMODE_FLIPPED) {foreach ($row as $key => $val) {$results[$key][] = $val;}} else {$results[] = $row;}}$res->free();if (DB::isError($row)) {$tmp =& $this->raiseError($row);return $tmp;}return $results;}// }}}// {{{ autoCommit()/*** enable automatic Commit** @param boolean $onoff* @return mixed DB_Error** @access public*/function autoCommit($onoff=false){return $this->raiseError(DB_ERROR_NOT_CAPABLE);}// }}}// {{{ commit()/*** starts a Commit** @return mixed DB_Error** @access public*/function commit(){return $this->raiseError(DB_ERROR_NOT_CAPABLE);}// }}}// {{{ rollback()/*** starts a rollback** @return mixed DB_Error** @access public*/function rollback(){return $this->raiseError(DB_ERROR_NOT_CAPABLE);}// }}}// {{{ numRows()/*** Returns the number of rows in a result object** @param object DB_Result the result object to check** @return mixed DB_Error or the number of rows** @access public*/function numRows($result){return $this->raiseError(DB_ERROR_NOT_CAPABLE);}// }}}// {{{ affectedRows()/*** Returns the affected rows of a query** @return mixed DB_Error or number of rows** @access public*/function affectedRows(){return $this->raiseError(DB_ERROR_NOT_CAPABLE);}// }}}// {{{ errorNative()/*** Returns an errormessage, provides by the database** @return mixed DB_Error or message** @access public*/function errorNative(){return $this->raiseError(DB_ERROR_NOT_CAPABLE);}// }}}// {{{ getSequenceName()/*** Generate the name used inside the database for a sequence** The createSequence() docblock contains notes about storing sequence* names.** @param string $sqn the sequence's public name** @return string the sequence's name in the backend** @see DB_common::createSequence(), DB_common::dropSequence(),* DB_common::nextID(), DB_common::setOption()* @access private*/function getSequenceName($sqn){return sprintf($this->getOption('seqname_format'),preg_replace('/[^a-z0-9_.]/i', '_', $sqn));}// }}}// {{{ nextId()/*** Returns the next free id in a sequence** @param string $seq_name name of the sequence* @param boolean $ondemand when true, the seqence is automatically* created if it does not exist** @return int the next id number in the sequence. DB_Error if problem.** @see DB_common::createSequence(), DB_common::dropSequence(),* DB_common::getSequenceName()* @access public*/function nextId($seq_name, $ondemand = true){return $this->raiseError(DB_ERROR_NOT_CAPABLE);}// }}}// {{{ createSequence()/*** Creates a new sequence** The name of a given sequence is determined by passing the string* provided in the <var>$seq_name</var> argument through PHP's sprintf()* function using the value from the <var>seqname_format</var> option as* the sprintf()'s format argument.** <var>seqname_format</var> is set via setOption().** @param string $seq_name name of the new sequence** @return int DB_OK on success. A DB_Error object is returned if* problems arise.** @see DB_common::dropSequence(), DB_common::getSequenceName(),* DB_common::nextID()* @access public*/function createSequence($seq_name){return $this->raiseError(DB_ERROR_NOT_CAPABLE);}// }}}// {{{ dropSequence()/*** Deletes a sequence** @param string $seq_name name of the sequence to be deleted** @return int DB_OK on success. DB_Error if problems.** @see DB_common::createSequence(), DB_common::getSequenceName(),* DB_common::nextID()* @access public*/function dropSequence($seq_name){return $this->raiseError(DB_ERROR_NOT_CAPABLE);}// }}}// {{{ tableInfo()/*** Returns information about a table or a result set** The format of the resulting array depends on which <var>$mode</var>* you select. The sample output below is based on this query:* <pre>* SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId* FROM tblFoo* JOIN tblBar ON tblFoo.fldId = tblBar.fldId* </pre>** <ul>* <li>** <kbd>null</kbd> (default)* <pre>* [0] => Array (* [table] => tblFoo* [name] => fldId* [type] => int* [len] => 11* [flags] => primary_key not_null* )* [1] => Array (* [table] => tblFoo* [name] => fldPhone* [type] => string* [len] => 20* [flags] =>* )* [2] => Array (* [table] => tblBar* [name] => fldId* [type] => int* [len] => 11* [flags] => primary_key not_null* )* </pre>** </li><li>** <kbd>DB_TABLEINFO_ORDER</kbd>** <p>In addition to the information found in the default output,* a notation of the number of columns is provided by the* <samp>num_fields</samp> element while the <samp>order</samp>* element provides an array with the column names as the keys and* their location index number (corresponding to the keys in the* the default output) as the values.</p>** <p>If a result set has identical field names, the last one is* used.</p>** <pre>* [num_fields] => 3* [order] => Array (* [fldId] => 2* [fldTrans] => 1* )* </pre>** </li><li>** <kbd>DB_TABLEINFO_ORDERTABLE</kbd>** <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> but adds more* dimensions to the array in which the table names are keys and* the field names are sub-keys. This is helpful for queries that* join tables which have identical field names.</p>** <pre>* [num_fields] => 3* [ordertable] => Array (* [tblFoo] => Array (* [fldId] => 0* [fldPhone] => 1* )* [tblBar] => Array (* [fldId] => 2* )* )* </pre>** </li>* </ul>** The <samp>flags</samp> element contains a space separated list* of extra information about the field. This data is inconsistent* between DBMS's due to the way each DBMS works.* + <samp>primary_key</samp>* + <samp>unique_key</samp>* + <samp>multiple_key</samp>* + <samp>not_null</samp>** Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>* elements if <var>$result</var> is a table name. The following DBMS's* provide full information from queries:* + fbsql* + mysql** If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp>* turned on, the names of tables and fields will be lowercased.** @param object|string $result DB_result object from a query or a* string containing the name of a table.* While this also accepts a query result* resource identifier, this behavior is* deprecated.* @param int $mode either unused or one of the tableInfo modes:* <kbd>DB_TABLEINFO_ORDERTABLE</kbd>,* <kbd>DB_TABLEINFO_ORDER</kbd> or* <kbd>DB_TABLEINFO_FULL</kbd> (which does both).* These are bitwise, so the first two can be* combined using <kbd>|</kbd>.* @return array an associative array with the information requested.* If something goes wrong an error object is returned.** @see DB_common::setOption()* @access public*/function tableInfo($result, $mode = null){/** If the DB_<driver> class has a tableInfo() method, that one* overrides this one. But, if the driver doesn't have one,* this method runs and tells users about that fact.*/return $this->raiseError(DB_ERROR_NOT_CAPABLE);}// }}}// {{{ getTables()/*** @deprecated Deprecated in release 1.2 or lower*/function getTables(){return $this->getListOf('tables');}// }}}// {{{ getListOf()/*** list internal DB info* valid values for $type are db dependent,* often: databases, users, view, functions** @param string $type type of requested info** @return mixed DB_Error or the requested data** @access public*/function getListOf($type){$sql = $this->getSpecialQuery($type);if ($sql === null) { // No supportreturn $this->raiseError(DB_ERROR_UNSUPPORTED);} elseif (is_int($sql) || DB::isError($sql)) { // Previous errorreturn $this->raiseError($sql);} elseif (is_array($sql)) { // Already the resultreturn $sql;}return $this->getCol($sql); // Launch this query}// }}}// {{{ getSpecialQuery()/*** Returns the query needed to get some backend info** @param string $type What kind of info you want to retrieve** @return string The SQL query string** @access public*/function getSpecialQuery($type){return $this->raiseError(DB_ERROR_UNSUPPORTED);}// }}}// {{{ _rtrimArrayValues()/*** Right trim all strings in an array** @param array $array the array to be trimmed (passed by reference)* @return void* @access private*/function _rtrimArrayValues(&$array){foreach ($array as $key => $value) {if (is_string($value)) {$array[$key] = rtrim($value);}}}// }}}// {{{ _convertNullArrayValuesToEmpty()/*** Convert all null values in an array to empty strings** @param array $array the array to be de-nullified (passed by reference)* @return void* @access private*/function _convertNullArrayValuesToEmpty(&$array){foreach ($array as $key => $value) {if (is_null($value)) {$array[$key] = '';}}}// }}}}/** Local variables:* tab-width: 4* c-basic-offset: 4* End:*/?>