Package home | Report new bug | New search | Development Roadmap Status: Open | Feedback | All | Closed Since Version 2.5.0b5

Request #18229 general code cleanup (jgg)
Submitted: 2011-01-31 04:14 UTC
From: psmerd Assigned:
Status: No Feedback Package: MDB2 (version 2.5.0b3)
PHP Version: 5.3.0 OS: all
Roadmaps: (Not assigned)    
Subscription  


 [2011-01-31 04:14 UTC] psmerd (John Gillespie)
Description: ------------ The attached file has the following changes to MDB2.php v2.5.0b3 1) The recent versions of MDB2 declare class properties as var, and all methods as public. I have added public/protected/private, guided by the storage class mentioned in comments. Where mine and the comments differ, mine should be used. This delta has been tested in the field for 18 months at different sites. If these changes are made, corresponding changes in the drivers will also be needed. 2) There are several methods that do not reference $this that should be static. 3) Several functions use the construct foreach (arr as $key => $value) where $value is not used. These have been replaced by the pattern foreach (array_keys($arr) as $key) 4) for preg_match, the $match variables are not properly initialized. 5) several functions neglect to return a value consistently All of these issues are structural, and do not create externally measurable problems.

Comments

 [2011-01-31 04:23 UTC] psmerd (John Gillespie)
<?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.307 2007/11/10 13:29:05 quipo Exp $ // /** * @package MDB2 * @category Database * @author 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); define('MDB2_ERROR_NO_PERMISSION', -36); define('MDB2_ERROR_DISCONNECT_FAILED', -37); // }}} // {{{ Verbose constants /** * These are just helper constants to more verbosely express parameters to prepare() */ 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_Driver_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 */ static 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 */ static public function classExists($classname) { return class_exists($classname, false); } // }}} // {{{ 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 */ static public function loadClass($class_name, $debug) { if (!MDB2::classExists($class_name)) { $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php'; 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_name'"; } else { $msg = "unable to load class '$class_name' from file '$file_name'"; } $err = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg); return $err; } if (!MDB2::classExists($class_name)) { $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 * * @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 */ static 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_Driver_* connection object and connect to the specified * database * * @param mixed $dsn '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 $options 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 */ static public 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. * * @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 */ static public 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('array'); 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'])) { return $GLOBALS['_MDB2_databases'][key($GLOBALS['_MDB2_databases'])]; } $db = MDB2::factory($dsn, $options); return $db; } // }}} // {{{ function areEquals() /** * It looks like there's a memory leak in array_diff() in PHP 5.1.x, * so use this method instead. * @see http://pear.php.net/bugs/bug.php?id=11790 * * @param array $arr1 * @param array $arr2 * @return boolean */ static public function areEquals($arr1, $arr2) { if (count($arr1) != count($arr2)) { return false; } foreach (array_keys($arr1) as $k) { if (!array_key_exists($k, $arr2) || $arr1[$k] != $arr2[$k]) { return false; } } return true; } // }}} // {{{ function loadFile($file) /** * load a file (like 'Date') * * @param string $file name of the file in the MDB2 directory (without '.php') * * @return string name of the file that was included * * @access public */ static 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 */ static public function apiVersion() { return '2.5.0b3'; } // }}} // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = 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 */ static public function &raiseError($code = null, $mode = null, $options = null, $userinfo = null, $dummy1 = null, $dummy2 = null, $dummy3 = false) { $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 */ static public function isError($data, $code = null) { if ($data instanceof MDB2_Error) { if (null === $code) { return true; } if (is_string($code)) { return $data->getMessage() === $code; } return in_array($data->getCode(), (array)$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 */ static public function isConnection($value) { return ($value instanceof MDB2_Driver_Common); } // }}} // {{{ function isResult($value) /** * Tell whether a value is a MDB2 result * * @param mixed $value value to test * * @return bool whether $value is a MDB2 result * * @access public */ static public function isResult($value) { return ($value instanceof MDB2_Result); } // }}} // {{{ function isResultCommon($value) /** * Tell whether a value is a MDB2 result implementing the common interface * * @param mixed $value value to test * * @return bool whether $value is a MDB2 result implementing the common interface * * @access public */ static public function isResultCommon($value) { return ($value instanceof 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 */ static public function isStatement($value) { return ($value instanceof MDB2_Statement_Common); } // }}} // {{{ 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 */ static public function errorMessage($value = null) { static $errorMessages; if (is_array($value)) { $errorMessages = $value; return MDB2_OK; } if (!isset($errorMessages)) { $errorMessages = array( MDB2_OK => 'no error', MDB2_ERROR => 'unknown error', MDB2_ERROR_ALREADY_EXISTS => 'already exists', MDB2_ERROR_CANNOT_CREATE => 'can not create', MDB2_ERROR_CANNOT_ALTER => 'can not alter', MDB2_ERROR_CANNOT_REPLACE => 'can not replace', MDB2_ERROR_CANNOT_DELETE => 'can not delete', MDB2_ERROR_CANNOT_DROP => 'can not drop', MDB2_ERROR_CONSTRAINT => 'constraint violation', MDB2_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint', MDB2_ERROR_DIVZERO => 'division by zero', MDB2_ERROR_INVALID => 'invalid', MDB2_ERROR_INVALID_DATE => 'invalid date or time', MDB2_ERROR_INVALID_NUMBER => 'invalid number', MDB2_ERROR_MISMATCH => 'mismatch', MDB2_ERROR_NODBSELECTED => 'no database selected', MDB2_ERROR_NOSUCHFIELD => 'no such field', MDB2_ERROR_NOSUCHTABLE => 'no such table', MDB2_ERROR_NOT_CAPABLE => 'MDB2 backend not capable', MDB2_ERROR_NOT_FOUND => 'not found', MDB2_ERROR_NOT_LOCKED => 'not locked', MDB2_ERROR_SYNTAX => 'syntax error', MDB2_ERROR_UNSUPPORTED => 'not supported', MDB2_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', MDB2_ERROR_INVALID_DSN => 'invalid DSN', MDB2_ERROR_CONNECT_FAILED => 'connect failed', MDB2_ERROR_NEED_MORE_DATA => 'insufficient data supplied', MDB2_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', MDB2_ERROR_NOSUCHDB => 'no such database', MDB2_ERROR_ACCESS_VIOLATION => 'insufficient permissions', MDB2_ERROR_LOADMODULE => 'error while including on demand module', MDB2_ERROR_TRUNCATED => 'truncated', MDB2_ERROR_DEADLOCK => 'deadlock detected', MDB2_ERROR_NO_PERMISSION => 'no permission', MDB2_ERROR_DISCONNECT_FAILED => 'disconnect failed', ); } if (null === $value) { return $errorMessages; } if (PEAR::isError($value)) { $value = $value->getCode(); } 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=0644 * 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> */ static public 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) $arr = array(); 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 $match = array(); 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; $pos = strrpos($proto_opts, '/'); $dsn = substr($proto_opts, $pos + 1); $proto_opts = substr($proto_opts, 0, $pos); } 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 (!array_key_exists($key, $parsed) || false === $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 */ static 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_ERROR_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 mixed additional debug info, such as the last query */ function __construct($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, $debuginfo = null, $dummy = null) { if (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 */ public $db_index = 0; /** * DSN used for the next query * @var array * @access protected */ public $dsn = array(); /** * DSN that was used to create the current connection * @var array * @access protected */ protected $connected_dsn = array(); /** * connection resource * @var mixed * @access protected */ public $connection = 0; /** * if the current opened connection is a persistent connection * @var bool * @access protected */ protected $opened_persistent; /** * the name of the database for the next query * @var string * @access protected */ protected $database_name = ''; /** * the name of the database currently selected * @var string * @access protected */ protected $connected_database_name = ''; /** * server version information * @var string * @access protected */ protected $connected_server_info = ''; /** * list of all supported features of the given driver * @var array * @access public */ public $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, 'triggers' => 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 connections</li> * <li>$options['field_case'] -> CASE_LOWER|CASE_UPPER: determines what case to force on field/table names</li> * <li>$options['disable_query'] -> boolean: determines if queries should be 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 sets into</li> * <li>$options['result_buffering'] -> boolean should results be buffered or not?</li> * <li>$options['fetch_class'] -> string: class to use when fetch mode object 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 debug 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 savepoint names</li> * <li>$options['statement_format'] -> string: pattern for prepared statement names</li> * <li>$options['seqcol_name'] -> string: sequence column name</li> * <li>$options['quote_identifier'] -> boolean: if identifier quoting should be done when check_option is used</li> * <li>$options['use_transactions'] -> boolean: if transaction use should be enabled</li> * <li>$options['decimal_places'] -> integer: number of decimal places to handle</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 to be emulated</li> * <li>$options['datatype_map'] -> array: map user defined datatypes to other primitive datatypes</li> * <li>$options['datatype_map_callback'] -> array: callback function/method that should be called</li> * <li>$options['bindname_format'] -> string: regular expression pattern for named parameters</li> * <li>$options['multi_query'] -> boolean: determines if queries returning multiple result sets should be executed</li> * <li>$options['max_identifiers_length'] -> integer: max identifier length</li> * <li>$options['default_fk_action_onupdate'] -> string: default FOREIGN KEY ON UPDATE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']</li> * <li>$options['default_fk_action_ondelete'] -> string: default FOREIGN KEY ON DELETE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']</li> * </ul> * * @var array * @access public * @see MDB2::connect() * @see MDB2::factory() * @see MDB2::singleton() * @see MDB2_Driver_Common::setOption() */ public $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(), 'lob_allow_url_include' => false, 'bindname_format' => '(?:\d+)|(?:[a-zA-Z][a-zA-Z0-9_]*)', 'max_identifiers_length' => 30, 'default_fk_action_onupdate' => 'RESTRICT', 'default_fk_action_ondelete' => 'RESTRICT', ); /** * string array * @var string * @access protected */ protected $string_quoting = array('start' => "'", 'end' => "'", 'escape' => false, 'escape_pattern' => false); /** * identifier quoting * @var array * @access protected */ protected $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"'); /** * sql comments * @var array * @access protected */ protected $sql_comments = array( array('start' => '--', 'end' => "\n", 'escape' => false), array('start' => '/*', 'end' => '*/', 'escape' => false), ); /** * comparision wildcards * @var array * @access protected */ protected $wildcards = array('%', '_'); /** * column alias keyword * @var string * @access protected */ protected $as_keyword = ' AS '; /** * warnings * @var array * @access protected */ protected $warnings = array(); /** * string with the debugging information * @var string * @access public */ public $debug_output = ''; /** * determine if there is an open transaction * @var bool * @access protected */ protected $in_transaction = false; /** * the smart transaction nesting depth * @var int * @access protected */ protected $nested_transaction_counter = null; /** * the first error that occured inside a nested transaction * @var MDB2_Error|bool * @access protected */ protected $has_transaction_error = false; /** * result offset used in the next query * @var int * @access protected */ public $offset = 0; /** * result limit used in the next query * @var int * @access protected */ public $limit = 0; /** * Database backend used in PHP (mysql, odbc etc.) * @var string * @access public */ public $phptype; /** * Database used with regards to SQL syntax etc. * @var string * @access public */ public $dbsyntax; /** * the last query sent to the driver * @var string * @access public */ public $last_query; /** * the default fetchmode used * @var int * @access protected */ public $fetchmode = MDB2_FETCHMODE_ORDERED; /** * array of module instances * @var array * @access protected */ protected $modules = array(); /** * determines of the PHP4 destructor emulation has been enabled yet * @var array * @access protected */ protected $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; } // }}} // {{{ 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 */ 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 */ public function errorInfo($error = null) { return array($error, null, null); } // }}} // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = 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 $code integer error code, or a PEAR error object (all * other parameters are ignored if this parameter is * an object * @param int $mode error mode, see PEAR_Error docs * @param mixed $options 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 $userinfo Extra debug information. Defaults to the last * query and native error code. * @param string $method name of the method that triggered the error * @param string $dummy1 not used * @param bool $dummy2 not used * * @return PEAR_Error instance of a PEAR Error object * @access public * @see PEAR_Error */ public function &raiseError($code = null, $mode = null, $options = null, $userinfo = null, $method = null, $dummy1 = null, $dummy2 = false ) { $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 ((null === $mode) && !empty($this->_default_error_mode)) { $mode = $this->_default_error_mode; $options = $this->_default_error_options; } if (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 ((null !== $native_errno) && $native_errno !== '') { $userinfo.= "[Native code: $native_errno]\n"; } if ((null !== $native_msg) && $native_msg !== '') { $userinfo.= "[Native message: ". strip_tags($native_msg) ."]\n"; } if (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_transaction_error) { $this->has_transaction_error = $err; } return $err; } // }}} // {{{ function resetWarnings() /** * reset the warning array * * @return void * * @access public */ 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() */ public 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_OBJECT */ public 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 */ 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 */ 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 */ 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_manip']; } 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 */ 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 */ 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) * * @param string the input string to quote * * @return string quoted string * * @access public */ 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'], $text); foreach ($this->wildcards as $wildcard) { $text = str_replace($wildcard, $this->string_quoting['escape_pattern'] . $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. * * NOTE: if you have table names containing periods, don't use this method * (@see bug #11906) * * 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 */ 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_quoting['escape'] . $this->identifier_quoting['end'], $str); $parts = explode('.', $str); foreach (array_keys($parts) as $k) { $parts[$k] = $this->identifier_quoting['start'] . $parts[$k] . $this->identifier_quoting['end']; } return implode('.', $parts); } // }}} // {{{ function getAsKeyword() /** * Gets the string to alias column * * @return string to use when aliasing a column */ public 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 */ 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 */ public 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)] = $value; } $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)] = $value; } $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)] = $value; } $row = $tmp_row; break; case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_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)] = $value; } $row = $tmp_row; break; } } // }}} // {{{ function loadModule($module, $property = null, $phptype_specific = null) /** * 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 */ 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_name)) ) { $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 module in a specific version if ($version) { if (method_exists($class_name, 'getClassName')) { $class_name_new = call_user_func(array($class_name, 'getClassName'), $this->db_index); if ($class_name != $class_name_new) { $class_name = $class_name_new; $err = MDB2::loadClass($class_name, $this-
 [2011-02-16 15:14 UTC] alec (Aleksander Machniak)
The file is not complete. Please create unified patch file and attach it to this request.
 [2011-02-20 09:45 UTC] doconnor (Daniel O'Connor)
-Type: Bug +Type: Feature/Change Request
 [2011-03-07 01:27 UTC] quipo (Lorenzo Alberton)
-Status: Open +Status: Feedback
Hi John, can you please upload your changes as a patch and attach it to this ticket? Thanks
 [2012-03-22 19:34 UTC] danielc (Daniel Convissor)
-Status: Feedback +Status: No Feedback