View Issue Details

IDProjectCategoryView StatusLast Update
0000504XMB1Bugspublic2018-01-18 06:09
Reportermiqrogroove Assigned Tomiqrogroove  
PrioritynormalSeveritycrashReproducibilityhave not tried
Status closedResolutionfixed 
Product Version1.9.8 SP2 
Target Version1.9.11.15Fixed in Version1.9.11.15 
Summary0000504: XMB Incompatible with PHP 7 Database Extensions
DescriptionThe mysql extension for PHP no longer exists as of version 7.0, making all versions of XMB totally incompatible.
Additional Informationsee http://php.net/manual/en/mysql.php
TagsNo tags attached.
MySQL Version
PHP Version7.0
Web Server
Browser
Flags
Original Reporter
SVN Revision2729

Activities

Xian

2017-09-16 20:53

reporter   ~0000335

New mysqli.php file for PHP 7

Included in XMB 1.9.12.4.BETA
mysqli.php (13,277 bytes)   
<?php
/**
 * eXtreme Message Board
 * XMB 1.9.12 BETA
 *
 * Developed And Maintained By The XMB Group
 * Copyright (c) 2001-2017, The XMB Group
 * http://www.xmbforum2.com/
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 **/

if (!defined('IN_CODE')) {
	header('HTTP/1.0 403 Forbidden');
	exit("Not allowed to run this file directly.");
}

class dbstuff {
	var $querynum		= 0;
	var $querylist  = array();
	var $querytimes = array();
	var $link				= '';
	var $db					= '';
	var $duration		= 0;
	var $timer			= 0;
	var $errcallb		= 'xmb_mysqli_error';
	var $last_id		= 0;
	var $last_rows  = 0;

	/**
	 * Establishes a connection to the MySQL server.
	 *
	 * @param string $dbhost
	 * @param string $dbuser
	 * @param string $dbpw
	 * @param string $dbname
	 * @param bool   $pconnect Keep the connection open after the script ends.
	 * @param bool   $force_db Generate a fatal error if the $dbname database doesn't exist on the server.
	 * @param bool   $new_link Use to connect to a second database on the same server at the same time.
	 */
	function connect($dbhost='localhost', $dbuser, $dbpw, $dbname, $pconnect=FALSE, $force_db=FALSE, $new_link=FALSE) {

		if ($pconnect) {
			$this->link = mysqli_connect("p:$dbhost", $dbuser, $dbpw, $new_link);
		} else {
			$this->link = mysqli_connect($dbhost, $dbuser, $dbpw, $new_link);
		}

		if (FALSE === $this->link) {
			echo '<h3>Database connection error!</h3>';
			echo 'A connection to the Database could not be established.<br />';
			echo 'Please check your username, password, database name and host.<br />';
			echo 'Also make sure <i>config.php</i> is rightly configured!<br /><br />';
			$sql = '';
			$this->panic($sql);
		}

		unset($GLOBALS['dbhost'], $GLOBALS['dbuser'], $GLOBALS['dbpw']);

		return $this->select_db($dbname, $force_db);
	}

	/**
	 * Sets the name of the database to be used on this connection.
	 *
	 * @param string $database The full name of the MySQL database.
	 * @param bool $force Optional. Specifies error mode. Dies if true.
	 * @return bool TRUE on success, FALSE on failure with !$force.
	 */
	function select_db($database, $force = TRUE) {
		if (mysqli_select_db($this->link, $database)) {
			$this->db = $database;
			return TRUE;
		}
		if ($force) {
			$sql = "USE $database -- XMB couldn't find the database! Please reconfigure the config.php file.";
			$this->panic($sql);
		} else {
			return FALSE;
		}
	}

	/**
	 * Searches for an accessible database containing the XMB settings table.
	 *
	 * @param string $tablepre The settings table name prefix.
	 * @return bool
	 */
	function find_database($tablepre) {
		$dbs = mysql_list_dbs($this->link);
		while($db = $this->fetch_array($dbs)) {
			if ('information_schema' == $db['Database']) {
				continue;
			}
			$q = $this->query("SHOW TABLES FROM `{$db['Database']}`");

			while ($table = $this->fetch_array($q)) {
				if ($tablepre.'settings' == $table[0]) {
					if (mysqli_select_db($this->link, $db['Database'])) {
						$this->db = $db['Database'];
						return TRUE;
					}
				}
			}
		}
		return FALSE;
	}

	function error() {
		return mysqli_error($this->link);
	}

	function free_result($query) {
		set_error_handler($this->errcallb);
		$return = mysqli_free_result($query);
		restore_error_handler();
		return $return;
	}

	function fetch_array($query, $type=MYSQLI_ASSOC) {
		set_error_handler($this->errcallb);
		$array = mysqli_fetch_array($query, $type);
		restore_error_handler();
		return $array;
	}

	function field_name($query, $field) {
		set_error_handler($this->errcallb);
		$return = mysqli_fetch_field_direct($query, $field);
		restore_error_handler();
		return $return;
	}

	/**
	 * Returns the length of a field as specified in the database schema.
	 *
	 * @since 1.9.11.13
	 * @param resource $query The result of a query.
	 * @param int $field The field_offset starts at 0.
	 * @return int
	 */
	function field_len($query, $field) {
		set_error_handler($this->errcallb);
		$return = mysqli_fetch_lengths($query, $field);
		restore_error_handler();
		return $return;
	}

	function panic(&$sql) {
		if (!headers_sent()) {
			header('HTTP/1.0 500 Internal Server Error');
		}

		// Check that we actually made a connection
		if ($this->link === FALSE) {
			$error = mysqli_error();
			$errno = mysqli_errno();
		} else {
			$error = mysqli_error($this->link);
			$errno = mysqli_errno($this->link);
		}

		if (DEBUG And (!defined('X_SADMIN') Or X_SADMIN)) {
			require_once(ROOT.'include/validate.inc.php');
			echo '<pre>MySQL encountered the following error: '.cdataOut($error)."(errno = ".$errno.")\n<br />";
			if ($sql != '') {
				echo 'In the following query: <em>'.cdataOut($sql);
			}
			echo '</em></pre>';
		} else {
			echo "<pre>The system has failed to process your request. If you're an administrator, please set the DEBUG flag to true in config.php.</pre>";
		}
		if (LOG_MYSQL_ERRORS) {
			$log = "MySQL encountered the following error:\n$error\n(errno = $errno)\n";
			if (strlen($sql) > 0) {
				if (1153 == $errno and strlen($sql) > 16000) {
					$log .= "In the following query (log truncated):\n" . substr($sql, 0, 16000);
				} else {
					$log .= "In the following query:\n$sql";
				}
			}
			if (!ini_get('log_errors')) {
				ini_set('log_errors', TRUE);
				ini_set('error_log', 'error_log');
			}
			error_log($log);
		}
		exit;
	}

	/**
	 * Can be used to make any expression query-safe, but see next function.
	 *
	 * Example: $db->query('UPDATE a SET b = "'.$db->escape("Hello, my name is $rawinput").'"');
	 *
	 * @param string $rawstring
	 * @return string
	 */
	function escape($rawstring) {
		set_error_handler($this->errcallb);
		$return = mysqli_real_escape_string($this->link, $rawstring);
		restore_error_handler();
		return $return;
	}

	/**
	 * Preferred for performance when escaping any string variable.
	 *
	 * Note this only works when the raw value can be discarded.
	 *
	 * Example:
	 *  $db->escape_fast($rawinput);
	 *  $db->query('UPDATE a SET b = "Hello, my name is '.$rawinput.'"');
	 *
	 * @since 1.9.11.12
	 * @param string $sql Read/Write Variable
	 */
	function escape_fast(&$sql) {
		set_error_handler($this->errcallb);
		$sql = mysqli_real_escape_string($this->link, $sql);
		restore_error_handler();
	}

	function like_escape($rawstring) {
		set_error_handler($this->errcallb);
		$search = array('%', '_');
		$replace   = array('\%', '\_');
		return str_replace($search, $replace, $rawstring);
	}

	/**
	 * Executes a MySQL Query
	 *
	 * @param string $sql Unique MySQL query (multiple queries are not supported). The query string should not end with a semicolon.
	 * @param bool $panic XMB will die and use dbstuff::panic() in case of any MySQL error unless this param is set to FALSE.
	 * @return mixed Returns a MySQL resource or a bool, depending on the query type and error status.
	 */
	function query($sql, $panic = TRUE) {
		$this->start_timer();
		$query = mysqli_query($this->link, $sql);
		if (FALSE === $query and $panic) {
			$this->panic($sql);
		}
		$this->querytimes[] = $this->stop_timer();
		$this->querynum++;
		if (DEBUG) {
			if (LOG_MYSQL_ERRORS) {
				$this->last_id = mysqli_insert_id($this->link);
				$this->last_rows = mysqli_affected_rows($this->link);

				$query2 = mysqli_query($this->link, 'SHOW COUNT(*) WARNINGS');
				if (($warnings = mysqli_data_seek($query2, 0)) > 0) {
					if (!ini_get('log_errors')) {
						ini_set('log_errors', TRUE);
						ini_set('error_log', 'error_log');
					}
					if (strlen($sql) > 16000) {
						$output = "MySQL generated $warnings warnings in the following query (log truncated):\n" . substr($sql, 0, 16000) . "\n";
					} else {
						$output = "MySQL generated $warnings warnings in the following query:\n$sql\n";
					}
					$query3 = mysqli_query($this->link, 'SHOW WARNINGS');
					while ($row = mysqli_fetch_array($query3, MYSQLI_ASSOC)) {
						$output .= var_export($row, TRUE)."\n";
					}
					error_log($output);
					mysqli_free_result($query3);
				}
				mysqli_free_result($query2);
			}
			if (!defined('X_SADMIN') or X_SADMIN) {
				$this->querylist[] = $sql;
			}
		}
		return $query;
	}

	/**
	 * Sends a MySQL query without fetching the result rows.
	 */
	function unbuffered_query($sql, $panic = TRUE) {
		$this->start_timer();
		$query = mysqli_real_query($this->link, $sql);
		if (FALSE === $query and $panic) {
			$this->panic($sql);
		}
		$this->querynum++;
		if (DEBUG and (!defined('X_SADMIN') or X_SADMIN)) {
			$this->querylist[] = $sql;
		}
		$this->querytimes[] = $this->stop_timer();
		return $query;
	}

	function fetch_tables($dbname = NULL) {
		if ($dbname == NULL) {
			$dbname = $this->db;
		}
		$this->select_db($dbname);

		$q = $this->query("SHOW TABLES");
		while($table = $this->fetch_array($q, MYSQLI_NUM)) {
			$array[] = $table[0];
		}
		return $array;
	}

	/**
	* Replaces result
	*
	* You cannot use mysql_result() as of PHP 7
	* This function replaces that function.
	* By Xian
	*/
	function result($res,$row=0,$col=0) {
		$numrows = mysqli_num_rows($res);
			if ($numrows && $row <= ($numrows-1) && $row >=0) {
				mysqli_data_seek($res,$row);
				$resrow = (is_numeric($col)) ? mysqli_fetch_row($res) : mysqli_fetch_assoc($res);
				if (isset($resrow[$col])){
					return $resrow[$col];
				}
			}
		return false;
	}

	function num_rows($query) {
		set_error_handler($this->errcallb);
		$query = mysqli_num_rows($query);
		restore_error_handler();
		return $query;
	}

	function num_fields($query) {
		set_error_handler($this->errcallb);
		$return = mysqli_num_fields($query);
		restore_error_handler();
		return $return;
	}

	function insert_id() {
		if (DEBUG and LOG_MYSQL_ERRORS) {
			$id = $this->last_id;
		} else {
			set_error_handler($this->errcallb);
			$id = mysqli_insert_id($this->link);
			restore_error_handler();
		}
		return $id;
	}

	function fetch_row($query) {
		set_error_handler($this->errcallb);
		$query = mysqli_fetch_row($query);
		restore_error_handler();
		return $query;
	}

	function data_seek($query, $row) {
		set_error_handler($this->errcallb);
		$return = mysqli_data_seek($query, $row);
		restore_error_handler();
		return $return;
	}

	function affected_rows() {
		if (DEBUG and LOG_MYSQL_ERRORS) {
			$return = $this->last_rows;
		} else {
			set_error_handler($this->errcallb);
			$return = mysqli_affected_rows($this->link);
			restore_error_handler();
		}
		return $return;
	}

	function time($time=NULL) {
		if ($time === NULL) {
			$time = time();
		}
		return "LPAD('".$time."', '15', '0')";
	}

	function start_timer() {
		$mtime = explode(" ", microtime());
		$this->timer = $mtime[1] + $mtime[0];
		return true;
	}

	function stop_timer() {
		$mtime = explode(" ", microtime());
		$endtime = $mtime[1] + $mtime[0];
		$taken = ($endtime - $this->timer);
		$this->duration += $taken;
		$this->timer = 0;
		return $taken;
	}

	/**
	 * Retrieve the MySQL server version number.
	 *
	 * @return string
	 */
	function server_version(){
		return mysqli_get_server_info($this->link);
	}
}

/**
* Replaces mysql_result
*
* You cannot use mysql_result() as of PHP 7
* This function replaces that function.
* By Xian
*/
function mysqli_result($res,$row=0,$col=0){
	$numrows = mysqli_num_rows($res);
		if ($numrows && $row <= ($numrows-1) && $row >=0){
			mysqli_data_seek($res,$row);
			$resrow = (is_numeric($col)) ? mysqli_fetch_row($res) : mysqli_fetch_assoc($res);
			if (isset($resrow[$col])){
				return $resrow[$col];
			}
		}
	return false;
}

/**
 * Proper error reporting for abstracted mysqli_* function calls.
 *
 * @param int $errno
 * @param string $errstr
 * @author Robert Chapin (miqrogroove)
 */
function xmb_mysqli_error($errno, $errstr) {
	$output = '';
	{
		$trace = debug_backtrace();
		if (isset($trace[2]['function'])) { // Catch MySQL error
			$depth = 2;
		} else { // Catch syntax error
			$depth = 1;
		}
		$functionname = $trace[$depth]['function'];
		$filename = $trace[$depth]['file'];
		$linenum = $trace[$depth]['line'];
		$output = "MySQL encountered the following error: $errstr in \$db->{$functionname}() called by {$filename} on line {$linenum}";
		unset($trace, $functionname, $filename, $linenum);
	}

	if (!headers_sent()) {
		header('HTTP/1.0 500 Internal Server Error');
	}
	if (DEBUG And (!defined('X_SADMIN') Or X_SADMIN)) {
		require_once(ROOT.'include/validate.inc.php');
		echo "<pre>".cdataOut($output)."</pre>";
	} else {
		echo "<pre>The system has failed to process your request. If you're an administrator, please set the DEBUG flag to true in config.php.</pre>";
	}
	if (LOG_MYSQL_ERRORS) {
		if (!ini_get('log_errors')) {
			ini_set('log_errors', TRUE);
			ini_set('error_log', 'error_log');
		}
		error_log($output);
	}
	exit;
}
?>
mysqli.php (13,277 bytes)   

miqrogroove

2017-12-20 11:03

administrator   ~0000339

This bug will target patch level 15. The changes in 14 were version-dependent, which would not help people using versions older than 7.0.

miqrogroove

2017-12-26 07:16

administrator   ~0000366

Trunk version now has a proper MySQLi driver that is always used when available, in any PHP version.

Issue History

Date Modified Username Field Change
2017-03-15 19:36 miqrogroove New Issue
2017-09-16 20:53 Xian File Added: mysqli.php
2017-09-16 20:53 Xian Note Added: 0000335
2017-12-20 08:16 miqrogroove Target Version => 1.9.11.14
2017-12-20 11:03 miqrogroove Status new => confirmed
2017-12-20 11:03 miqrogroove Target Version 1.9.11.14 => 1.9.11.15
2017-12-20 11:03 miqrogroove Note Added: 0000339
2017-12-25 05:49 miqrogroove Assigned To => miqrogroove
2017-12-25 05:49 miqrogroove Status confirmed => assigned
2017-12-25 05:49 miqrogroove Priority low => normal
2017-12-26 07:16 miqrogroove Status assigned => resolved
2017-12-26 07:16 miqrogroove Resolution open => fixed
2017-12-26 07:16 miqrogroove Fixed in Version => 1.9.11.15
2017-12-26 07:16 miqrogroove SVN Revision => 2729
2017-12-26 07:16 miqrogroove Note Added: 0000366
2018-01-18 06:09 miqrogroove Status resolved => closed