dbType = $dbType; // $this->initMetaFunctions(); if (!$errorHandler) { $this->errorHandler = Array(&$this, 'handleError'); } else { $this->errorHandler = $errorHandler; } $this->_captureStatistics = defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !(defined('ADMIN') && ADMIN); if (class_exists('kApplication')) { // prevents "Fatal Error" on 2nd installation step (when database is empty) $this->Application =& kApplication::Instance(); } } /** * Set's custom error * * @param int $code * @param string $msg * @access public */ function setError($code, $msg) { $this->errorCode = $code; $this->errorMessage = $msg; } /** * Checks if previous query execution * raised an error. * * @return bool * @access public */ function hasError() { return !($this->errorCode == 0); } /** * Caches function specific to requested * db type * * @access private */ function initMetaFunctions() { $ret = Array(); switch ($this->dbType) { case 'mysql': $ret = Array(); // only define functions, that name differs from "dbType_" break; } $this->metaFunctions = $ret; } /** * Get's function for specific db type * based on it's meta name * * @param string $name * @return string * @access private */ function getMetaFunction($name) { /*if (!isset($this->metaFunctions[$name])) { $this->metaFunctions[$name] = $name; }*/ return $this->dbType.'_'.$name; } /** * Try to connect to database server * using specified parameters and set * database to $db if connection made * * @param string $host * @param string $user * @param string $pass * @param string $db * @access public */ function Connect($host, $user, $pass, $db, $force_new = false) { $func = $this->getMetaFunction('connect'); $this->connectionID = $func($host, $user, $pass, $force_new) or trigger_error("Database connection failed, please check your connection settings", defined('IS_INSTALL') && IS_INSTALL ? E_USER_WARNING : E_USER_ERROR); if ($this->connectionID) { if (defined('DBG_SQL_MODE')) { $this->Query('SET sql_mode = \''.DBG_SQL_MODE.'\''); } if (defined('SQL_COLLATION') && defined('SQL_CHARSET')) { $this->Query('SET NAMES \''.SQL_CHARSET.'\' COLLATE \''.SQL_COLLATION.'\''); } $this->setDB($db); $this->showError(); } else { // simulate error, becase php mysql client doesn't provide such information $this->errorCode = 2003; $this->errorMessage = "Can't connect to MySQL server on '$host' (113)"; } } function ReConnect($host, $user, $pass, $db, $force_new = false) { $func = $this->getMetaFunction('close'); $func($this->connectionID); $this->Connect($host, $user, $pass, $db, $force_new); } /** * Shows error message from previous operation * if it failed * * @access private */ function showError($sql = '') { $this->setError(0, ''); // reset error if ($this->connectionID) { $func = $this->getMetaFunction('errno'); $this->errorCode = $func($this->connectionID); if ($this->hasError()) { $func = $this->getMetaFunction('error'); $this->errorMessage = $func($this->connectionID); if (is_array($this->errorHandler)) { $func = $this->errorHandler[1]; $ret = $this->errorHandler[0]->$func($this->errorCode, $this->errorMessage, $sql); } else { $func = $this->errorHandler; $ret = $func($this->errorCode,$this->errorMessage,$sql); } if (!$ret) exit; } } } /** * Default error handler for sql errors * * @param int $code * @param string $msg * @param string $sql * @return bool * @access private */ function handleError($code, $msg, $sql) { echo 'Processing SQL: '.$sql.'
'; echo 'Error ('.$code.'): '.$msg.'
'; return false; } /** * Set's database name for connection * to $new_name * * @param string $new_name * @return bool * @access public */ function setDB($new_name) { if (!$this->connectionID) return false; $func = $this->getMetaFunction('select_db'); return $func($new_name, $this->connectionID); } /** * Returns first field of first line * of recordset if query ok or false * otherwise * * @param string $sql * @param int $offset * @return string * @access public */ function GetOne($sql, $offset = 0) { $row = $this->GetRow($sql, $offset); if(!$row) return false; return array_shift($row); } /** * Returns first row of recordset * if query ok, false otherwise * * @param stirng $sql * @param int $offset * @return Array * @access public */ function GetRow($sql, $offset = 0) { $sql .= ' '.$this->getLimitClause($offset, 1); $ret = $this->Query($sql); if(!$ret) return false; return array_shift($ret); } /** * Returns 1st column of recordset as * one-dimensional array or false otherwise * Optional parameter $key_field can be used * to set field name to be used as resulting * array key * * @param string $sql * @param string $key_field * @return Array * @access public */ function GetCol($sql, $key_field = null) { $rows = $this->Query($sql); if (!$rows) return $rows; $i = 0; $row_count = count($rows); $ret = Array(); if (isset($key_field)) { while ($i < $row_count) { $ret[$rows[$i][$key_field]] = array_shift($rows[$i]); $i++; } } else { while ($i < $row_count) { $ret[] = array_shift($rows[$i]); $i++; } } return $ret; } /** * Queries db with $sql query supplied * and returns rows selected if any, false * otherwise. Optional parameter $key_field * allows to set one of the query fields * value as key in string array. * * @param string $sql * @param string $key_field * @return Array */ function Query($sql, $key_field = null, $no_debug = false) { $this->lastQuery = $sql; if (!$no_debug) { $this->_queryCount++; } if ($this->debugMode && !$no_debug) { return $this->debugQuery($sql,$key_field); } $query_func = $this->getMetaFunction('query'); // set 1st checkpoint: begin if ($this->_captureStatistics) { $start_time = getmicrotime(); } // set 1st checkpoint: end $this->queryID = $query_func($sql,$this->connectionID); if (is_resource($this->queryID)) { $ret = Array(); $fetch_func = $this->getMetaFunction('fetch_assoc'); if (isset($key_field)) { while (($row = $fetch_func($this->queryID))) { $ret[$row[$key_field]] = $row; } } else { while (($row = $fetch_func($this->queryID))) { $ret[] = $row; } } // set 2nd checkpoint: begin if ($this->_captureStatistics) { $query_time = getmicrotime() - $start_time; if ($query_time > DBG_MAX_SQL_TIME && !$no_debug) { $this->Application->logSlowQuery($sql, $query_time); } $this->_queryTime += $query_time; } // set 2nd checkpoint: end $this->Destroy(); return $ret; } else { // set 2nd checkpoint: begin if ($this->_captureStatistics) { $this->_queryTime += getmicrotime() - $start_time; } // set 2nd checkpoint: end } $this->showError($sql); return false; } function ChangeQuery($sql) { $this->Query($sql); return $this->errorCode == 0 ? true : false; } function debugQuery($sql, $key_field = null) { global $debugger; $query_func = $this->getMetaFunction('query'); // set 1st checkpoint: begin $profileSQLs = defined('DBG_SQL_PROFILE') && DBG_SQL_PROFILE; if ($profileSQLs) { $queryID = $debugger->generateID(); $debugger->profileStart('sql_'.$queryID, $debugger->formatSQL($sql)); } // set 1st checkpoint: end $this->queryID = $query_func($sql, $this->connectionID); if( is_resource($this->queryID) ) { $ret = Array(); $fetch_func = $this->getMetaFunction('fetch_assoc'); if( isset($key_field) ) { while( ($row = $fetch_func($this->queryID)) ) { $ret[$row[$key_field]] = $row; } } else { while( ($row = $fetch_func($this->queryID)) ) { $ret[] = $row; } } // set 2nd checkpoint: begin if ($profileSQLs) { $first_cell = count($ret) == 1 && count(current($ret)) == 1 ? current(current($ret)) : null; if (strlen($first_cell) > 200) { $first_cell = substr($first_cell, 0, 50) . ' ...'; } $debugger->profileFinish('sql_'.$queryID, null, null, $this->getAffectedRows(), $first_cell, $this->_queryCount, $this->nextQueryCachable); $debugger->profilerAddTotal('sql', 'sql_'.$queryID); $this->nextQueryCachable = false; } // set 2nd checkpoint: end $this->Destroy(); return $ret; } else { // set 2nd checkpoint: begin if ($profileSQLs) { $debugger->profileFinish('sql_'.$queryID, null, null, $this->getAffectedRows(), null, $this->_queryCount, $this->nextQueryCachable); $debugger->profilerAddTotal('sql', 'sql_'.$queryID); $this->nextQueryCachable = false; } // set 2nd checkpoint: end } $this->showError($sql); return false; } /** * Free memory used to hold recordset handle * * @access private */ function Destroy() { if($this->queryID) { $free_func = $this->getMetaFunction('free_result'); $free_func($this->queryID); $this->queryID = null; } } /** * Returns auto increment field value from * insert like operation if any, zero otherwise * * @return int * @access public */ function getInsertID() { $func = $this->getMetaFunction('insert_id'); return $func($this->connectionID); } /** * Returns row count affected by last query * * @return int * @access public */ function getAffectedRows() { $func = $this->getMetaFunction('affected_rows'); return $func($this->connectionID); } /** * Returns LIMIT sql clause part for specific db * * @param int $offset * @param int $rows * @return string * @access private */ function getLimitClause($offset, $rows) { if(!($rows > 0)) return ''; switch ($this->dbType) { default: return 'LIMIT '.$offset.','.$rows; break; } } /** * If it's a string, adds quotes and backslashes (only work since PHP 4.3.0) * Otherwise returns as-is * * @param mixed $string * * @return string */ function qstr($string) { if ( is_null($string) ) { return 'NULL'; } # This will also quote numeric values. This should be harmless, # and protects against weird problems that occur when they really # _are_ strings such as article titles and string->number->string # conversion is not 1:1. return "'" . mysql_real_escape_string($string, $this->connectionID) . "'"; } /** * Escapes strings (only work since PHP 4.3.0) * * @param mixed $string * * @return string */ function escape($string) { if ( is_null($string) ) { return 'NULL'; } $string = mysql_real_escape_string($string, $this->connectionID); // prevent double-escaping of MySQL wildcard symbols ("%" and "_") in case if they were already escaped return str_replace(Array ('\\\\%', '\\\\_'), Array ('\\%', '\\_'), $string); } /** * Returns last error code occured * * @return int */ function getErrorCode() { return $this->errorCode; } /** * Returns last error message * * @return string * @access public */ function getErrorMsg() { return $this->errorMessage; } /** * Performs insert of given data (useful with small number of queries) * or stores it to perform multiple insert later (useful with large number of queries) * * @param Array $fields_hash * @param string $table * @param string $type * @param bool $insert_now * @return bool */ function doInsert($fields_hash, $table, $type = 'INSERT', $insert_now = true) { static $value_sqls = Array (); if ($insert_now) { $fields_sql = '`' . implode('`,`', array_keys($fields_hash)) . '`'; } $values_sql = ''; foreach ($fields_hash as $field_name => $field_value) { $values_sql .= $this->qstr($field_value) . ','; } // don't use preg here, as it may fail when string is too long $value_sqls[] = rtrim($values_sql, ','); $insert_result = true; if ($insert_now) { $insert_count = count($value_sqls); if (($insert_count > 1) && ($value_sqls[$insert_count - 1] == $value_sqls[$insert_count - 2])) { // last two records are the same array_pop($value_sqls); } $sql = strtoupper($type) . ' INTO `' . $table . '` (' . $fields_sql . ') VALUES (' . implode('),(', $value_sqls) . ')'; $insert_result = $this->ChangeQuery($sql); $value_sqls = Array (); } return $insert_result; } function doUpdate($fields_hash, $table, $key_clause) { if (!$fields_hash) return true; $fields_sql = ''; foreach ($fields_hash as $field_name => $field_value) { $fields_sql .= '`'.$field_name.'` = ' . $this->qstr($field_value) . ','; } // don't use preg here, as it may fail when string is too long $fields_sql = rtrim($fields_sql, ','); $sql = 'UPDATE `'.$table.'` SET '.$fields_sql.' WHERE '.$key_clause; return $this->ChangeQuery($sql); } /** * Allows to detect table's presense in database * * @param string $table_name * @return bool */ function TableFound($table_name) { static $table_found = Array(); if (!preg_match('/^'.preg_quote(TABLE_PREFIX, '/').'(.*)/', $table_name)) { $table_name = TABLE_PREFIX.$table_name; } if (!isset($table_found[$table_name])) { $table_found[$table_name] = $this->Query('SHOW TABLES LIKE "'.$table_name.'"'); } return $table_found[$table_name]; } /** * Returns query processing statistics * * @return Array */ function getQueryStatistics() { return Array ('time' => $this->_queryTime, 'count' => $this->_queryCount); } }