You are on page 1of 74

<?

php // vim: set et ts=4 sw=4 fdm=marker: // +----------------------------------------------------------------------+ // | PHP versions 4 and 5 | // +----------------------------------------------------------------------+ // | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, | // | Stig. S. Bakken, Lukas Smith | // | All rights reserved. | // +----------------------------------------------------------------------+ // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB | // | API as well as database abstraction for PHP applications. | // | This LICENSE is in the BSD license style. | // | | // | Redistribution and use in source and binary forms, with or without | // | modification, are permitted provided that the following conditions | // | are met: | // | | // | Redistributions of source code must retain the above copyright | // | notice, this list of conditions and the following disclaimer. | // | | // | Redistributions in binary form must reproduce the above copyright | // | notice, this list of conditions and the following disclaimer in the | // | documentation and/or other materials provided with the distribution. | // | | // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, | // | Lukas Smith nor the names of his contributors may be used to endorse | // | or promote products derived from this software without specific prior| // | written permission. | // | | // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS| // | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY| // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | // | POSSIBILITY OF SUCH DAMAGE. | // +----------------------------------------------------------------------+ // | Author: Lukas Smith <smith@pooteeweet.org> | // +----------------------------------------------------------------------+ // // $Id: MDB2.php,v 1.292 2007/04/25 09:31:01 quipo Exp $ // /** * @package * @category * @author */ MDB2 Database Lukas Smith <smith@pooteeweet.org>

require_once 'PEAR.php'; // {{{ Error constants /** * The method mapErrorCode in each MDB2_dbtype implementation maps

* native error codes to one of these. * * If you add an error code here, make sure you also add a textual * version of it in MDB2::errorMessage(). */ define('MDB2_OK', true); define('MDB2_ERROR', -1); define('MDB2_ERROR_SYNTAX', -2); define('MDB2_ERROR_CONSTRAINT', -3); define('MDB2_ERROR_NOT_FOUND', -4); define('MDB2_ERROR_ALREADY_EXISTS', -5); define('MDB2_ERROR_UNSUPPORTED', -6); define('MDB2_ERROR_MISMATCH', -7); define('MDB2_ERROR_INVALID', -8); define('MDB2_ERROR_NOT_CAPABLE', -9); define('MDB2_ERROR_TRUNCATED', -10); define('MDB2_ERROR_INVALID_NUMBER', -11); define('MDB2_ERROR_INVALID_DATE', -12); define('MDB2_ERROR_DIVZERO', -13); define('MDB2_ERROR_NODBSELECTED', -14); define('MDB2_ERROR_CANNOT_CREATE', -15); define('MDB2_ERROR_CANNOT_DELETE', -16); define('MDB2_ERROR_CANNOT_DROP', -17); define('MDB2_ERROR_NOSUCHTABLE', -18); define('MDB2_ERROR_NOSUCHFIELD', -19); define('MDB2_ERROR_NEED_MORE_DATA', -20); define('MDB2_ERROR_NOT_LOCKED', -21); define('MDB2_ERROR_VALUE_COUNT_ON_ROW', -22); define('MDB2_ERROR_INVALID_DSN', -23); define('MDB2_ERROR_CONNECT_FAILED', -24); define('MDB2_ERROR_EXTENSION_NOT_FOUND',-25); define('MDB2_ERROR_NOSUCHDB', -26); define('MDB2_ERROR_ACCESS_VIOLATION', -27); define('MDB2_ERROR_CANNOT_REPLACE', -28); define('MDB2_ERROR_CONSTRAINT_NOT_NULL',-29); define('MDB2_ERROR_DEADLOCK', -30); define('MDB2_ERROR_CANNOT_ALTER', -31); define('MDB2_ERROR_MANAGER', -32); define('MDB2_ERROR_MANAGER_PARSE', -33); define('MDB2_ERROR_LOADMODULE', -34); define('MDB2_ERROR_INSUFFICIENT_DATA', -35); // }}} // {{{ Verbose constants /** * These are just helper constants to more verbosely express parameters to prepa re() */ define('MDB2_PREPARE_MANIP', false); define('MDB2_PREPARE_RESULT', null); // }}} // {{{ Fetchmode constants /** * This is a special constant that tells MDB2 the user hasn't specified * any particular get mode, so the default should be used. */ define('MDB2_FETCHMODE_DEFAULT', 0);

/** * Column data indexed by numbers, ordered from 0 and up */ define('MDB2_FETCHMODE_ORDERED', 1); /** * Column data indexed by column names */ define('MDB2_FETCHMODE_ASSOC', 2); /** * Column data as object properties */ define('MDB2_FETCHMODE_OBJECT', 3); /** * For multi-dimensional results: normally the first level of arrays * is the row number, and the second level indexed by column number or name. * MDB2_FETCHMODE_FLIPPED switches this order, so the first level of arrays * is the column name, and the second level the row number. */ define('MDB2_FETCHMODE_FLIPPED', 4); // }}} // {{{ Portability mode constants /** * Portability: turn off all portability features. * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_NONE', 0); /** * Portability: convert names of tables and fields to case defined in the * "field_case" option when using the query*(), fetch*() and tableInfo() methods . * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_FIX_CASE', 1); /** * Portability: right trim the data output by query*() and fetch*(). * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_RTRIM', 2); /** * Portability: force reporting the number of rows deleted. * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_DELETE_COUNT', 4); /** * Portability: not needed in MDB2 (just left here for compatibility to DB) * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_NUMROWS', 8); /**

* Portability: makes certain error messages in certain drivers compatible * with those from other DBMS's. * * + mysql, mysqli: change unique/primary key constraints * MDB2_ERROR_ALREADY_EXISTS -> MDB2_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 MDB2_ERROR_NOSUCHFIELD. * * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_ERRORS', 16); /** * Portability: convert empty values to null strings in data output by * query*() and fetch*(). * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_EMPTY_TO_NULL', 32); /** * Portability: removes database/table qualifiers from associative indexes * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES', 64); /** * Portability: turn on all portability features. * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_ALL', 127); // }}} // {{{ Globals for class instance tracking /** * These are global variables that are used to track the various class instances */ $GLOBALS['_MDB2_databases'] = array(); $GLOBALS['_MDB2_dsninfo_default'] = array( 'phptype' => false, 'dbsyntax' => false, 'username' => false, 'password' => false, 'protocol' => false, 'hostspec' => false, 'port' => false, 'socket' => false, 'database' => false, 'mode' => false, ); // }}} // {{{ class MDB2 /** * The main 'MDB2' class is simply a container class with some static * methods for creating DB objects as well as some utility functions

* common to all parts of DB. * * The object model of MDB2 is as follows (indentation means inheritance): * * MDB2 The main MDB2 class. This is simply a utility class * with some 'static' methods for creating MDB2 objects as * well as common utility functions for other MDB2 classes. * * MDB2_Driver_Common The base for each MDB2 implementation. Provides default * | implementations (in OO lingo virtual methods) for * | the actual DB implementations as well as a bunch of * | query utility functions. * | * +-MDB2_Driver_mysql The MDB2 implementation for MySQL. Inherits MDB2_Driver_ Common. * When calling MDB2::factory or MDB2::connect for MySQL * connections, the object returned is an instance of this * class. * +-MDB2_Driver_pgsql The MDB2 implementation for PostGreSQL. Inherits MDB2_Dr iver_Common. * When calling MDB2::factory or MDB2::connect for PostGreSQL * connections, the object returned is an instance of this * class. * * @package MDB2 * @category Database * @author Lukas Smith <smith@pooteeweet.org> */ class MDB2 { // {{{ function setOptions(&$db, $options) /** * set option array in an exiting database object * * @param MDB2_Driver_Common MDB2 object * @param array An associative array of option names and their values. * * @return mixed MDB2_OK or a PEAR Error object * * @access public */ function setOptions(&$db, $options) { if (is_array($options)) { foreach ($options as $option => $value) { $test = $db->setOption($option, $value); if (PEAR::isError($test)) { return $test; } } } return MDB2_OK; } // }}} // {{{ function classExists($classname) /** * Checks if a class exists without triggering __autoload

* * @param string classname * * @return bool true success and false on error * @static * @access public */ function classExists($classname) { if (version_compare(phpversion(), "5.0", ">=")) { return class_exists($classname, false); } return class_exists($classname); } // }}} // {{{ function loadClass($class_name, $debug) /** * Loads a PEAR class. * * @param string classname to load * @param bool if errors should be suppressed * * @return mixed true success or PEAR_Error on failure * * @access public */ function loadClass($class_name, $debug) { if (!MDB2::classExists($class_name)) { $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.ph p'; if ($debug) { $include = include_once($file_name); } else { $include = @include_once($file_name); } if (!$include) { if (!MDB2::fileExists($file_name)) { $msg = "unable to find package '$class_name' file '$file_nam e'"; } else { $msg = "unable to load class '$class_name' from file '$file_ name'"; } $err =& MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg) ; return $err; } } return MDB2_OK; } // }}} // {{{ function &factory($dsn, $options = false) /** * Create a new MDB2 object for the specified database type *

* IMPORTANT: In order for MDB2 to work properly it is necessary that * you make sure that you work with a reference of the original * object instead of a copy (this is a PHP4 quirk). * * For example: * $db =& MDB2::factory($dsn); * ^^ * And not: * $db = MDB2::factory($dsn); * * @param mixed 'data source name', see the MDB2::parseDSN * method for a description of the dsn format. * Can also be specified as an array of the * format returned by MDB2::parseDSN. * @param array An associative array of option names and * their values. * * @return mixed a newly created MDB2 object, or false on error * * @access public */ function &factory($dsn, $options = false) { $dsninfo = MDB2::parseDSN($dsn); if (empty($dsninfo['phptype'])) { $err =& MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'no RDBMS driver specified'); return $err; } $class_name = 'MDB2_Driver_'.$dsninfo['phptype']; $debug = (!empty($options['debug'])); $err = MDB2::loadClass($class_name, $debug); if (PEAR::isError($err)) { return $err; } $db =& new $class_name(); $db->setDSN($dsninfo); $err = MDB2::setOptions($db, $options); if (PEAR::isError($err)) { return $err; } return $db; } // }}} // {{{ function &connect($dsn, $options = false) /** * Create a new MDB2 connection object and connect to the specified * database * * IMPORTANT: In order for MDB2 to work properly it is necessary that * you make sure that you work with a reference of the original * object instead of a copy (this is a PHP4 quirk). * * For example: * $db =& MDB2::connect($dsn);

* ^^ * And not: * $db = MDB2::connect($dsn); * ^^ * * @param mixed 'data source name', see the MDB2::parseDSN * method for a description of the dsn format. * Can also be specified as an array of the * format returned by MDB2::parseDSN. * @param array An associative array of option names and * their values. * * @return mixed a newly created MDB2 connection object, or a MDB2 * error object on error * * @access public * @see MDB2::parseDSN */ function &connect($dsn, $options = false) { $db =& MDB2::factory($dsn, $options); if (PEAR::isError($db)) { return $db; } $err = $db->connect(); if (PEAR::isError($err)) { $dsn = $db->getDSN('string', 'xxx'); $db->disconnect(); $err->addUserInfo($dsn); return $err; } return $db; } // }}} // {{{ function &singleton($dsn = null, $options = false) /** * Returns a MDB2 connection with the requested DSN. * A new MDB2 connection object is only created if no object with the * requested DSN exists yet. * * IMPORTANT: In order for MDB2 to work properly it is necessary that * you make sure that you work with a reference of the original * object instead of a copy (this is a PHP4 quirk). * * For example: * $db =& MDB2::singleton($dsn); * ^^ * And not: * $db = MDB2::singleton($dsn); * ^^ * * @param mixed 'data source name', see the MDB2::parseDSN * method for a description of the dsn format. * Can also be specified as an array of the * format returned by MDB2::parseDSN. * @param array An associative array of option names and

* their values. * * @return mixed a newly created MDB2 connection object, or a MDB2 * error object on error * * @access public * @see MDB2::parseDSN */ function &singleton($dsn = null, $options = false) { if ($dsn) { $dsninfo = MDB2::parseDSN($dsn); $dsninfo = array_merge($GLOBALS['_MDB2_dsninfo_default'], $dsninfo); $keys = array_keys($GLOBALS['_MDB2_databases']); for ($i=0, $j=count($keys); $i<$j; ++$i) { if (isset($GLOBALS['_MDB2_databases'][$keys[$i]])) { $tmp_dsn = $GLOBALS['_MDB2_databases'][$keys[$i]]->getDSN('a rray'); if (count(array_diff_assoc($tmp_dsn, $dsninfo)) == 0) { MDB2::setOptions($GLOBALS['_MDB2_databases'][$keys[$i]], $options); return $GLOBALS['_MDB2_databases'][$keys[$i]]; } } } } elseif (is_array($GLOBALS['_MDB2_databases']) && reset($GLOBALS['_MDB2 _databases'])) { $db =& $GLOBALS['_MDB2_databases'][key($GLOBALS['_MDB2_databases'])] ; return $db; } $db =& MDB2::factory($dsn, $options); return $db; } // }}} // {{{ function loadFile($file) /** * load a file (like 'Date') * * @param string name of the file in the MDB2 directory (without '.php') * * @return string name of the file that was included * * @access public */ function loadFile($file) { $file_name = 'MDB2'.DIRECTORY_SEPARATOR.$file.'.php'; if (!MDB2::fileExists($file_name)) { return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'unable to find: '.$file_name); } if (!include_once($file_name)) { return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'unable to load driver class: '.$file_name); } return $file_name; }

// }}} // {{{ function apiVersion() /** * Return the MDB2 API version * * @return string the MDB2 API version number * * @access public */ function apiVersion() { return '2.4.1'; } // }}} // {{{ function &raiseError($code = null, $mode = null, $options = null, $us erinfo = null) /** * This method is used to communicate an error and invoke error * callbacks etc. Basically a wrapper for PEAR::raiseError * without the message string. * * @param mixed int error code * * @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. * * @return PEAR_Error instance of a PEAR Error object * * @access private * @see PEAR_Error */ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null) { $err =& PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_ Error', true); return $err; } // }}} // {{{ function isError($data, $code = null) /** * Tell whether a value is a MDB2 error. * * @param mixed the value to test * @param int if is an error object, return true

* only if $code is a string and * $db->getMessage() == $code or * $code is an integer and $db->getCode() == $code * * @return bool true if parameter is an error * * @access public */ function isError($data, $code = null) { if (is_a($data, 'MDB2_Error')) { if (is_null($code)) { return true; } elseif (is_string($code)) { return $data->getMessage() === $code; } else { $code = (array)$code; return in_array($data->getCode(), $code); } } return false; } // }}} // {{{ function isConnection($value) /** * Tell whether a value is a MDB2 connection * * @param mixed value to test * * @return bool whether $value is a MDB2 connection * * @access public */ function isConnection($value) { return is_a($value, 'MDB2_Driver_Common'); } // }}} // {{{ function isResult($value) /** * Tell whether a value is a MDB2 result * * @param mixed value to test * * @return bool whether $value is a MDB2 result * * @access public */ function isResult($value) { return is_a($value, 'MDB2_Result'); } // }}} // {{{ function isResultCommon($value)

/** * Tell whether a value is a MDB2 result implementing the common interface * * @param mixed value to test * * @return bool whether $value is a MDB2 result implementing the common interface * * @access public */ function isResultCommon($value) { return is_a($value, 'MDB2_Result_Common'); } // }}} // {{{ function isStatement($value) /** * Tell whether a value is a MDB2 statement interface * * @param mixed value to test * * @return bool whether $value is a MDB2 statement interface * * @access public */ function isStatement($value) { return is_a($value, 'MDB2_Statement'); } // }}} // {{{ function errorMessage($value = null) /** * Return a textual error message for a MDB2 error code * * @param int|array integer error code, null to get the current error code-message map, or an array with a new error code-message map * * @return string error message, or false if the error code was * not recognized * * @access public */ function errorMessage($value = null) { static $errorMessages; if (is_array($value)) { $errorMessages = $value; return MDB2_OK; } if (!isset($errorMessages)) { $errorMessages = array( MDB2_OK MDB2_ERROR

=> 'no error', => 'unknown error',

MDB2_ERROR_ALREADY_EXISTS => MDB2_ERROR_CANNOT_CREATE => MDB2_ERROR_CANNOT_ALTER => MDB2_ERROR_CANNOT_REPLACE => MDB2_ERROR_CANNOT_DELETE => MDB2_ERROR_CANNOT_DROP => MDB2_ERROR_CONSTRAINT => MDB2_ERROR_CONSTRAINT_NOT_NULL=> onstraint', MDB2_ERROR_DIVZERO => MDB2_ERROR_INVALID => MDB2_ERROR_INVALID_DATE => MDB2_ERROR_INVALID_NUMBER => MDB2_ERROR_MISMATCH => MDB2_ERROR_NODBSELECTED => MDB2_ERROR_NOSUCHFIELD => MDB2_ERROR_NOSUCHTABLE => MDB2_ERROR_NOT_CAPABLE => MDB2_ERROR_NOT_FOUND => MDB2_ERROR_NOT_LOCKED => MDB2_ERROR_SYNTAX => MDB2_ERROR_UNSUPPORTED => MDB2_ERROR_VALUE_COUNT_ON_ROW => MDB2_ERROR_INVALID_DSN => MDB2_ERROR_CONNECT_FAILED => MDB2_ERROR_NEED_MORE_DATA => MDB2_ERROR_EXTENSION_NOT_FOUND=> MDB2_ERROR_NOSUCHDB => MDB2_ERROR_ACCESS_VIOLATION => MDB2_ERROR_LOADMODULE => d module', MDB2_ERROR_TRUNCATED MDB2_ERROR_DEADLOCK ); } if (is_null($value)) { return $errorMessages; } if (PEAR::isError($value)) { $value = $value->getCode(); }

'already exists', 'can not create', 'can not alter', 'can not replace', 'can not delete', 'can not drop', 'constraint violation', 'null value violates not-null c 'division by zero', 'invalid', 'invalid date or time', 'invalid number', 'mismatch', 'no database selected', 'no such field', 'no such table', 'MDB2 backend not capable', 'not found', 'not locked', 'syntax error', 'not supported', 'value count on row', 'invalid DSN', 'connect failed', 'insufficient data supplied', 'extension not found', 'no such database', 'insufficient permissions', 'error while including on deman

=> 'truncated', => 'deadlock detected',

return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[MDB2_ERROR]; } // }}} // {{{ function parseDSN($dsn) /** * Parse a data source name. * * Additional keys can be added by appending a URI query string to the * end of the DSN. * * The format of the supplied DSN is in its fullest form: * <code> * phptype(dbsyntax)://username:password@protocol+hostspec/database?option=

8&another=true * </code> * * Most variations are allowed: * <code> * phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0 644 * phptype://username:password@hostspec/database_name * phptype://username:password@hostspec * phptype://username@hostspec * phptype://hostspec/database * phptype://hostspec * phptype(dbsyntax) * phptype * </code> * * @param string Data Source Name to be parsed * * @return array an associative array with the following keys: * + phptype: Database backend used in PHP (mysql, odbc etc.) * + dbsyntax: Database used with regards to SQL syntax etc. * + protocol: Communication protocol to use (tcp, unix etc.) * + hostspec: Host specification (hostname[:port]) * + database: Database to use on the DBMS server * + username: User name for login * + password: Password for login * * @access public * @author Tomas V.V.Cox <cox@idecnet.com> */ function parseDSN($dsn) { $parsed = $GLOBALS['_MDB2_dsninfo_default']; if (is_array($dsn)) { $dsn = array_merge($parsed, $dsn); if (!$dsn['dbsyntax']) { $dsn['dbsyntax'] = $dsn['phptype']; } return $dsn; } // Find phptype and dbsyntax if (($pos = strpos($dsn, '://')) !== false) { $str = substr($dsn, 0, $pos); $dsn = substr($dsn, $pos + 3); } else { $str = $dsn; $dsn = null; } // Get phptype and dbsyntax // $str => phptype(dbsyntax) if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { $parsed['phptype'] = $arr[1]; $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2]; } else { $parsed['phptype'] = $str; $parsed['dbsyntax'] = $str; }

if (!count($dsn)) { return $parsed; } // Get (if found): username and password // $dsn => username:password@protocol+hostspec/database if (($at = strrpos($dsn,'@')) !== false) { $str = substr($dsn, 0, $at); $dsn = substr($dsn, $at + 1); if (($pos = strpos($str, ':')) !== false) { $parsed['username'] = rawurldecode(substr($str, 0, $pos)); $parsed['password'] = rawurldecode(substr($str, $pos + 1)); } else { $parsed['username'] = rawurldecode($str); } } // Find protocol and hostspec // $dsn => proto(proto_opts)/database if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { $proto = $match[1]; $proto_opts = $match[2] ? $match[2] : false; $dsn = $match[3]; // $dsn => protocol+hostspec/database (old format) } else { if (strpos($dsn, '+') !== false) { list($proto, $dsn) = explode('+', $dsn, 2); } if ( strpos($dsn, '//') === 0 && strpos($dsn, '/', 2) !== false && $parsed['phptype'] == 'oci8' ) { //oracle's "Easy Connect" syntax: //"username/password@[//]host[:port][/service_name]" //e.g. "scott/tiger@//mymachine:1521/oracle" $proto_opts = $dsn; $dsn = null; } elseif (strpos($dsn, '/') !== false) { list($proto_opts, $dsn) = explode('/', $dsn, 2); } else { $proto_opts = $dsn; $dsn = null; } } // process the different protocol options $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp'; $proto_opts = rawurldecode($proto_opts); if (strpos($proto_opts, ':') !== false) { list($proto_opts, $parsed['port']) = explode(':', $proto_opts); } if ($parsed['protocol'] == 'tcp') { $parsed['hostspec'] = $proto_opts; } elseif ($parsed['protocol'] == 'unix') { $parsed['socket'] = $proto_opts; }

// Get dabase if any // $dsn => database if ($dsn) { // /database if (($pos = strpos($dsn, '?')) === false) { $parsed['database'] = $dsn; // /database?param1=value1&param2=value2 } else { $parsed['database'] = substr($dsn, 0, $pos); $dsn = substr($dsn, $pos + 1); if (strpos($dsn, '&') !== false) { $opts = explode('&', $dsn); } else { // database?param1=value1 $opts = array($dsn); } foreach ($opts as $opt) { list($key, $value) = explode('=', $opt); if (!isset($parsed[$key])) { // don't allow params overwrite $parsed[$key] = rawurldecode($value); } } } } return $parsed; } // }}} // {{{ function fileExists($file) /** * Checks if a file exists in the include path * * @param string filename * * @return bool true success and false on error * * @access public */ function fileExists($file) { // safe_mode does notwork with is_readable() if (!@ini_get('safe_mode')) { $dirs = explode(PATH_SEPARATOR, ini_get('include_path')); foreach ($dirs as $dir) { if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) { return true; } } } else { $fp = @fopen($file, 'r', true); if (is_resource($fp)) { @fclose($fp); return true; } } return false; } // }}}

} // }}} // {{{ class MDB2_Error extends PEAR_Error /** * MDB2_Error implements a class for reporting portable database error * messages. * * @package MDB2 * @category Database * @author Stig Bakken <ssb@fast.no> */ class MDB2_Error extends PEAR_Error { // {{{ constructor: function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERR OR_RETURN, $level = E_USER_NOTICE, $debuginfo = null) /** * MDB2_Error constructor. * * @param mixed MDB2 error code, or string with error message. * @param int what 'error mode' to operate in * @param int what error level to use for $mode & PEAR_ERROR_TRIGGER * @param smixed additional debug info, such as the last query */ function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, $debuginfo = null) { if (is_null($code)) { $code = MDB2_ERROR; } $this->PEAR_Error('MDB2 Error: '.MDB2::errorMessage($code), $code, $mode, $level, $debuginfo); } // }}} } // }}} // {{{ class MDB2_Driver_Common extends PEAR /** * MDB2_Driver_Common: Base class that is extended by each MDB2 driver * * @package MDB2 * @category Database * @author Lukas Smith <smith@pooteeweet.org> */ class MDB2_Driver_Common extends PEAR { // {{{ Variables (Properties) /** * index of the MDB2 object within the $GLOBALS['_MDB2_databases'] array * @var int * @access public */ var $db_index = 0;

/** * DSN used for the next query * @var array * @access protected */ var $dsn = array(); /** * DSN that was used to create the current connection * @var array * @access protected */ var $connected_dsn = array(); /** * connection resource * @var mixed * @access protected */ var $connection = 0; /** * if the current opened connection is a persistent connection * @var bool * @access protected */ var $opened_persistent; /** * the name of the database for the next query * @var string * @access protected */ var $database_name = ''; /** * the name of the database currently selected * @var string * @access protected */ var $connected_database_name = ''; /** * server version information * @var string * @access protected */ var $connected_server_info = ''; /** * list of all supported features of the given driver * @var array * @access public */ var $supported = array( 'sequences' => false, 'indexes' => false, 'affected_rows' => false, 'summary_functions' => false, 'order_by_text' => false,

'transactions' => false, 'savepoints' => false, 'current_id' => false, 'limit_queries' => false, 'LOBs' => false, 'replace' => false, 'sub_selects' => false, 'auto_increment' => false, 'primary_key' => false, 'result_introspection' => false, 'prepared_statements' => false, 'identifier_quoting' => false, 'pattern_escaping' => false, 'new_link' => false, ); /** * Array of supported options that can be passed to the MDB2 instance. * * The options can be set during object creation, using * MDB2::connect(), MDB2::factory() or MDB2::singleton(). The options can * also be set after the object is created, using MDB2::setOptions() or * MDB2_Driver_Common::setOption(). * The list of available option includes: * <ul> * <li>$options['ssl'] -> boolean: determines if ssl should be used for con nections</li> * <li>$options['field_case'] -> CASE_LOWER|CASE_UPPER: determines what cas e to force on field/table names</li> * <li>$options['disable_query'] -> boolean: determines if queries should b e executed</li> * <li>$options['result_class'] -> string: class used for result sets</li> * <li>$options['buffered_result_class'] -> string: class used for buffered result sets</li> * <li>$options['result_wrap_class'] -> string: class used to wrap result s ets into</li> * <li>$options['result_buffering'] -> boolean should results be buffered o r not?</li> * <li>$options['fetch_class'] -> string: class to use when fetch mode obje ct is used</li> * <li>$options['persistent'] -> boolean: persistent connection?</li> * <li>$options['debug'] -> integer: numeric debug level</li> * <li>$options['debug_handler'] -> string: function/method that captures d ebug messages</li> * <li>$options['debug_expanded_output'] -> bool: BC option to determine if more context information should be send to the debug handler</li> * <li>$options['default_text_field_length'] -> integer: default text field length to use</li> * <li>$options['lob_buffer_length'] -> integer: LOB buffer length</li> * <li>$options['log_line_break'] -> string: line-break format</li> * <li>$options['idxname_format'] -> string: pattern for index name</li> * <li>$options['seqname_format'] -> string: pattern for sequence name</li> * <li>$options['savepoint_format'] -> string: pattern for auto generated s avepoint names</li> * <li>$options['statement_format'] -> string: pattern for prepared stateme nt names</li> * <li>$options['seqcol_name'] -> string: sequence column name</li> * <li>$options['quote_identifier'] -> boolean: if identifier quoting shoul d be done when check_option is used</li> * <li>$options['use_transactions'] -> boolean: if transaction use should b

e enabled</li> * <li>$options['decimal_places'] -> integer: number of decimal places to h andle</li> * <li>$options['portability'] -> integer: portability constant</li> * <li>$options['modules'] -> array: short to long module name mapping for __call()</li> * <li>$options['emulate_prepared'] -> boolean: force prepared statements t o be emulated</li> * <li>$options['datatype_map'] -> array: map user defined datatypes to oth er primitive datatypes</li> * <li>$options['datatype_map_callback'] -> array: callback function/method that should be called</li> * </ul> * * @var array * @access public * @see MDB2::connect() * @see MDB2::factory() * @see MDB2::singleton() * @see MDB2_Driver_Common::setOption() */ var $options = array( 'ssl' => false, 'field_case' => CASE_LOWER, 'disable_query' => false, 'result_class' => 'MDB2_Result_%s', 'buffered_result_class' => 'MDB2_BufferedResult_%s', 'result_wrap_class' => false, 'result_buffering' => true, 'fetch_class' => 'stdClass', 'persistent' => false, 'debug' => 0, 'debug_handler' => 'MDB2_defaultDebugOutput', 'debug_expanded_output' => false, 'default_text_field_length' => 4096, 'lob_buffer_length' => 8192, 'log_line_break' => "\n", 'idxname_format' => '%s_idx', 'seqname_format' => '%s_seq', 'savepoint_format' => 'MDB2_SAVEPOINT_%s', 'statement_format' => 'MDB2_STATEMENT_%1$s_%2$s', 'seqcol_name' => 'sequence', 'quote_identifier' => false, 'use_transactions' => true, 'decimal_places' => 2, 'portability' => MDB2_PORTABILITY_ALL, 'modules' => array( 'ex' => 'Extended', 'dt' => 'Datatype', 'mg' => 'Manager', 'rv' => 'Reverse', 'na' => 'Native', 'fc' => 'Function', ), 'emulate_prepared' => false, 'datatype_map' => array(), 'datatype_map_callback' => array(), 'nativetype_map_callback' => array(), );

/** * string array * @var string * @access protected */ var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => false, 'escape_pattern' => false); /** * identifier quoting * @var array * @access protected */ var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '" '); /** * sql comments * @var array * @access protected */ var $sql_comments = array( array('start' => '--', 'end' => "\n", 'escape' => false), array('start' => '/*', 'end' => '*/', 'escape' => false), ); /** * comparision wildcards * @var array * @access protected */ var $wildcards = array('%', '_'); /** * column alias keyword * @var string * @access protected */ var $as_keyword = ' AS '; /** * warnings * @var array * @access protected */ var $warnings = array(); /** * string with the debugging information * @var string * @access public */ var $debug_output = ''; /** * determine if there is an open transaction * @var bool * @access protected */ var $in_transaction = false;

/** * the smart transaction nesting depth * @var int * @access protected */ var $nested_transaction_counter = null; /** * the first error that occured inside a nested transaction * @var MDB2_Error|bool * @access protected */ var $has_transaction_error = false; /** * result offset used in the next query * @var int * @access protected */ var $offset = 0; /** * result limit used in the next query * @var int * @access protected */ var $limit = 0; /** * Database backend used in PHP (mysql, odbc etc.) * @var string * @access public */ var $phptype; /** * Database used with regards to SQL syntax etc. * @var string * @access public */ var $dbsyntax; /** * the last query sent to the driver * @var string * @access public */ var $last_query; /** * the default fetchmode used * @var int * @access protected */ var $fetchmode = MDB2_FETCHMODE_ORDERED; /** * array of module instances * @var array

* @access protected */ var $modules = array(); /** * determines of the PHP4 destructor emulation has been enabled yet * @var array * @access protected */ var $destructor_registered = true; // }}} // {{{ constructor: function __construct() /** * Constructor */ function __construct() { end($GLOBALS['_MDB2_databases']); $db_index = key($GLOBALS['_MDB2_databases']) + 1; $GLOBALS['_MDB2_databases'][$db_index] = &$this; $this->db_index = $db_index; } // }}} // {{{ function MDB2_Driver_Common() /** * PHP 4 Constructor */ function MDB2_Driver_Common() { $this->destructor_registered = false; $this->__construct(); } // }}} // {{{ destructor: function __destruct() /** * Destructor */ function __destruct() { $this->disconnect(false); } // }}} // {{{ function free() /** * Free the internal references so that the instance can be destroyed * * @return bool true on success, false if result is invalid * * @access public */ function free() {

unset($GLOBALS['_MDB2_databases'][$this->db_index]); unset($this->db_index); return MDB2_OK; } // }}} // {{{ function __toString() /** * String conversation * * @return string representation of the object * * @access public */ function __toString() { $info = get_class($this); $info.= ': (phptype = '.$this->phptype.', dbsyntax = '.$this->dbsyntax.' )'; if ($this->connection) { $info.= ' [connected]'; } return $info; } // }}} // {{{ function errorInfo($error = null) /** * This method is used to collect information about an error * * @param mixed error code or resource * * @return array with MDB2 errorcode, native error code, native message * * @access public */ function errorInfo($error = null) { return array($error, null, null); } // }}} // {{{ function &raiseError($code = null, $mode = null, $options = null, $us erinfo = null) /** * This method is used to 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 string name of the method that triggered the error * * @return PEAR_Error instance of a PEAR Error object * * @access public * @see PEAR_Error */ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null, $method = null) { $userinfo = "[Error message: $userinfo]\n"; // The error is yet a MDB2 error object if (PEAR::isError($code)) { // because we use the static PEAR::raiseError, our global // handler should be used if it is set if (is_null($mode) && !empty($this->_default_error_mode)) { $mode = $this->_default_error_mode; $options = $this->_default_error_options; } if (is_null($userinfo)) { $userinfo = $code->getUserinfo(); } $code = $code->getCode(); } elseif ($code == MDB2_ERROR_NOT_FOUND) { // extension not loaded: don't call $this->errorInfo() or the script // will die } elseif (isset($this->connection)) { if (!empty($this->last_query)) { $userinfo.= "[Last executed query: {$this->last_query}]\n"; } $native_errno = $native_msg = null; list($code, $native_errno, $native_msg) = $this->errorInfo($code); if (!is_null($native_errno) && $native_errno !== '') { $userinfo.= "[Native code: $native_errno]\n"; } if (!is_null($native_msg) && $native_msg !== '') { $userinfo.= "[Native message: ". strip_tags($native_msg) ."]\n"; } if (!is_null($method)) { $userinfo = $method.': '.$userinfo; } } $err =& PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_ Error', true); if ($err->getMode() !== PEAR_ERROR_RETURN && isset($this->nested_transaction_counter) && !$this->has_transacti on_error) { $this->has_transaction_error =& $err; } return $err; } // }}} // {{{ function resetWarnings() /**

* reset the warning array * * @return void * * @access public */ function resetWarnings() { $this->warnings = array(); } // }}} // {{{ function getWarnings() /** * Get all warnings in reverse order. * This means that the last warning is the first element in the array * * @return array with warnings * * @access public * @see resetWarnings() */ function getWarnings() { return array_reverse($this->warnings); } // }}} // {{{ function setFetchMode($fetchmode, $object_class = 'stdClass') /** * Sets which fetch mode should be used by default on queries * on this connection * * @param int MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC * or MDB2_FETCHMODE_OBJECT * @param string the class name of the object to be returned * by the fetch methods when the * MDB2_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 possibility to use * and extend the 'MDB2_row' class. * * @return mixed MDB2_OK or MDB2 Error Object * * @access public * @see MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC, MDB2_FETCHMODE_OBJ ECT */ function setFetchMode($fetchmode, $object_class = 'stdClass') { switch ($fetchmode) { case MDB2_FETCHMODE_OBJECT: $this->options['fetch_class'] = $object_class; case MDB2_FETCHMODE_ORDERED: case MDB2_FETCHMODE_ASSOC: $this->fetchmode = $fetchmode; break;

default: return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'invalid fetchmode mode', __FUNCTION__); } return MDB2_OK; } // }}} // {{{ function setOption($option, $value) /** * set the option for the db class * * @param string option name * @param mixed value for the option * * @return mixed MDB2_OK or MDB2 Error Object * * @access public */ function setOption($option, $value) { if (array_key_exists($option, $this->options)) { $this->options[$option] = $value; return MDB2_OK; } return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, "unknown option $option", __FUNCTION__); } // }}} // {{{ function getOption($option) /** * Returns the value of an option * * @param string option name * * @return mixed the option value or error object * * @access public */ function getOption($option) { if (array_key_exists($option, $this->options)) { return $this->options[$option]; } return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, "unknown option $option", __FUNCTION__); } // }}} // {{{ function debug($message, $scope = '', $is_manip = null) /** * set a debug message * * @param string message that should be appended to the debug variable * @param string usually the method name that triggered the debug call:

* for example 'query', 'prepare', 'execute', 'parameters', * 'beginTransaction', 'commit', 'rollback' * @param array contains context information about the debug() call * common keys are: is_manip, time, result etc. * * @return void * * @access public */ function debug($message, $scope = '', $context = array()) { if ($this->options['debug'] && $this->options['debug_handler']) { if (!$this->options['debug_expanded_output']) { if (!empty($context['when']) && $context['when'] !== 'pre') { return null; } $context = empty($context['is_manip']) ? false : $context['is_ma nip']; } return call_user_func_array($this->options['debug_handler'], array(& $this, $scope, $message, $context)); } return null; } // }}} // {{{ function getDebugOutput() /** * output debug info * * @return string content of the debug_output class variable * * @access public */ function getDebugOutput() { return $this->debug_output; } // }}} // {{{ function escape($text) /** * Quotes a string so it can be safely used in a query. It will quote * the text so it can safely be used within a query. * * @param string the input string to quote * @param bool escape wildcards * * @return string quoted string * * @access public */ function escape($text, $escape_wildcards = false) { if ($escape_wildcards) { $text = $this->escapePattern($text); }

$text = str_replace($this->string_quoting['end'], $this->string_quoting[ 'escape'] . $this->string_quoting['end'], $text); return $text; } // }}} // {{{ function escapePattern($text) /** * Quotes pattern (% and _) characters in a string) * * EXPERIMENTAL * * WARNING: this function is experimental and may change signature at * any time until labelled as non-experimental * * @param string the input string to quote * * @return string quoted string * * @access public */ function escapePattern($text) { if ($this->string_quoting['escape_pattern']) { $text = str_replace($this->string_quoting['escape_pattern'], $this-> string_quoting['escape_pattern'] . $this->string_quoting['escape_pattern'], $tex t); foreach ($this->wildcards as $wildcard) { $text = str_replace($wildcard, $this->string_quoting['escape_pat tern'] . $wildcard, $text); } } return $text; } // }}} // {{{ function quoteIdentifier($str, $check_option = false) /** * 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

* + pgsql * + sqlite * * InterBase doesn't seem to be able to use delimited identifiers * via PHP 4. They work fine under PHP 5. * * @param string identifier name to be quoted * @param bool check the 'quote_identifier' option * * @return string quoted identifier string * * @access public */ function quoteIdentifier($str, $check_option = false) { if ($check_option && !$this->options['quote_identifier']) { return $str; } $str = str_replace($this->identifier_quoting['end'], $this->identifier_q uoting['escape'] . $this->identifier_quoting['end'], $str); return $this->identifier_quoting['start'] . $str . $this->identifier_quo ting['end']; } // }}} // {{{ function getAsKeyword() /** * Gets the string to alias column * * @return string to use when aliasing a column */ function getAsKeyword() { return $this->as_keyword; } // }}} // {{{ function getConnection() /** * Returns a native connection * * @return mixed a valid MDB2 connection object, * or a MDB2 error object on error * * @access public */ function getConnection() { $result = $this->connect(); if (PEAR::isError($result)) { return $result; } return $this->connection; } // }}} // {{{ function _fixResultArrayValues(&$row, $mode)

/** * Do all necessary conversions on result arrays to fix DBMS quirks * * @param array the array to be fixed (passed by reference) * @param array bit-wise addition of the required portability modes * * @return void * * @access protected */ function _fixResultArrayValues(&$row, $mode) { switch ($mode) { case MDB2_PORTABILITY_EMPTY_TO_NULL: foreach ($row as $key => $value) { if ($value === '') { $row[$key] = null; } } break; case MDB2_PORTABILITY_RTRIM: foreach ($row as $key => $value) { if (is_string($value)) { $row[$key] = rtrim($value); } } break; case MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES: $tmp_row = array(); foreach ($row as $key => $value) { $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $v alue; } $row = $tmp_row; break; case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL): foreach ($row as $key => $value) { if ($value === '') { $row[$key] = null; } elseif (is_string($value)) { $row[$key] = rtrim($value); } } break; case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES): $tmp_row = array(); foreach ($row as $key => $value) { if (is_string($value)) { $value = rtrim($value); } $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $v alue; } $row = $tmp_row; break; case (MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_ NAMES): $tmp_row = array(); foreach ($row as $key => $value) { if ($value === '') {

$value = null; } $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $v alue; } $row = $tmp_row; break; case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_POR TABILITY_FIX_ASSOC_FIELD_NAMES): $tmp_row = array(); foreach ($row as $key => $value) { if ($value === '') { $value = null; } elseif (is_string($value)) { $value = rtrim($value); } $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $v alue; } $row = $tmp_row; break; } } // }}} // {{{ function &loadModule($module, $property = null, $phptype_specific = n ull) /** * loads a module * * @param string name of the module that should be loaded * (only used for error messages) * @param string name of the property into which the class will be loaded * @param bool if the class to load for the module is specific to the * phptype * * @return object on success a reference to the given module is returned * and on failure a PEAR error * * @access public */ function &loadModule($module, $property = null, $phptype_specific = null) { if (!$property) { $property = strtolower($module); } if (!isset($this->{$property})) { $version = $phptype_specific; if ($phptype_specific !== false) { $version = true; $class_name = 'MDB2_Driver_'.$module.'_'.$this->phptype; $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name). '.php'; } if ($phptype_specific === false || (!MDB2::classExists($class_name) && !MDB2::fileExists($file_n ame)) ) {

$version = false; $class_name = 'MDB2_'.$module; $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name). '.php'; } $err = MDB2::loadClass($class_name, $this->getOption('debug')); if (PEAR::isError($err)) { return $err; } // load modul in a specific version if ($version) { if (method_exists($class_name, 'getClassName')) { $class_name_new = call_user_func(array($class_name, 'getClas sName'), $this->db_index); if ($class_name != $class_name_new) { $class_name = $class_name_new; $err = MDB2::loadClass($class_name, $this->getOption('de bug')); if (PEAR::isError($err)) { return $err; } } } } if (!MDB2::classExists($class_name)) { $err =& $this->raiseError(MDB2_ERROR_LOADMODULE, null, null, "unable to load module '$module' into property '$property'", __FUNCTION__); return $err; } $this->{$property} =& new $class_name($this->db_index); $this->modules[$module] =& $this->{$property}; if ($version) { // this will be used in the connect method to determine if the m odule // needs to be loaded with a different version if the server // version changed in between connects $this->loaded_version_modules[] = $property; } } return $this->{$property}; } // }}} // {{{ function __call($method, $params) /** * Calls a module method using the __call magic method * * @param string Method name. * @param array Arguments. * * @return mixed Returned value. */ function __call($method, $params) {

$module = null; if (preg_match('/^([a-z]+)([A-Z])(.*)$/', $method, $match) && isset($this->options['modules'][$match[1]]) ) { $module = $this->options['modules'][$match[1]]; $method = strtolower($match[2]).$match[3]; if (!isset($this->modules[$module]) || !is_object($this->modules[$mo dule])) { $result =& $this->loadModule($module); if (PEAR::isError($result)) { return $result; } } } else { foreach ($this->modules as $key => $foo) { if (is_object($this->modules[$key]) && method_exists($this->modules[$key], $method) ) { $module = $key; break; } } } if (!is_null($module)) { return call_user_func_array(array(&$this->modules[$module], $method) , $params); } trigger_error(sprintf('Call to undefined function: %s::%s().', get_class ($this), $method), E_USER_ERROR); } // }}} // {{{ function beginTransaction($savepoint = null) /** * Start a transaction or set a savepoint. * * @param string name of a savepoint to set * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function beginTransaction($savepoint = null) { $this->debug('Starting transaction', __FUNCTION__, array('is_manip' => t rue, 'savepoint' => $savepoint)); return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'transactions are not supported', __FUNCTION__); } // }}} // {{{ function commit($savepoint = null) /** * Commit the database changes done during a transaction that is in * progress or release a savepoint. This function may only be called when * auto-committing is disabled, otherwise it will fail. Therefore, a new * transaction is implicitly started after committing the pending changes. * * @param string name of a savepoint to release

* @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function commit($savepoint = null) { $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is _manip' => true, 'savepoint' => $savepoint)); return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'commiting transactions is not supported', __FUNCTION__); } // }}} // {{{ function rollback($savepoint = null) /** * Cancel any database changes done during a transaction or since a specific * savepoint that is in progress. This function may only be called when * auto-committing is disabled, otherwise it will fail. Therefore, a new * transaction is implicitly started after canceling the pending changes. * * @param string name of a savepoint to rollback to * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function rollback($savepoint = null) { $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array(' is_manip' => true, 'savepoint' => $savepoint)); return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'rolling back transactions is not supported', __FUNCTION__); } // }}} // {{{ function inTransaction($ignore_nested = false) /** * If a transaction is currently open. * * @param bool if the nested transaction count should be ignored * @return int|bool - an integer with the nesting depth is returned if a * nested transaction is open * - true is returned for a normal open transaction * - false is returned if no transaction is open * * @access public */ function inTransaction($ignore_nested = false) { if (!$ignore_nested && isset($this->nested_transaction_counter)) { return $this->nested_transaction_counter; } return $this->in_transaction; } // }}} // {{{ function setTransactionIsolation($isolation) /**

* Set the transacton isolation level. * * @param string standard isolation level * READ UNCOMMITTED (allows dirty reads) * READ COMMITTED (prevents dirty reads) * REPEATABLE READ (prevents nonrepeatable reads) * SERIALIZABLE (prevents phantom reads) * @param array some transaction options: * 'wait' => 'WAIT' | 'NO WAIT' * 'rw' => 'READ WRITE' | 'READ ONLY' * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public * @since 2.1.1 */ function setTransactionIsolation($isolation, $options = array()) { $this->debug('Setting transaction isolation level', __FUNCTION__, array( 'is_manip' => true)); return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'isolation level setting is not supported', __FUNCTION__); } // }}} // {{{ function beginNestedTransaction($savepoint = false) /** * Start a nested transaction. * * EXPERIMENTAL * * WARNING: this function is experimental and may change signature at * any time until labelled as non-experimental * * @return mixed MDB2_OK on success/savepoint name, a MDB2 error on failu re * * @access public * @since 2.1.1 */ function beginNestedTransaction() { if ($this->in_transaction) { ++$this->nested_transaction_counter; $savepoint = sprintf($this->options['savepoint_format'], $this->nest ed_transaction_counter); if ($this->supports('savepoints') && $savepoint) { return $this->beginTransaction($savepoint); } return MDB2_OK; } $this->has_transaction_error = false; $result = $this->beginTransaction(); $this->nested_transaction_counter = 1; return $result; } // }}} // {{{ function completeNestedTransaction($force_rollback = false, $release = false)

/** * Finish a nested transaction by rolling back if an error occured or * committing otherwise. * * EXPERIMENTAL * * WARNING: this function is experimental and may change signature at * any time until labelled as non-experimental * * @param bool if the transaction should be rolled back regardless * even if no error was set within the nested transaction * @return mixed MDB_OK on commit/counter decrementing, false on rollback * and a MDB2 error on failure * * @access public * @since 2.1.1 */ function completeNestedTransaction($force_rollback = false) { if ($this->nested_transaction_counter > 1) { $savepoint = sprintf($this->options['savepoint_format'], $this->nest ed_transaction_counter); if ($this->supports('savepoints') && $savepoint) { if ($force_rollback || $this->has_transaction_error) { $result = $this->rollback($savepoint); if (!PEAR::isError($result)) { $result = false; $this->has_transaction_error = false; } } else { $result = $this->commit($savepoint); } } else { $result = MDB2_OK; } --$this->nested_transaction_counter; return $result; } $this->nested_transaction_counter = null; $result = MDB2_OK; // transaction has not yet been rolled back if ($this->in_transaction) { if ($force_rollback || $this->has_transaction_error) { $result = $this->rollback(); if (!PEAR::isError($result)) { $result = false; } } else { $result = $this->commit(); } } $this->has_transaction_error = false; return $result; } // }}} // {{{ function failNestedTransaction($error = null, $immediately = false)

/** * Force setting nested transaction to failed. * * EXPERIMENTAL * * WARNING: this function is experimental and may change signature at * any time until labelled as non-experimental * * @param mixed value to return in getNestededTransactionError() * @param bool if the transaction should be rolled back immediately * @return bool MDB2_OK * * @access public * @since 2.1.1 */ function failNestedTransaction($error = null, $immediately = false) { if (is_null($error)) { $error = $this->has_transaction_error ? $this->has_transaction_error : true; } elseif (!$error) { $error = true; } $this->has_transaction_error = $error; if (!$immediately) { return MDB2_OK; } return $this->rollback(); } // }}} // {{{ function getNestedTransactionError() /** * The first error that occured since the transaction start. * * EXPERIMENTAL * * WARNING: this function is experimental and may change signature at * any time until labelled as non-experimental * * @return MDB2_Error|bool MDB2 error object if an error occured or fal se. * * @access public * @since 2.1.1 */ function getNestedTransactionError() { return $this->has_transaction_error; } // }}} // {{{ connect() /** * Connect to the database * * @return true on success, MDB2 Error Object on failure

*/ function connect() { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ setCharset($charset, $connection = null) /** * Set the charset on the current connection * * @param string charset * @param resource connection handle * * @return true on success, MDB2 Error Object on failure */ function setCharset($charset, $connection = null) { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function disconnect($force = true) /** * Log out and disconnect from the database. * * @param bool if the disconnect should be forced even if the * connection is opened persistently * * @return mixed true on success, false if not connected and error * object on error * * @access public */ function disconnect($force = true) { $this->connection = 0; $this->connected_dsn = array(); $this->connected_database_name = ''; $this->opened_persistent = null; $this->connected_server_info = ''; $this->in_transaction = null; $this->nested_transaction_counter = null; return MDB2_OK; } // }}} // {{{ function setDatabase($name) /** * Select a different database * * @param string name of the database that should be selected * * @return string name of the database previously connected to *

* @access public */ function setDatabase($name) { $previous_database_name = (isset($this->database_name)) ? $this->databas e_name : ''; $this->database_name = $name; $this->disconnect(false); return $previous_database_name; } // }}} // {{{ function getDatabase() /** * Get the current database * * @return string name of the database * * @access public */ function getDatabase() { return $this->database_name; } // }}} // {{{ function setDSN($dsn) /** * set the DSN * * @param mixed DSN string or array * * @return MDB2_OK * * @access public */ function setDSN($dsn) { $dsn_default = $GLOBALS['_MDB2_dsninfo_default']; $dsn = MDB2::parseDSN($dsn); if (array_key_exists('database', $dsn)) { $this->database_name = $dsn['database']; unset($dsn['database']); } $this->dsn = array_merge($dsn_default, $dsn); return $this->disconnect(false); } // }}} // {{{ function getDSN($type = 'string', $hidepw = false) /** * return the DSN as a string * * @param string format to return ("array", "string") * @param string string to hide the password with * * @return mixed DSN in the chosen type

* * @access public */ function getDSN($type = 'string', $hidepw = false) { $dsn = array_merge($GLOBALS['_MDB2_dsninfo_default'], $this->dsn); $dsn['phptype'] = $this->phptype; $dsn['database'] = $this->database_name; if ($hidepw) { $dsn['password'] = $hidepw; } switch ($type) { // expand to include all possible options case 'string': $dsn = $dsn['phptype']. ($dsn['dbsyntax'] ? ('('.$dsn['dbsyntax'].')') : ''). '://'.$dsn['username'].':'. $dsn['password'].'@'.$dsn['hostspec']. ($dsn['port'] ? (':'.$dsn['port']) : ''). '/'.$dsn['database']; break; case 'array': default: break; } return $dsn; } // }}} // {{{ function &standaloneQuery($query, $types = null, $is_manip = false) /** * execute a query as database administrator * * @param string the SQL query * @param mixed array that contains the types of the columns in * the result set * @param bool if the query is a manipulation query * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function &standaloneQuery($query, $types = null, $is_manip = false) { $offset = $this->offset; $limit = $this->limit; $this->offset = $this->limit = 0; $query = $this->_modifyQuery($query, $is_manip, $limit, $offset); $connection = $this->getConnection(); if (PEAR::isError($connection)) { return $connection; } $result =& $this->_doQuery($query, $is_manip, $connection, false); if (PEAR::isError($result)) { return $result; }

if ($is_manip) { $affected_rows = $this->_affectedRows($connection, $result); return $affected_rows; } $result =& $this->_wrapResult($result, $types, true, false, $limit, $off set); return $result; } // }}} // {{{ function _modifyQuery($query, $is_manip, $limit, $offset) /** * Changes a query string for various DBMS specific reasons * * @param string query to modify * @param bool if it is a DML query * @param int limit the number of rows * @param int start reading from given offset * * @return string modified query * * @access protected */ function _modifyQuery($query, $is_manip, $limit, $offset) { return $query; } // }}} // {{{ function &_doQuery($query, $is_manip = false, $connection = null, $da tabase_name = null) /** * Execute a query * @param string query * @param bool if the query is a manipulation query * @param resource connection handle * @param string database name * * @return result or error object * * @access protected */ function &_doQuery($query, $is_manip = false, $connection = null, $database_ name = null) { $this->last_query = $query; $result = $this->debug($query, 'query', array('is_manip' => $is_manip, ' when' => 'pre')); if ($result) { if (PEAR::isError($result)) { return $result; } $query = $result; } $err =& $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); return $err; }

// }}} // {{{ function _affectedRows($connection, $result = null) /** * Returns the number of rows affected * * @param resource result handle * @param resource connection handle * * @return mixed MDB2 Error Object or the number of rows affected * * @access private */ function _affectedRows($connection, $result = null) { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function &exec($query) /** * Execute a manipulation query to the database and return the number of aff ected rows * * @param string the SQL query * * @return mixed number of affected rows on success, a MDB2 error on fail ure * * @access public */ function &exec($query) { $offset = $this->offset; $limit = $this->limit; $this->offset = $this->limit = 0; $query = $this->_modifyQuery($query, true, $limit, $offset); $connection = $this->getConnection(); if (PEAR::isError($connection)) { return $connection; } $result =& $this->_doQuery($query, true, $connection, $this->database_na me); if (PEAR::isError($result)) { return $result; } $affectedRows = $this->_affectedRows($connection, $result); return $affectedRows; } // }}} // {{{ function &query($query, $types = null, $result_class = true, $result_ wrap_class = false)

/** * Send a query to the database and return any results * * @param string the SQL query * @param mixed array that contains the types of the columns in * the result set * @param mixed string which specifies which result class to use * @param mixed string which specifies which class to wrap results in * * @return mixed an MDB2_Result handle on success, a MDB2 error on failure * * @access public */ function &query($query, $types = null, $result_class = true, $result_wrap_cl ass = false) { $offset = $this->offset; $limit = $this->limit; $this->offset = $this->limit = 0; $query = $this->_modifyQuery($query, false, $limit, $offset); $connection = $this->getConnection(); if (PEAR::isError($connection)) { return $connection; } $result =& $this->_doQuery($query, false, $connection, $this->database_n ame); if (PEAR::isError($result)) { return $result; } $result =& $this->_wrapResult($result, $types, $result_class, $result_wr ap_class, $limit, $offset); return $result; } // }}} // {{{ function &_wrapResult($result, $types = array(), $result_class = true , $result_wrap_class = false, $limit = null, $offset = null) /** * wrap a result set into the correct class * * @param resource result handle * @param mixed array that contains the types of the columns in * the result set * @param mixed string which specifies which result class to use * @param mixed string which specifies which class to wrap results in * @param string number of rows to select * @param string first row to select * * @return mixed an MDB2_Result, a MDB2 error on failure * * @access protected */ function &_wrapResult($result, $types = array(), $result_class = true, $result_wrap_class = false, $limit = null, $offset = null) { if ($types === true) {

if ($this->supports('result_introspection')) { $this->loadModule('Reverse', null, true); $tableInfo = $this->reverse->tableInfo($result); if (PEAR::isError($tableInfo)) { return $tableInfo; } $types = array(); foreach ($tableInfo as $field) { $types[] = $field['mdb2type']; } } else { $types = null; } } if ($result_class === true) { $result_class = $this->options['result_buffering'] ? $this->options['buffered_result_class'] : $this->options['resu lt_class']; } if ($result_class) { $class_name = sprintf($result_class, $this->phptype); if (!MDB2::classExists($class_name)) { $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'result class does not exist '.$class_name, __FUNCTION__); return $err; } $result =& new $class_name($this, $result, $limit, $offset); if (!MDB2::isResultCommon($result)) { $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'result class is not extended from MDB2_Result_Common', __FU NCTION__); return $err; } if (!empty($types)) { $err = $result->setResultTypes($types); if (PEAR::isError($err)) { $result->free(); return $err; } } } if ($result_wrap_class === true) { $result_wrap_class = $this->options['result_wrap_class']; } if ($result_wrap_class) { if (!MDB2::classExists($result_wrap_class)) { $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'result wrap class does not exist '.$result_wrap_class, __FU NCTION__); return $err; } $result =& new $result_wrap_class($result, $this->fetchmode); } return $result; } // }}} // {{{ function getServerVersion($native = false)

/** * return version information about the server * * @param bool determines if the raw version string should be returned * * @return mixed array with version information or row string * * @access public */ function getServerVersion($native = false) { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function setLimit($limit, $offset = null) /** * set the range of the next query * * @param string number of rows to select * @param string first row to select * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function setLimit($limit, $offset = null) { if (!$this->supports('limit_queries')) { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'limit is not supported by this driver', __FUNCTION__); } $limit = (int)$limit; if ($limit < 0) { return $this->raiseError(MDB2_ERROR_SYNTAX, null, null, 'it was not specified a valid selected range row limit', __FUNCT ION__); } $this->limit = $limit; if (!is_null($offset)) { $offset = (int)$offset; if ($offset < 0) { return $this->raiseError(MDB2_ERROR_SYNTAX, null, null, 'it was not specified a valid first selected range row', __F UNCTION__); } $this->offset = $offset; } return MDB2_OK; } // }}} // {{{ function subSelect($query, $type = false) /** * simple subselect emulation: leaves the query untouched for all RDBMS * that support subselects

* * @param string the SQL query for the subselect that may only * return a column * @param string determines type of the field * * @return string the query * * @access public */ function subSelect($query, $type = false) { if ($this->supports('sub_selects') === true) { return $query; } if (!$this->supports('sub_selects')) { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } $col = $this->queryCol($query, $type); if (PEAR::isError($col)) { return $col; } if (!is_array($col) || count($col) == 0) { return 'NULL'; } if ($type) { $this->loadModule('Datatype', null, true); return $this->datatype->implodeArray($col, $type); } return implode(', ', $col); } // }}} // {{{ function replace($table, $fields) /** * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT * query, except that if there is already a row in the table with the same * key field values, the REPLACE query just updates its values instead of * inserting a new row. * * The REPLACE type of query does not make part of the SQL standards. Since * practically only MySQL and SQLite implement it natively, this type of * query isemulated through this method for other DBMS using standard types * of queries inside a transaction to assure the atomicity of the operation. * * @param string name of the table on which the REPLACE query will * be executed. * @param array associative array that describes the fields and the * values that will be inserted or updated in the specified table. The * indexes of the array are the names of all the fields of the table. * The values of the array are also associative arrays that describe * the values and other properties of the table fields. * * Here follows a list of field properties that need to be specified: * * value * Value to be assigned to the specified field. This value may be

* of specified in database independent type format as this * function can perform the necessary datatype conversions. * * Default: this property is required unless the Null property is * set to 1. * * type * Name of the type of the field. Currently, all types MDB2 * are supported except for clob and blob. * * Default: no type conversion * * null * bool property that indicates that the value for this field * should be set to null. * * The default value for fields missing in INSERT queries may be * specified the definition of a table. Often, the default value * is already null, but since the REPLACE may be emulated using * an UPDATE query, make sure that all fields of the table are * listed in this function argument array. * * Default: 0 * * key * bool property that indicates that this field should be * handled as a primary key or at least as part of the compound * unique index of the table that will determine the row that will * updated if it exists or inserted a new row otherwise. * * This function will fail if no key field is specified or if the * value of a key field is set to null because fields that are * part of unique index they may not be null. * * Default: 0 * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function replace($table, $fields) { if (!$this->supports('replace')) { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'replace query is not supported', __FUNCTION__); } $count = count($fields); $condition = $values = array(); for ($colnum = 0, reset($fields); $colnum < $count; next($fields), $coln um++) { $name = key($fields); if (isset($fields[$name]['null']) && $fields[$name]['null']) { $value = 'NULL'; } else { $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null; $value = $this->quote($fields[$name]['value'], $type); } $values[$name] = $value; if (isset($fields[$name]['key']) && $fields[$name]['key']) {

if ($value === 'NULL') { return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, nu ll, 'key value '.$name.' may not be NULL', __FUNCTION__); } $condition[] = $name . '=' . $value; } } if (empty($condition)) { return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, 'not specified which fields are keys', __FUNCTION__); } $result = null; $in_transaction = $this->in_transaction; if (!$in_transaction && PEAR::isError($result = $this->beginTransaction( ))) { return $result; } $connection = $this->getConnection(); if (PEAR::isError($connection)) { return $connection; } $condition = ' WHERE '.implode(' AND ', $condition); $query = "DELETE FROM $table$condition"; $result =& $this->_doQuery($query, true, $connection); if (!PEAR::isError($result)) { $affected_rows = $this->_affectedRows($connection, $result); $insert = implode(', ', array_keys($values)); $values = implode(', ', $values); $query = "INSERT INTO $table ($insert) VALUES ($values)"; $result =& $this->_doQuery($query, true, $connection); if (!PEAR::isError($result)) { $affected_rows += $this->_affectedRows($connection, $result);; } } if (!$in_transaction) { if (PEAR::isError($result)) { $this->rollback(); } else { $result = $this->commit(); } } if (PEAR::isError($result)) { return $result; } return $affected_rows; } // }}} // {{{ function &prepare($query, $types = null, $result_types = null, $lobs = array()) /** * Prepares a query for multiple execution with execute().

* With some database backends, this is emulated. * prepare() requires a generic query as string like * 'INSERT INTO numbers VALUES(?,?)' or * 'INSERT INTO numbers VALUES(:foo,:bar)'. * The ? and :[a-zA-Z] and are placeholders which can be set using * bindParam() and the query can be send off using the execute() method. * * @param string the query to prepare * @param mixed array that contains the types of the placeholders * @param mixed array that contains the types of the columns in * the result set or MDB2_PREPARE_RESULT, if set to * MDB2_PREPARE_MANIP the query is handled as a manip ulation query * @param mixed key (field) value (parameter) pair for all lob placehold ers * * @return mixed resource handle for the prepared query on success, * a MDB2 error on failure * * @access public * @see bindParam, execute */ function &prepare($query, $types = null, $result_types = null, $lobs = array ()) { $is_manip = ($result_types === MDB2_PREPARE_MANIP); $offset = $this->offset; $limit = $this->limit; $this->offset = $this->limit = 0; $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_man ip, 'when' => 'pre')); if ($result) { if (PEAR::isError($result)) { return $result; } $query = $result; } $placeholder_type_guess = $placeholder_type = null; $question = '?'; $colon = ':'; $positions = array(); $position = 0; $ignores = $this->sql_comments; $ignores[] = $this->string_quoting; $ignores[] = $this->identifier_quoting; while ($position < strlen($query)) { $q_position = strpos($query, $question, $position); $c_position = strpos($query, $colon, $position); if ($q_position && $c_position) { $p_position = min($q_position, $c_position); } elseif ($q_position) { $p_position = $q_position; } elseif ($c_position) { $p_position = $c_position; } else { break; } if (is_null($placeholder_type)) { $placeholder_type_guess = $query[$p_position]; }

$new_pos = $this->_skipDelimitedStrings($query, $position, $p_positi on); if (PEAR::isError($new_pos)) { return $new_pos; } if ($new_pos != $position) { $position = $new_pos; continue; //evaluate again starting from the new position } if ($query[$position] == $placeholder_type_guess) { if (is_null($placeholder_type)) { $placeholder_type = $query[$p_position]; $question = $colon = $placeholder_type; if (!empty($types) && is_array($types)) { if ($placeholder_type == ':') { if (is_int(key($types))) { $types_tmp = $types; $types = array(); $count = -1; } } else { $types = array_values($types); } } } if ($placeholder_type == ':') { $parameter = preg_replace('/^.{'.($position+1).'}([a-z0-9_]+ ).*$/si', '\\1', $query); if ($parameter === '') { $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null, 'named parameter with an empty name', __FUNCTION__); return $err; } $positions[$p_position] = $parameter; $query = substr_replace($query, '?', $position, strlen($para meter)+1); // use parameter name in type array if (isset($count) && isset($types_tmp[++$count])) { $types[$parameter] = $types_tmp[$count]; } } else { $positions[$p_position] = count($positions); } $position = $p_position + 1; } else { $position = $p_position; } } $class_name = 'MDB2_Statement_'.$this->phptype; $statement = null; $obj =& new $class_name($this, $statement, $positions, $query, $types, $ result_types, $is_manip, $limit, $offset); $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj)); return $obj; } // }}}

// {{{ function _skipDelimitedStrings($query, $position, $p_position) /** * Utility method, used by prepare() to avoid replacing placeholders within delimited strings. * Check if the placeholder is contained within a delimited string. * If so, skip it and advance the position, otherwise return the current pos ition, * which is valid * * @param string $query * @param integer $position current string cursor position * @param integer $p_position placeholder position * * @return mixed integer $new_position on success * MDB2_Error on failure * * @access protected */ function _skipDelimitedStrings($query, $position, $p_position) { $ignores = $this->sql_comments; $ignores[] = $this->string_quoting; $ignores[] = $this->identifier_quoting; foreach ($ignores as $ignore) { if (!empty($ignore['start'])) { if (is_int($start_quote = strpos($query, $ignore['start'], $posi tion)) && $start_quote < $p_position) { $end_quote = $start_quote; do { if (!is_int($end_quote = strpos($query, $ignore['end'], $end_quote + 1))) { if ($ignore['end'] === "\n") { $end_quote = strlen($query) - 1; } else { $err =& $this->raiseError(MDB2_ERROR_SYNTAX, nul l, null, 'query with an unterminated text string spec ified', __FUNCTION__); return $err; } } } while ($ignore['escape'] && $query[($end_quote - 1)] == $i gnore['escape']); $position = $end_quote + 1; return $position; } } } return $position; } // }}} // {{{ function quote($value, $type = null, $quote = true) /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. *

* @param string text string value that is intended to be converted. * @param string type to which the value should be converted to * @param bool quote * @param bool escape wildcards * * @return string text string that represents the given argument value in * a DBMS specific format. * * @access public */ function quote($value, $type = null, $quote = true, $escape_wildcards = fals e) { $result = $this->loadModule('Datatype', null, true); if (PEAR::isError($result)) { return $result; } return $this->datatype->quote($value, $type, $quote, $escape_wildcards); } // }}} // {{{ function getDeclaration($type, $name, $field) /** * Obtain DBMS specific SQL code portion needed to declare * of the given type * * @param string type to which the value should be converted to * @param string name the field to be declared. * @param string definition of the field * * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * * @access public */ function getDeclaration($type, $name, $field) { $result = $this->loadModule('Datatype', null, true); if (PEAR::isError($result)) { return $result; } return $this->datatype->getDeclaration($type, $name, $field); } // }}} // {{{ function compareDefinition($current, $previous) /** * Obtain an array of changes that may need to applied * * @param array new definition * @param array old definition * * @return array containing all changes that will need to be applied * * @access public */ function compareDefinition($current, $previous)

{ $result = $this->loadModule('Datatype', null, true); if (PEAR::isError($result)) { return $result; } return $this->datatype->compareDefinition($current, $previous); } // }}} // {{{ function supports($feature) /** * Tell whether a DB implementation or its backend extension * supports a given feature. * * @param string name of the feature (see the MDB2 class doc) * * @return bool|string if this DB implementation supports a given feature * false means no, true means native, * 'emulated' means emulated * * @access public */ function supports($feature) { if (array_key_exists($feature, $this->supported)) { return $this->supported[$feature]; } return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, "unknown support feature $feature", __FUNCTION__); } // }}} // {{{ function getSequenceName($sqn) /** * adds sequence name formatting to a sequence name * * @param string name of the sequence * * @return string formatted sequence name * * @access public */ function getSequenceName($sqn) { return sprintf($this->options['seqname_format'], preg_replace('/[^a-z0-9_\$.]/i', '_', $sqn)); } // }}} // {{{ function getIndexName($idx) /** * adds index name formatting to a index name * * @param string name of the index * * @return string formatted index name *

* @access public */ function getIndexName($idx) { return sprintf($this->options['idxname_format'], preg_replace('/[^a-z0-9_\$]/i', '_', $idx)); } // }}} // {{{ function nextID($seq_name, $ondemand = true) /** * Returns the next free id of a sequence * * @param string name of the sequence * @param bool when true missing sequences are automatic created * * @return mixed MDB2 Error Object or id * * @access public */ function nextID($seq_name, $ondemand = true) { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function lastInsertID($table = null, $field = null) /** * Returns the autoincrement ID if supported or $id or fetches the current * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field) * * @param string name of the table into which a new row was inserted * @param string name of the field into which a new row was inserted * * @return mixed MDB2 Error Object or id * * @access public */ function lastInsertID($table = null, $field = null) { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function currID($seq_name) /** * Returns the current id of a sequence * * @param string name of the sequence * * @return mixed MDB2 Error Object or id * * @access public */ function currID($seq_name)

{ $this->warnings[] = 'database does not support getting current sequence value, the sequence value was incremented'; return $this->nextID($seq_name); } // }}} // {{{ function queryOne($query, $type = null, $colnum = 0) /** * Execute the specified query, fetch the value from the first column of * the first row of the result set and then frees * the result set. * * @param string the SELECT query statement to be executed. * @param string optional argument that specifies the expected * datatype of the result set field, so that an eventual conversion * may be performed. The default datatype is text, meaning that no * conversion is performed * @param int the column number to fetch * * @return mixed MDB2_OK or field value on success, a MDB2 error on failu re * * @access public */ function queryOne($query, $type = null, $colnum = 0) { $result = $this->query($query, $type); if (!MDB2::isResultCommon($result)) { return $result; } $one = $result->fetchOne($colnum); $result->free(); return $one; } // }}} // {{{ function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_ DEFAULT) /** * Execute the specified query, fetch the values from the first * row of the result set into an array and then frees * the result set. * * @param string the SELECT query statement to be executed. * @param array optional array argument that specifies a list of * expected datatypes of the result set columns, so that the eventual * conversions may be performed. The default list of datatypes is * empty, meaning that no conversion is performed. * @param int how the array data should be indexed * * @return mixed MDB2_OK or data array on success, a MDB2 error on failur e * * @access public */ function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT

) { $result = $this->query($query, $types); if (!MDB2::isResultCommon($result)) { return $result; } $row = $result->fetchRow($fetchmode); $result->free(); return $row; } // }}} // {{{ function queryCol($query, $type = null, $colnum = 0) /** * Execute the specified query, fetch the value from the first column of * each row of the result set into an array and then frees the result set. * * @param string the SELECT query statement to be executed. * @param string optional argument that specifies the expected * datatype of the result set field, so that an eventual conversion * may be performed. The default datatype is text, meaning that no * conversion is performed * @param int the row number to fetch * * @return mixed MDB2_OK or data array on success, a MDB2 error on failur e * * @access public */ function queryCol($query, $type = null, $colnum = 0) { $result = $this->query($query, $type); if (!MDB2::isResultCommon($result)) { return $result; } $col = $result->fetchCol($colnum); $result->free(); return $col; } // }}} // {{{ function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_ DEFAULT, $rekey = false, $force_array = false, $group = false) /** * Execute the specified query, fetch all the rows of the result set into * a two dimensional array and then frees the result set. * * @param string the SELECT query statement to be executed. * @param array optional array argument that specifies a list of * expected datatypes of the result set columns, so that the eventual * conversions may be performed. The default list of datatypes is * empty, meaning that no conversion is performed. * @param int how the array data should be indexed * @param bool if set to true, the $all will have the first * column as its first dimension * @param bool 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 bool 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 mixed MDB2_OK or data array on success, a MDB2 error on failur e * * @access public */ function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT , $rekey = false, $force_array = false, $group = false) { $result = $this->query($query, $types); if (!MDB2::isResultCommon($result)) { return $result; } $all = $result->fetchAll($fetchmode, $rekey, $force_array, $group); $result->free(); return $all; } // }}} } // }}} // {{{ class MDB2_Result /** * The dummy class that all user space result classes should extend from * * @package MDB2 * @category Database * @author Lukas Smith <smith@pooteeweet.org> */ class MDB2_Result { } // }}} // {{{ class MDB2_Result_Common extends MDB2_Result /** * The common result class for MDB2 result objects * * @package MDB2 * @category Database * @author Lukas Smith <smith@pooteeweet.org> */ class MDB2_Result_Common extends MDB2_Result { // {{{ Variables (Properties) var $db; var $result; var $rownum = -1;

var var var var var var

$types = array(); $values = array(); $offset; $offset_count = 0; $limit; $column_names;

// }}} // {{{ constructor: function __construct(&$db, &$result, $limit = 0, $offset = 0) /** * Constructor */ function __construct(&$db, &$result, $limit = 0, $offset = 0) { $this->db =& $db; $this->result =& $result; $this->offset = $offset; $this->limit = max(0, $limit - 1); } // }}} // {{{ function MDB2_Result_Common(&$db, &$result, $limit = 0, $offset = 0) /** * PHP 4 Constructor */ function MDB2_Result_Common(&$db, &$result, $limit = 0, $offset = 0) { $this->__construct($db, $result, $limit, $offset); } // }}} // {{{ function setResultTypes($types) /** * Define the list of types to be associated with the columns of a given * result set. * * This function may be called before invoking fetchRow(), fetchOne(), * fetchCol() and fetchAll() so that the necessary data type * conversions are performed on the data to be retrieved by them. If this * function is not called, the type of all result set columns is assumed * to be text, thus leading to not perform any conversions. * * @param array variable that lists the * data types to be expected in the result set columns. If this array * contains less types than the number of columns that are returned * in the result set, the remaining columns are assumed to be of the * type text. Currently, the types clob and blob are not fully * supported. * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function setResultTypes($types) { $load = $this->db->loadModule('Datatype', null, true);

if (PEAR::isError($load)) { return $load; } $types = $this->db->datatype->checkResultTypes($types); if (PEAR::isError($types)) { return $types; } $this->types = $types; return MDB2_OK; } // }}} // {{{ function seek($rownum = 0) /** * Seek to a specific row in a result set * * @param int number of the row where the data can be found * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function seek($rownum = 0) { $target_rownum = $rownum - 1; if ($this->rownum > $target_rownum) { return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'seeking to previous rows not implemented', __FUNCTION__); } while ($this->rownum < $target_rownum) { $this->fetchRow(); } return MDB2_OK; } // }}} // {{{ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = nul l) /** * Fetch and return a row of data * * @param int how the array data should be indexed * @param int number of the row where the data can be found * * @return int data array on success, a MDB2 error on failure * * @access public */ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) { $err =& $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); return $err; } // }}} // {{{ function fetchOne($colnum = 0)

/** * fetch single column from the next row from a result set * * @param int the column number to fetch * @param int number of the row where the data can be found * * @return string data on success, a MDB2 error on failure * * @access public */ function fetchOne($colnum = 0, $rownum = null) { $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMO DE_ASSOC; $row = $this->fetchRow($fetchmode, $rownum); if (!is_array($row) || PEAR::isError($row)) { return $row; } if (!array_key_exists($colnum, $row)) { return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null, 'column is not defined in the result set: '.$colnum, __FUNCTION_ _); } return $row[$colnum]; } // }}} // {{{ function fetchCol($colnum = 0) /** * Fetch and return a column from the current row pointer position * * @param int the column number to fetch * * @return mixed data array on success, a MDB2 error on failure * * @access public */ function fetchCol($colnum = 0) { $column = array(); $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMO DE_ASSOC; $row = $this->fetchRow($fetchmode); if (is_array($row)) { if (!array_key_exists($colnum, $row)) { return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null, 'column is not defined in the result set: '.$colnum, __FUNCT ION__); } do { $column[] = $row[$colnum]; } while (is_array($row = $this->fetchRow($fetchmode))); } if (PEAR::isError($row)) { return $row; } return $column; }

// }}} // {{{ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false , $force_array = false, $group = false) /** * Fetch and return all rows from the current row pointer position * * @param int $fetchmode the fetch mode to use: * + MDB2_FETCHMODE_ORDERED * + MDB2_FETCHMODE_ASSOC * + MDB2_FETCHMODE_ORDERED | MDB2_FETCHMODE_FLIP PED * D * @param bool if set to true, the $all will have the first * column as its first dimension * @param bool 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 bool 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 mixed data array on success, a MDB2 error on failure * * @access public * @see getAssoc() */ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false) { $all = array(); $row = $this->fetchRow($fetchmode); if (PEAR::isError($row)) { return $row; } elseif (!$row) { return $all; } $shift_array = $rekey ? false : null; if (!is_null($shift_array)) { if (is_object($row)) { $colnum = count(get_object_vars($row)); } else { $colnum = count($row); } if ($colnum < 2) { return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null, 'rekey feature requires atleast 2 column', __FUNCTION__); } $shift_array = (!$force_array && $colnum == 2); } if ($rekey) { do { if (is_object($row)) { $arr = get_object_vars($row); $key = reset($arr); unset($row->{$key}); + MDB2_FETCHMODE_ASSOC | MDB2_FETCHMODE_FLIPPE

} else { if ($fetchmode & MDB2_FETCHMODE_ASSOC) { $key = reset($row); unset($row[key($row)]); } else { $key = array_shift($row); } if ($shift_array) { $row = array_shift($row); } } if ($group) { $all[$key][] = $row; } else { $all[$key] = $row; } } while (($row = $this->fetchRow($fetchmode))); } elseif ($fetchmode & MDB2_FETCHMODE_FLIPPED) { do { foreach ($row as $key => $val) { $all[$key][] = $val; } } while (($row = $this->fetchRow($fetchmode))); } else { do { $all[] = $row; } while (($row = $this->fetchRow($fetchmode))); } return $all; } // }}} // {{{ function rowCount() /** * Returns the actual row number that was last fetched (count from 0) * @return int * * @access public */ function rowCount() { return $this->rownum + 1; } // }}} // {{{ function numRows() /** * Returns the number of rows in a result object * * @return mixed MDB2 Error Object or the number of rows * * @access public */ function numRows() { return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); }

// }}} // {{{ function nextResult() /** * Move the internal result pointer to the next available result * * @return true on success, false if there is no more result set or an erro r object on failure * * @access public */ function nextResult() { return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function getColumnNames() /** * Retrieve the names of columns returned by the DBMS in a query result or * from the cache. * * @param bool If set to true the values are the column names, * otherwise the names of the columns are the keys. * @return mixed Array variable that holds the names of columns or an * MDB2 error on failure. * Some DBMS may not return any columns when the result set * does not contain any rows. * * @access public */ function getColumnNames($flip = false) { if (!isset($this->column_names)) { $result = $this->_getColumnNames(); if (PEAR::isError($result)) { return $result; } $this->column_names = $result; } if ($flip) { return array_flip($this->column_names); } return $this->column_names; } // }}} // {{{ function _getColumnNames() /** * Retrieve the names of columns returned by the DBMS in a query result. * * @return mixed Array variable that holds the names of columns as keys * or an MDB2 error on failure. * Some DBMS may not return any columns when the result set * does not contain any rows. *

* @access private */ function _getColumnNames() { return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function numCols() /** * Count the number of columns returned by the DBMS in a query result. * * @return mixed integer value with the number of columns, a MDB2 error * on failure * * @access public */ function numCols() { return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function getResource() /** * return the resource associated with the result object * * @return resource * * @access public */ function getResource() { return $this->result; } // }}} // {{{ function bindColumn($column, &$value, $type = null) /** * Set bind variable to a column. * * @param int column number or name * @param mixed variable reference * @param string specifies the type of the field * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function bindColumn($column, &$value, $type = null) { if (!is_numeric($column)) { $column_names = $this->getColumnNames(); if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { if ($this->db->options['field_case'] == CASE_LOWER) {

$column = strtolower($column); } else { $column = strtoupper($column); } } $column = $column_names[$column]; } $this->values[$column] =& $value; if (!is_null($type)) { $this->types[$column] = $type; } return MDB2_OK; } // }}} // {{{ function _assignBindColumns($row) /** * Bind a variable to a value in the result row. * * @param array row data * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access private */ function _assignBindColumns($row) { $row = array_values($row); foreach ($row as $column => $value) { if (array_key_exists($column, $this->values)) { $this->values[$column] = $value; } } return MDB2_OK; } // }}} // {{{ function free() /** * Free the internal resources associated with result. * * @return bool true on success, false if result is invalid * * @access public */ function free() { $this->result = false; return MDB2_OK; } // }}} } // }}} // {{{ class MDB2_Row /**

* The simple class that accepts row data as an array * * @package MDB2 * @category Database * @author Lukas Smith <smith@pooteeweet.org> */ class MDB2_Row { // {{{ constructor: function __construct(&$row) /** * constructor * * @param resource row data as array */ function __construct(&$row) { foreach ($row as $key => $value) { $this->$key = &$row[$key]; } } // }}} // {{{ function MDB2_Row(&$row) /** * PHP 4 Constructor * * @param resource row data as array */ function MDB2_Row(&$row) { $this->__construct($row); } // }}} } // }}} // {{{ class MDB2_Statement_Common /** * The common statement class for MDB2 statement objects * * @package MDB2 * @category Database * @author Lukas Smith <smith@pooteeweet.org> */ class MDB2_Statement_Common { // {{{ Variables (Properties) var var var var var var var var $db; $statement; $query; $result_types; $types; $values = array(); $limit; $offset;

var $is_manip; // }}} // {{{ constructor: function __construct(&$db, &$statement, $positions, $que ry, $types, $result_types, $is_manip = false, $limit = null, $offset = null) /** * Constructor */ function __construct(&$db, &$statement, $positions, $query, $types, $result_ types, $is_manip = false, $limit = null, $offset = null) { $this->db =& $db; $this->statement =& $statement; $this->positions = $positions; $this->query = $query; $this->types = (array)$types; $this->result_types = (array)$result_types; $this->limit = $limit; $this->is_manip = $is_manip; $this->offset = $offset; } // }}} // {{{ function MDB2_Statement_Common(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null) /** * PHP 4 Constructor */ function MDB2_Statement_Common(&$db, &$statement, $positions, $query, $types , $result_types, $is_manip = false, $limit = null, $offset = null) { $this->__construct($db, $statement, $positions, $query, $types, $result_ types, $is_manip, $limit, $offset); } // }}} // {{{ function bindValue($parameter, &$value, $type = null) /** * Set the value of a parameter of a prepared query. * * @param int the order number of the parameter in the query * statement. The order number of the first parameter is 1. * @param mixed value that is meant to be assigned to specified * parameter. The type of the value depends on the $type argument. * @param string specifies the type of the field * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function bindValue($parameter, $value, $type = null) { if (!is_numeric($parameter)) { $parameter = preg_replace('/^:(.*)$/', '\\1', $parameter); } if (!in_array($parameter, $this->positions)) { return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,

'Unable to bind to missing placeholder: '.$parameter, __FUNCTION __); } $this->values[$parameter] = $value; if (!is_null($type)) { $this->types[$parameter] = $type; } return MDB2_OK; } // }}} // {{{ function bindValueArray($values, $types = null) /** * Set the values of multiple a parameter of a prepared query in bulk. * * @param array specifies all necessary information * for bindValue() the array elements must use keys corresponding to * the number of the position of the parameter. * @param array specifies the types of the fields * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public * @see bindParam() */ function bindValueArray($values, $types = null) { $types = is_array($types) ? array_values($types) : array_fill(0, count($ values), null); $parameters = array_keys($values); foreach ($parameters as $key => $parameter) { $err = $this->bindValue($parameter, $values[$parameter], $types[$key ]); if (PEAR::isError($err)) { return $err; } } return MDB2_OK; } // }}} // {{{ function bindParam($parameter, &$value, $type = null) /** * Bind a variable to a parameter of a prepared query. * * @param int the order number of the parameter in the query * statement. The order number of the first parameter is 1. * @param mixed variable that is meant to be bound to specified * parameter. The type of the value depends on the $type argument. * @param string specifies the type of the field * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function bindParam($parameter, &$value, $type = null) { if (!is_numeric($parameter)) {

$parameter = preg_replace('/^:(.*)$/', '\\1', $parameter); } if (!in_array($parameter, $this->positions)) { return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION __); } $this->values[$parameter] =& $value; if (!is_null($type)) { $this->types[$parameter] = $type; } return MDB2_OK; } // }}} // {{{ function bindParamArray(&$values, $types = null) /** * Bind the variables of multiple a parameter of a prepared query in bulk. * * @param array specifies all necessary information * for bindParam() the array elements must use keys corresponding to * the number of the position of the parameter. * @param array specifies the types of the fields * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public * @see bindParam() */ function bindParamArray(&$values, $types = null) { $types = is_array($types) ? array_values($types) : array_fill(0, count($ values), null); $parameters = array_keys($values); foreach ($parameters as $key => $parameter) { $err = $this->bindParam($parameter, $values[$parameter], $types[$key ]); if (PEAR::isError($err)) { return $err; } } return MDB2_OK; } // }}} // {{{ function &execute($values = null, $result_class = true, $result_wrap_ class = false) /** * Execute a prepared query statement. * * @param array specifies all necessary information * for bindParam() the array elements must use keys corresponding to * the number of the position of the parameter. * @param mixed specifies which result class to use * @param mixed specifies which class to wrap results in * * @return mixed a result handle or MDB2_OK on success, a MDB2 error on f ailure

* * @access public */ function &execute($values = null, $result_class = true, $result_wrap_class = false) { if (is_null($this->positions)) { return $this->db->raiseError(MDB2_ERROR, null, null, 'Prepared statement has already been freed', __FUNCTION__); } $values = (array)$values; if (!empty($values)) { $err = $this->bindValueArray($values); if (PEAR::isError($err)) { return $this->db->raiseError(MDB2_ERROR, null, null, 'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__); } } $result =& $this->_execute($result_class, $result_wrap_class); return $result; } // }}} // {{{ function &_execute($result_class = true, $result_wrap_class = false) /** * Execute * * @param * @param * * @return re * * @access private */ function &_execute($result_class = true, $result_wrap_class = false) { $this->last_query = $this->query; $query = ''; $last_position = 0; foreach ($this->positions as $current_position => $parameter) { if (!array_key_exists($parameter, $this->values)) { return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: '.$parameter, __FUNC TION__); } $value = $this->values[$parameter]; $query.= substr($this->query, $last_position, $current_position - $l ast_position); if (!isset($value)) { $value_quoted = 'NULL'; } else { $type = !empty($this->types[$parameter]) ? $this->types[$paramet er] : null; $value_quoted = $this->db->quote($value, $type); if (PEAR::isError($value_quoted)) { return $value_quoted; a prepared query statement helper method. mixed mixed mixed specifies which result class to use specifies which class to wrap results in MDB2_Result or integer on success, a MDB2 error on failu

} } $query.= $value_quoted; $last_position = $current_position + 1; } $query.= substr($this->query, $last_position); $this->db->offset = $this->offset; $this->db->limit = $this->limit; if ($this->is_manip) { $result = $this->db->exec($query); } else { $result =& $this->db->query($query, $this->result_types, $result_cla ss, $result_wrap_class); } return $result; } // }}} // {{{ function free() /** * Release resources allocated for the specified prepared query. * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function free() { if (is_null($this->positions)) { return $this->db->raiseError(MDB2_ERROR, null, null, 'Prepared statement has already been freed', __FUNCTION__); } $this->statement = null; $this->positions = null; $this->query = null; $this->types = null; $this->result_types = null; $this->limit = null; $this->is_manip = null; $this->offset = null; $this->values = null; return MDB2_OK; } // }}} } // }}} // {{{ class MDB2_Module_Common /** * The common modules class for MDB2 module objects * * @package MDB2 * @category Database * @author Lukas Smith <smith@pooteeweet.org>

*/ class MDB2_Module_Common { // {{{ Variables (Properties) /** * contains the key to the global MDB2 instance array of the associated * MDB2 instance * * @var int * @access protected */ var $db_index; // }}} // {{{ constructor: function __construct($db_index) /** * Constructor */ function __construct($db_index) { $this->db_index = $db_index; } // }}} // {{{ function MDB2_Module_Common($db_index) /** * PHP 4 Constructor */ function MDB2_Module_Common($db_index) { $this->__construct($db_index); } // }}} // {{{ function &getDBInstance() /** * Get the instance of MDB2 associated with the module instance * * @return object MDB2 instance or a MDB2 error on failure * * @access public */ function &getDBInstance() { if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) { $result =& $GLOBALS['_MDB2_databases'][$this->db_index]; } else { $result =& MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'could not find MDB2 instance'); } return $result; } // }}} }

// }}} // {{{ function MDB2_closeOpenTransactions() /** * Close any open transactions form persistent connections * * @return void * * @access public */ function MDB2_closeOpenTransactions() { reset($GLOBALS['_MDB2_databases']); while (next($GLOBALS['_MDB2_databases'])) { $key = key($GLOBALS['_MDB2_databases']); if ($GLOBALS['_MDB2_databases'][$key]->opened_persistent && $GLOBALS['_MDB2_databases'][$key]->in_transaction ) { $GLOBALS['_MDB2_databases'][$key]->rollback(); } } } // }}} // {{{ function MDB2_defaultDebugOutput(&$db, $scope, $message, $is_manip = null ) /** * default debug output handler * * @param object reference to an MDB2 database object * @param string usually the method name that triggered the debug call: * for example 'query', 'prepare', 'execute', 'parameters', * 'beginTransaction', 'commit', 'rollback' * @param string message that should be appended to the debug variable * @param array contains context information about the debug() call * common keys are: is_manip, time, result etc. * * @return void|string optionally return a modified message, this allows * rewriting a query before being issued or prepared * * @access public */ function MDB2_defaultDebugOutput(&$db, $scope, $message, $context = array()) { $db->debug_output.= $scope.'('.$db->db_index.'): '; $db->debug_output.= $message.$db->getOption('log_line_break'); return $message; } // }}} ?>

You might also like