ConLite/conlib/db_mysqli.inc

528 Zeilen
16 KiB
PHP

<?php
/**
* Project:
* Contenido Content Management System
*
* Description:
* MySQLi database driver
*
* Requirements:
* @con_php_req 5
*
*
* @package Contenido database
* @version 0.3.1
* @author Boris Erdmann, Kristian Koehntopp
* @copyright four for business AG <www.4fb.de>
* @license http://www.contenido.org/license/LIZENZ.txt
* @link http://www.4fb.de
* @link http://www.contenido.org
* @since file available since contenido release <Contenido Version>
*
* {@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 <holger.librenz@4fb.de>
* @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();
}
}