'', 'user' => '', 'pass' => '', 'db' => ''); /** * Index of database server * * @var int * @access protected */ protected $serverIndex = 0; /** * Handle of currently processed recordset * * @var mysqli_result * @access protected */ protected $queryID = null; /** * Function to handle sql errors * * @var callable */ protected $errorHandler = ''; /** * Error code * * @var int * @access protected */ protected $errorCode = 0; /** * Error message * * @var string * @access protected */ protected $errorMessage = ''; /** * Defines if database connection * operations should generate debug * information * * @var bool * @access public */ public $debugMode = false; /** * Save query execution statistics * * @var bool * @access protected */ protected $_captureStatistics = false; /** * Last query to database * * @var string * @access public */ public $lastQuery = ''; /** * Total processed queries count * * @var int * @access protected */ protected $_queryCount = 0; /** * Total time, used for serving queries * * @var Array * @access protected */ protected $_queryTime = 0; /** * Indicates, that next database query could be cached, when memory caching is enabled * * @var bool * @access public */ public $nextQueryCachable = false; /** * The "no debugging" state of the SQL queries. * * @var boolean */ public $noDebuggingState = false; /** * For backwards compatibility with kDBLoadBalancer class * * @var bool * @access public */ public $nextQueryFromMaster = false; /** * Initializes connection class with * db type to used in future * * @param string $db_type * @param string $error_handler * @param int $server_index * @access public */ public function __construct($db_type, $error_handler = '', $server_index = 0) { if ( class_exists('kApplication') ) { // prevents "Fatal Error" on 2nd installation step (when database is empty) parent::__construct(); } $this->serverIndex = $server_index; if ( !$error_handler ) { $this->setErrorHandler(array(&$this, 'handleError')); } else { $this->setErrorHandler($error_handler); } $this->_captureStatistics = defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !(defined('ADMIN') && ADMIN); } /** * Set's custom error * * @param int $code * @param string $msg * @access protected */ protected function setError($code, $msg) { $this->errorCode = $code; $this->errorMessage = $msg; } /** * Checks if previous query execution raised an error. * * @return bool * @access public */ public function hasError() { return $this->errorCode != 0; } /** * 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 * @param bool $retry * * @return bool * @access public * @throws RuntimeException When connection failed. */ public function Connect($host, $user, $pass, $db, $retry = false) { $this->connectionParams = Array ('host' => $host, 'user' => $user, 'pass' => $pass, 'db' => $db); $this->setError(0, ''); // reset error $this->connectionID = mysqli_connect($host, $user, $pass, $db); $this->errorCode = mysqli_connect_errno(); if ( is_object($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 . '\''); } if ( !$this->hasError() ) { $this->connectionOpened = true; return true; } } $this->errorMessage = mysqli_connect_error(); $error_msg = 'Database connection failed, please check your connection settings.
Error (' . $this->errorCode . '): ' . $this->errorMessage; if ( (defined('IS_INSTALL') && IS_INSTALL) || $retry ) { trigger_error($error_msg, E_USER_WARNING); } else { $this->Application->redirectToMaintenance(); throw new RuntimeException($error_msg); } $this->connectionOpened = false; return false; } /** * Checks if connection to database is opened. * * @return bool * @access public */ public function connectionOpened() { return $this->connectionOpened; } /** * Setups the connection according given configuration. * * @param Array $config * @return bool * @access public */ public function setup($config) { if ( is_object($this->Application) ) { $this->debugMode = $this->Application->isDebugMode(); } return $this->Connect( $config['Database']['DBHost'], $config['Database']['DBUser'], $config['Database']['DBUserPassword'], $config['Database']['DBName'] ); } /** * Performs 3 reconnect attempts in case if connection to a DB was lost in the middle of script run (e.g. server restart) * * @return bool * @access protected */ protected function ReConnect() { $retry_count = 0; $connected = false; $this->connectionID->close(); while ( $retry_count < 3 ) { sleep(5); // wait 5 seconds before each reconnect attempt $connected = $this->Connect( $this->connectionParams['host'], $this->connectionParams['user'], $this->connectionParams['pass'], $this->connectionParams['db'], true ); if ( $connected ) { break; } $retry_count++; } return $connected; } /** * Shows error message from previous operation * if it failed * * @param string $sql * @param string $key_field * @param boolean|null $no_debug * @return bool * @access protected */ protected function showError($sql = '', $key_field = null, $no_debug = null) { static $retry_count = 0; if ( $no_debug === null ) { $no_debug = $this->noDebuggingState; } if ( !is_object($this->connectionID) ) { // no connection while doing mysql_query $this->errorCode = mysqli_connect_errno(); if ( $this->hasError() ) { $this->errorMessage = mysqli_connect_error(); $ret = $this->callErrorHandler($sql); if (!$ret) { exit; } } return false; } // checking if there was an error during last mysql_query $this->errorCode = $this->connectionID->errno; if ( $this->hasError() ) { $this->errorMessage = $this->connectionID->error; $ret = $this->callErrorHandler($sql); if ( ($this->errorCode == 2006 || $this->errorCode == 2013) && ($retry_count < 3) ) { // #2006 - MySQL server has gone away // #2013 - Lost connection to MySQL server during query $retry_count++; if ( $this->ReConnect() ) { return $this->Query($sql, $key_field, $no_debug); } } if (!$ret) { exit; } } else { $retry_count = 0; } return false; } /** * Sends db error to a predefined error handler * * @param $sql * @return bool * @access protected */ protected function callErrorHandler($sql) { return call_user_func($this->errorHandler, $this->errorCode, $this->errorMessage, $sql); } /** * Default error handler for sql errors * * @param int $code * @param string $msg * @param string $sql * @return bool * @access public */ public function handleError($code, $msg, $sql) { echo 'Processing SQL: ' . $sql . '
'; echo 'Error (' . $code . '): ' . $msg . '
'; return false; } /** * Returns first field of first line of recordset if query ok or false otherwise. * * @param string $sql * @param int $offset * @return string * @access public */ 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 string $sql * @param int $offset * @return Array * @access public */ 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 */ 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; } /** * Returns iterator for 1st column of a recordset or false in case of error. * * 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 bool|kMySQLQueryCol */ public function GetColIterator($sql, $key_field = null) { return $this->GetIterator($sql, $key_field, false, 'kMySQLQueryCol'); } /** * 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 * @param boolean|null $no_debug * @return Array * @access public */ public function Query($sql, $key_field = null, $no_debug = null) { if ( $no_debug === null ) { $no_debug = $this->noDebuggingState; } if ( !$no_debug ) { $this->_queryCount++; } $this->lastQuery = $sql; // set 1st checkpoint: begin $start_time = $this->_captureStatistics ? microtime(true) : 0; // set 1st checkpoint: end $this->setError(0, ''); // reset error $this->queryID = $this->connectionID->query($sql); if ( is_object($this->queryID) ) { $ret = Array (); if ( isset($key_field) ) { while ( $row = $this->queryID->fetch_assoc() ) { $ret[$row[$key_field]] = $row; } } else { while ( $row = $this->queryID->fetch_assoc() ) { $ret[] = $row; } } $this->Destroy(); // set 2nd checkpoint: begin if ( $this->_captureStatistics ) { $query_time = microtime(true) - $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 return $ret; } else { // set 2nd checkpoint: begin if ( $this->_captureStatistics ) { $this->_queryTime += microtime(true) - $start_time; } // set 2nd checkpoint: end } return $this->showError($sql, $key_field, $no_debug); } /** * Returns iterator to a recordset, produced from running $sql query. * * Queries db with $sql query supplied and returns kMySQLQuery iterator or false in case of error. * 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 * @param boolean|null $no_debug * @param string $iterator_class * @return kMySQLQuery|bool * @access public */ public function GetIterator($sql, $key_field = null, $no_debug = null, $iterator_class = 'kMySQLQuery') { if ( $no_debug === null ) { $no_debug = $this->noDebuggingState; } if ( !$no_debug ) { $this->_queryCount++; } $this->lastQuery = $sql; // set 1st checkpoint: begin $start_time = $this->_captureStatistics ? microtime(true) : 0; // set 1st checkpoint: end $this->setError(0, ''); // reset error $this->queryID = $this->connectionID->query($sql); if ( is_object($this->queryID) ) { /** @var kMySQLQuery $ret */ $ret = new $iterator_class($this->queryID, $key_field); // set 2nd checkpoint: begin if ( $this->_captureStatistics ) { $query_time = microtime(true) - $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 return $ret; } else { // set 2nd checkpoint: begin if ( $this->_captureStatistics ) { $this->_queryTime += microtime(true) - $start_time; } // set 2nd checkpoint: end } return $this->showError($sql, $key_field, $no_debug); } /** * Free memory used to hold recordset handle. * * @access public */ public function Destroy() { $this->queryID->free(); unset($this->queryID); } /** * Performs sql query, that will change database content. * * @param string $sql * @return bool * @access public */ public function ChangeQuery($sql) { $this->Query($sql); return !$this->hasError(); } /** * Returns auto increment field value from insert like operation if any, zero otherwise. * * @return int * @access public */ public function getInsertID() { return $this->connectionID->insert_id; } /** * Returns row count affected by last query. * * @return int * @access public */ public function getAffectedRows() { return $this->connectionID->affected_rows; } /** * Returns LIMIT sql clause part for specific db. * * @param int $offset * @param int $rows * @return string * @access public */ public function getLimitClause($offset, $rows) { if ( !($rows > 0) ) { return ''; } return 'LIMIT ' . $offset . ',' . $rows; } /** * If it's a string, adds quotes and backslashes. Otherwise returns as-is. * * @param mixed $string * @return string * @access public */ public 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 "'" . $this->connectionID->real_escape_string($string) . "'"; } /** * Calls "qstr" function for each given array element. * * @param Array $array * @param string $function * @return Array */ public function qstrArray($array, $function = 'qstr') { return array_map(Array (&$this, $function), $array); } /** * Escapes string. * * @param mixed $string * @return string * @access public */ public function escape($string) { if ( is_null($string) ) { return 'NULL'; } $string = $this->connectionID->real_escape_string($string); // 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 occurred. * * @return int * @access public */ public function getErrorCode() { return $this->errorCode; } /** * Returns last error message. * * @return string * @access public */ 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 * @access public */ public 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) . ')'; $value_sqls = Array (); // reset before query to prevent repeated call from error handler to insert 2 records instead of 1 $insert_result = $this->ChangeQuery($sql); } return $insert_result; } /** * Update given field values to given record using $key_clause. * * @param Array $fields_hash * @param string $table * @param string $key_clause * @return bool * @access public */ public 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 presence in database. * * @param string $table_name * @param bool $force * @return bool * @access public */ public function TableFound($table_name, $force = false) { static $table_found = false; if ( $table_found === false ) { $table_found = array_flip($this->GetCol('SHOW TABLES')); } if ( !preg_match('/^' . preg_quote(TABLE_PREFIX, '/') . '(.*)/', $table_name) ) { $table_name = TABLE_PREFIX . $table_name; } if ( $force ) { if ( $this->Query('SHOW TABLES LIKE ' . $this->qstr($table_name)) ) { $table_found[$table_name] = 1; } else { unset($table_found[$table_name]); } } return isset($table_found[$table_name]); } /** * Returns query processing statistics. * * @return Array * @access public */ public function getQueryStatistics() { return Array ('time' => $this->_queryTime, 'count' => $this->_queryCount); } /** * Get status information from SHOW STATUS in an associative array. * * @param string $which * @return Array * @access public */ public function getStatus($which = '%') { $status = Array (); $records = $this->Query('SHOW STATUS LIKE "' . $which . '"'); foreach ($records as $record) { $status[ $record['Variable_name'] ] = $record['Value']; } return $status; } /** * Get slave replication lag. It will only work if the DB user has the PROCESS privilege. * * @return int * @access public */ public function getSlaveLag() { try { $rows = $this->Query('SHOW SLAVE STATUS'); } catch ( RuntimeException $e ) { // When "SUPER" or "REPLICATION CLIENT" permission is missing. return 0; } // On the silenced error OR database server isn't configured for a replication. if ( $rows === false || count($rows) !== 1 ) { return 0; } $row = reset($rows); // When slave is too busy catching up with a master we'll get a NULL/empty string here. return is_numeric($row['Seconds_Behind_Master']) ? $row['Seconds_Behind_Master'] : false; } /** * Sets an error handler. * * @param callable $error_handler Error handler. * * @return void */ public function setErrorHandler(callable $error_handler) { $this->errorHandler = $error_handler; } } class kDBConnectionDebug extends kDBConnection { protected $_profileSQLs = false; /** * Info about this database connection to show in debugger report * * @var string * @access protected */ protected $serverInfoLine = ''; /** * Initializes connection class with * db type to used in future * * @param string $db_type * @param string $error_handler * @param int $server_index * @access public */ public function __construct($db_type, $error_handler = '', $server_index = 0) { parent::__construct($db_type, $error_handler, $server_index); $this->_profileSQLs = defined('DBG_SQL_PROFILE') && DBG_SQL_PROFILE; } /** * 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 * @param bool $force_new * @param bool $retry * @return bool * @access public */ public function Connect($host, $user, $pass, $db, $force_new = false, $retry = false) { if ( defined('DBG_SQL_SERVERINFO') && DBG_SQL_SERVERINFO ) { $this->serverInfoLine = $this->serverIndex . ' (' . $host . ')'; } return parent::Connect($host, $user, $pass, $db, $force_new, $retry); } /** * 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 * @param boolean|null $no_debug * @return Array * @access public */ public function Query($sql, $key_field = null, $no_debug = null) { if ( $no_debug === null ) { $no_debug = $this->noDebuggingState; } if ( $no_debug ) { return parent::Query($sql, $key_field, $no_debug); } global $debugger; $this->_queryCount++; $this->lastQuery = $sql; // set 1st checkpoint: begin if ( $this->_profileSQLs ) { $queryID = $debugger->generateID(); $debugger->profileStart('sql_' . $queryID, $debugger->formatSQL($sql)); } // set 1st checkpoint: end $this->setError(0, ''); // reset error $this->queryID = $this->connectionID->query($sql); if ( is_object($this->queryID) ) { $ret = Array (); if ( isset($key_field) ) { while ( $row = $this->queryID->fetch_assoc() ) { $ret[$row[$key_field]] = $row; } } else { while ( $row = $this->queryID->fetch_assoc() ) { $ret[] = $row; } } // set 2nd checkpoint: begin if ( $this->_profileSQLs ) { $current_element = current($ret); $first_cell = count($ret) == 1 && count($current_element) == 1 ? current($current_element) : 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, $this->serverInfoLine); $debugger->profilerAddTotal('sql', 'sql_' . $queryID); $this->nextQueryCachable = false; } // set 2nd checkpoint: end $this->Destroy(); return $ret; } else { // set 2nd checkpoint: begin if ( $this->_profileSQLs ) { $debugger->profileFinish('sql_' . $queryID, null, null, $this->getAffectedRows(), null, $this->_queryCount, $this->nextQueryCachable, $this->serverInfoLine); $debugger->profilerAddTotal('sql', 'sql_' . $queryID); $this->nextQueryCachable = false; } // set 2nd checkpoint: end } return $this->showError($sql, $key_field); } /** * Returns iterator to a recordset, produced from running $sql query. * * Queries db with $sql query supplied and returns kMySQLQuery iterator or false in case of error. * 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 * @param boolean|null $no_debug * @param string $iterator_class * @return kMySQLQuery|bool * @access public */ public function GetIterator($sql, $key_field = null, $no_debug = null, $iterator_class = 'kMySQLQuery') { if ( $no_debug === null ) { $no_debug = $this->noDebuggingState; } if ( $no_debug ) { return parent::GetIterator($sql, $key_field, $no_debug, $iterator_class); } global $debugger; $this->_queryCount++; $this->lastQuery = $sql; // set 1st checkpoint: begin if ( $this->_profileSQLs ) { $queryID = $debugger->generateID(); $debugger->profileStart('sql_' . $queryID, $debugger->formatSQL($sql)); } // set 1st checkpoint: end $this->setError(0, ''); // reset error $this->queryID = $this->connectionID->query($sql); if ( is_object($this->queryID) ) { /** @var kMySQLQuery $ret */ $ret = new $iterator_class($this->queryID, $key_field); // set 2nd checkpoint: begin if ( $this->_profileSQLs ) { $current_row = $ret->current(); if ( count($ret) == 1 && $ret->fieldCount() == 1 ) { if ( is_array($current_row) ) { $first_cell = current($current_row); } else { $first_cell = $current_row; } } else { $first_cell = 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, $this->serverInfoLine); $debugger->profilerAddTotal('sql', 'sql_' . $queryID); $this->nextQueryCachable = false; } // set 2nd checkpoint: end return $ret; } else { // set 2nd checkpoint: begin if ( $this->_profileSQLs ) { $debugger->profileFinish('sql_' . $queryID, null, null, $this->getAffectedRows(), null, $this->_queryCount, $this->nextQueryCachable, $this->serverInfoLine); $debugger->profilerAddTotal('sql', 'sql_' . $queryID); $this->nextQueryCachable = false; } // set 2nd checkpoint: end } return $this->showError($sql, $key_field); } } class kMySQLQuery implements Iterator, Countable, SeekableIterator { /** * Current index in recordset * * @var int * @access protected */ protected $position = -1; /** * Query resource * * @var mysqli_result * @access protected */ protected $result; /** * Field to act as key in a resulting array * * @var string * @access protected */ protected $keyField = null; /** * Data in current row of recordset * * @var Array * @access protected */ protected $rowData = Array (); /** * Row count in a result * * @var int * @access protected */ protected $rowCount = 0; /** * Creates new instance of a class * * @param mysqli_result $result * @param null|string $key_field */ public function __construct(mysqli_result $result, $key_field = null) { $this->result = $result; $this->keyField = $key_field; $this->rowCount = $this->result->num_rows; $this->rewind(); } /** * Moves recordset pointer to first element * * @return void * @access public * @implements Iterator::rewind */ public function rewind() { $this->seek(0); } /** * Returns value at current position * * @return mixed * @access public * @implements Iterator::current */ function current() { return $this->rowData; } /** * Returns key at current position * * @return mixed * @access public * @implements Iterator::key */ function key() { return $this->keyField ? $this->rowData[$this->keyField] : $this->position; } /** * Moves recordset pointer to next position * * @return void * @access public * @implements Iterator::next */ function next() { $this->seek($this->position + 1); } /** * Detects if current position is within recordset bounds * * @return bool * @access public * @implements Iterator::valid */ public function valid() { return $this->position < $this->rowCount; } /** * Counts recordset rows * * @return int * @access public * @implements Countable::count */ public function count() { return $this->rowCount; } /** * Counts fields in current row * * @return int * @access public */ public function fieldCount() { return count($this->rowData); } /** * Moves cursor into given position within recordset * * @param int $position * @throws OutOfBoundsException * @access public * @implements SeekableIterator::seek */ public function seek($position) { if ( $this->position == $position ) { return; } $this->position = $position; if ( $this->valid() ) { $this->result->data_seek($this->position); $this->rowData = $this->result->fetch_assoc(); } /*if ( !$this->valid() ) { throw new OutOfBoundsException('Invalid seek position (' . $position . ')'); }*/ } /** * Returns first recordset row * * @return Array * @access public */ public function first() { $this->seek(0); return $this->rowData; } /** * Closes recordset and freese memory * * @return void * @access public */ public function close() { $this->result->free(); unset($this->result); } /** * Frees memory when object is destroyed * * @return void * @access public */ public function __destruct() { $this->close(); } /** * Returns all keys * * @return Array * @access public */ public function keys() { $ret = Array (); foreach ($this as $key => $value) { $ret[] = $key; } return $ret; } /** * Returns all values * * @return Array * @access public */ public function values() { $ret = Array (); foreach ($this as $value) { $ret[] = $value; } return $ret; } /** * Returns whole recordset as array * * @return Array * @access public */ public function toArray() { $ret = Array (); foreach ($this as $key => $value) { $ret[$key] = $value; } return $ret; } } class kMySQLQueryCol extends kMySQLQuery { /** * Returns value at current position * * @return mixed * @access public * @implements Iterator::current */ function current() { return reset($this->rowData); } /** * Returns first column of first recordset row * * @return string * @access public */ public function first() { $this->seek(0); return reset($this->rowData); } }