* @license http://www.contenido.org/license/LIZENZ.txt * @link http://www.4fb.de * @link http://www.contenido.org * @since file available since contenido release * * {@internal * created 2000-01-01 * modified 2008-07-04, bilal arslan, added security fix * modified 2009-10-29, Murat Purc, removed deprecated functions (PHP 5.3 ready) extended DB_Sql_Abstract, added/optimized some functioms and some formatting * modified 2009-12-29, Murat Purc, replaced is_resource() against mysqli compatible check [#CON-290] * modified 2011-03-03, Murat Purc, Some redesign and improvements (partial adaption to PHP 5 and extending DB_Sql_Abstract). * modified 2011-03-13, Murat Purc, Cleanup and documentation. * modified 2011-04-22, Murat Purc, Connect to DB server without database and more * readable connection settings. * * $Id$: * }} * */ if (!defined('CON_FRAMEWORK')) { die('Illegal call'); } class DB_Sql extends DB_Sql_Abstract { protected $_aDataTypes = array( 0 => 'decimal', 1 => 'tinyint', 2 => 'smallint', 3 => 'int', 4 => 'float', 5 => 'double', 7 => 'timestamp', 8 => 'bigint', 9 => 'mediumint', 10 => 'date', 11 => 'time', 12 => 'datetime', 13 => 'year', 252 => 'blob', // text, blob, tinyblob,mediumblob, etc... 253 => 'string', // varchar and char 254 => 'enum', ); /** * Constructor. * * @param array $options Optional assoziative options */ public function __construct(array $options = array()) { $options = array_merge($options, array( 'type' => 'mysqli', )); parent::__construct($options); } /** * @see DB_Sql_Abstract::_connect() */ protected function _connect() { // feasible connection values are: // - $options['connection']['host'] (string) Hostname or ip // - $options['connection']['database'] (string) Database name // - $options['connection']['user'] (string) User name // - $options['connection']['password'] (string) User password // - $options['connection']['options'] (array) Optional, MySQLi options array // - $options['connection']['socket'] (int) Optional, socket // - $options['connection']['port'] (int) Optional, port // - $options['connection']['flags'] (int) Optional, flags // see http://www.php.net/manual/en/mysqli.real-connect.php // extract($this->_aDbCfg['connection']); if (!extension_loaded('mysqli')) { $this->halt('MySQLi _connect() extension not loaded!'); return null; } // PHP 8.1 fix $driver = new mysqli_driver(); $driver->report_mode = MYSQLI_REPORT_OFF; $dbh = mysqli_init(); //print_r($dbh); if (!$dbh) { $this->halt('MySQLi _connect() Init failed'); return null; } $aCon = $this->_aDbCfg['connection']; if (!isset($aCon['host']) || !isset($aCon['user']) || !isset($aCon['password'])) { $this->halt('MySQLi _connect() Connection settings not complete'); return null; } // set existing option flags if (isset($aCon['options']) && is_array($aCon['options'])) { foreach ($aCon['options'] as $optKey => $optVal) { mysqli_options($dbh, $optKey, $optVal); } } if (($iPos = strpos($aCon['host'], ':')) !== false) { list($aCon['host'], $aCon['port']) = explode(':', $aCon['host']); } else { $aCon['port'] = null; } if (!isset($aCon['socket'])) { $aCon['socket'] = null; } if (!isset($aCon['flags'])) { $aCon['flags'] = MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT; } if (!isset($aCon['database'])) { $aCon['database'] = null; } $res = mysqli_real_connect( $dbh, $aCon['host'], $aCon['user'], $aCon['password'], $aCon['database'], $aCon['port'], $aCon['socket'], $aCon['flags'] ); if (!empty($aCon['charset'])) { mysqli_set_charset($dbh, $aCon['charset']); } else { mysqli_set_charset($dbh, 'utf8'); } //echo mysqli_character_set_name($dbh); if ($res && $dbh && $aCon['database']) { if (!@mysqli_select_db($dbh, $aCon['database'])) { $this->halt('MySQLi _connect() Cannot use database ' . $aCon['database']); return null; } $this->Database = $aCon['database']; } $this->User = $aCon['user']; return $dbh; } /** * Discard the query result */ public function free() { if (is_object($this->Query_ID)) { mysqli_free_result($this->Query_ID); } $this->Query_ID = 0; } /** * @see DB_Sql_Abstract::_query() */ protected function _query($sQuery) { $this->Query_ID = mysqli_query($this->Link_ID, $sQuery); $this->Row = 0; $this->Errno = $this->_getErrorNumber(); $this->Error = $this->_getErrorMessage(); if (!$this->Query_ID) { $this->halt($sQuery); } } /** * @see DB_Sql_Abstract::next_record() */ public function next_record(): bool|int { if (!$this->Query_ID instanceof mysqli_result) { return false; } $this->Record = mysqli_fetch_array($this->Query_ID, MYSQLI_BOTH); $this->Row += 1; $this->Errno = $this->_getErrorNumber(); $this->Error = $this->_getErrorMessage(); $stat = is_array($this->Record); if (!$stat && $this->Auto_Free) { $this->free(); } return $stat; } /** * @see DB_Sql_Abstract::seek() */ public function seek($pos = 0) { $status = mysqli_data_seek($this->Query_ID, $pos); if ($status) { $this->Row = $pos; } else { $this->halt("seek($pos) failed: result has " . $this->num_rows() . " rows."); // half assed attempt to save the day, but do not consider this // documented or even desireable behaviour. mysqli_data_seek($this->Query_ID, $this->num_rows()); $this->Row = $this->num_rows(); return 0; } return 1; } /** * @see DB_Sql_Abstract::lock() */ public function lock($table, $mode = 'write') { if ($this->_bNolock == true) { return true; } $query = 'LOCK TABLES '; if (is_array($table)) { while (list ($key, $value) = each($table)) { if (!is_int($key)) { // texts key are "read", "read local", "write", "low priority write" $query .= "$value $key, "; } else { $query .= "$value $mode, "; } } $query = substr($query, 0, -2); } else { $query .= "$table $mode"; } $res = $this->query($query); if (!$res) { $this->halt('lock() failed.'); return 0; } return $res; } /** * @see DB_Sql_Abstract::unlock() */ public function unlock() { if ($this->_bNolock == true) { return true; } $res = $this->query('UNLOCK TABLES'); if (!$res) { $this->halt('unlock() failed.'); } return $res; } /** * @see DB_Sql_Abstract::affected_rows() */ public function affected_rows() { return ($this->Link_ID) ? mysqli_affected_rows($this->Link_ID) : 0; } /** * @see DB_Sql_Abstract::num_rows() */ public function num_rows() { return ($this->Query_ID) ? mysqli_num_rows($this->Query_ID) : 0; } /** * @see DB_Sql_Abstract::num_fields() */ public function num_fields() { return ($this->Query_ID) ? mysqli_num_fields($this->Query_ID) : 0; } /** * @see DB_Sql_Abstract::nextid() */ public function nextid($seq_name) { $this->connect(); if ($this->lock($this->Seq_Table)) { /* get sequence number (locked) and increment */ $q = sprintf("SELECT nextid FROM `%s` WHERE seq_name = '%s'", $this->Seq_Table, $seq_name); $id = mysqli_query($this->Link_ID, $q); $res = mysqli_fetch_array($id, MYSQLI_BOTH); /* No current value, make one */ if (!is_array($res)) { $currentid = 0; $q = sprintf("INSERT INTO `%s` VALUES('%s', %s)", $this->Seq_Table, $seq_name, $currentid); $id = mysqli_query($this->Link_ID, $q); } else { $currentid = $res["nextid"]; } $nextid = $currentid + 1; $q = sprintf("UPDATE `%s` set nextid = '%s' WHERE seq_name = '%s'", $this->Seq_Table, $nextid, $seq_name); $id = mysqli_query($this->Link_ID, $q); $this->unlock(); } else { $this->halt('Cannot lock ' . $this->Seq_Table . ' - has it been created?'); return 0; } return $nextid; } /** * @see DB_Sql_Abstract::disconnect() */ public function disconnect() { //$this->_debug("Debug: Disconnecting $this->Link_ID..."); if (is_resource($this->Link_ID)) { mysqli_close($this->Link_ID); $this->_removeConnection($this->Link_ID); } $this->Link_ID = 0; $this->Query_ID = 0; } /** * @see DB_Sql_Abstract::_metaData() * * Due to compatibility problems with Table we changed the behavior * of metadata(); * depending on $full, metadata returns the following values: * * - full is false (default): * $result[]: * [0]["table"] table name * [0]["name"] field name * [0]["type"] field type * [0]["len"] field length * [0]["flags"] field flags * * - full is true * $result[]: * ["num_fields"] number of metadata records * [0]["table"] table name * [0]["name"] field name * [0]["type"] field type * [0]["len"] field length * [0]["flags"] field flags * ["meta"][field name] index of field named "field name" * This last one could be used if you have a field name, but no index. * Test: if (isset($result['meta']['myfield'])) { ... */ protected function _metaData($table = '', $full = false) { $count = 0; $id = 0; $res = array(); // if no $table specified, assume that we are working with a query // result if (!empty($table)) { $this->connect(); $id = mysqli_query($this->Link_ID, sprintf("SELECT * FROM `%s` LIMIT 1", $table)); if (!$id) { $this->halt('Metadata query failed.'); return false; } } else { //var_dump($this->Query_ID); $id = $this->Query_ID; if (!$id) { $this->halt('No query specified.'); return false; } } //$count = mysqli_num_fields($id); /* // made this IF due to performance (one if is faster than $count if's) for ($i = 0; $i < $count; $i ++) { $finfo = mysqli_fetch_field($id); $res[$i]['table'] = $finfo->table; $res[$i]['name'] = $finfo->name; $res[$i]['type'] = $this->_aDataTypes[$finfo->type]; $res[$i]['len'] = $finfo->max_length; $res[$i]['flags'] = $finfo->flags; if ($full) { $res['meta'][$res[$i]['name']] = $i; } } */ $count = 0; while ($finfo = $id->fetch_field()) { //rint_r($finfo); $res[$count]['table'] = $finfo->table; $res[$count]['name'] = $finfo->name; $res[$count]['type'] = $this->_aDataTypes[$finfo->type]; $res[$count]['len'] = $finfo->length; $res[$count]['flags'] = $finfo->flags; if ($full) { $res['meta'][$res[$count]['name']] = $count; } $count++; } if ($full) { $res['num_fields'] = $count + 1; } // free the result only if we were called on a table if ($table) { mysqli_free_result($id); } return (count($res) > 0) ? $res : FALSE; } /** * @see DB_Sql_Abstract::escape() */ public function escape($sString) { if(is_null($sString)) { $sString = ''; } $sResult = ''; if (is_resource($this->Link_ID) || $this->connect()) { $sResult = mysqli_real_escape_string($this->Link_ID, $sString); } return $sResult; } /** * @see DB_Sql_Abstract::_tableNames() */ protected function _tableNames() { $return = array(); $this->connect(); $h = @mysqli_query($this->Link_ID, 'SHOW TABLES'); $i = 0; if (isset($h) && @mysqli_num_rows($h) > 0) { while ($info = mysqli_fetch_row($h)) { $return[$i]['table_name'] = $info[0]; $return[$i]['tablespace_name'] = $this->Database; $return[$i]['database'] = $this->Database; $i++; } mysqli_free_result($h); } return $return; } /** * @see DB_Sql_Abstract::_serverInfo() */ protected function _serverInfo() { $arr['description'] = mysqli_get_server_info($this->Link_ID); return $arr; } /** * @see DB_Sql_Abstract::_getErrorMessage() */ protected function _getErrorMessage() { if ($this->Link_ID) { return @mysqli_error($this->Link_ID); } else { return @mysqli_connect_error(); } } /** * @see DB_Sql_Abstract::_getErrorNumber() */ protected function _getErrorNumber() { if ($this->Link_ID) { return @mysqli_errno($this->Link_ID); } else { return @mysqli_connect_errno(); } } /** * This method equates to mysqli_fetch_object(). It returns the current * result set as object or null if no result set is left. If optional * param $sClassName is set, the result object is an instance of class * $sClassName. * * @return object|null * * @author Holger Librenz * @version 1.0 */ public function getResultObject($sClassName = null) { $oResult = null; if (is_resource($this->Link_ID) && is_resource($this->Query_ID)) { if ($sClassName == null) { $oResult = mysqli_fetch_object($this->Query_ID); } else { $oResult = mysqli_fetch_object($this->Query_ID, $sClassName); } } return $oResult; } public function getServerInfo() { return mysqli_get_server_info($this->Link_ID); } public function getClientEncoding() { $oCharSet = mysqli_get_charset($this->Link_ID); return $oCharSet->charset; } public function getClientInfo() { return mysqli_get_client_info(); } }