<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package fusionLib
 * @copyright Copyright (c) 2007 - 2013 fusionLib. All rights reserved.
 * @link http://fusionlib.com
 */

/**
 * flDbMySQLiResult Class
 *
 * Driver class to hold MySQLi results.
 *
 * @package fusionLib
 */
class flDbMySQLiResult extends flDbResult {

	/**
	 * The result object.
	 *
	 * @var mysqli_result
	 */
	private $result;

	/**
	 * Init the results object.
	 *
	 * @param mysqli_result $result The raw result data.
	 */
	function __construct($result) {
		$this->result = $result;
	}

	/**
	 * Clean up.
	 */
	function __destruct() {
		$this->result->free();
	}

	/**
	 * Get the number of rows.
	 *
	 * @return int|void The number of rows in the results.
	 */
	function numRows() {
		return $this->result->num_rows;
	}

	/**
	 * Seek to a location in the results.
	 *
	 * @param int $row The row to seek to.
	 * @return bool true if the seek worked; else false.
	 */
	function seek($row) {
		return $this->result->data_seek($row);
	}

	/**
	 * Fetch the next row from the results.
	 *
	 * @return array The row.
	 */
	function fetch() {
		return $this->result->fetch_assoc();
	}
}

/**
 * flDbMySQLi Class
 *
 * Driver for MySQLi database.
 *
 * @package fusionLib
 */
class flDbMySQLi extends flDb {
	/**
	 * The master database link
	 *
	 * @var mysqli
	 */
	private $masterDB = null;

	/**
	 * The slave database link
	 *
	 * @var mysqli
	 */
	private $slaveDB = null;

	/**
	 * That last database link used
	 *
	 * @var mysqli
	 */
	private $lastDB = null;

	/**
	 * Flags if a slave database connection exists.
	 *
	 * @var bool
	 */
	private $hasSlave = false;

	/**
	 * The name of the database.
	 *
	 * @var string
	 */
	private $dbName;

	/**
	 * Initialise the MySQLi driver.
	 *
	 * @param array $master Array of the DSN parts for the master.
	 * @param string $slave The slave database DSN or null if not used.
	 * @throws flDbException
	 */
	function __construct($master, $slave) {

		// Connect master
		$this->masterDB = @mysqli_connect($master['host'], $master['user'], $master['pass'], $this->dbName = substr($master['path'], 1), $master['port']);
		if(mysqli_connect_errno()) {
			throw new flDbException("Error connecting to database server.\n\nMySQL Error: " . mysqli_connect_error() . "\n" .
				"MySQL Error Number: " . mysqli_connect_errno());
		}

		$this->masterDB->query("SET NAMES 'utf8'");

		// Connect slave
		if($slave !== null) {
			$slave = parse_url($slave);

			$this->slaveDB = @mysqli_connect($slave['host'], $slave['user'], $slave['pass'], substr($slave['path'], 1), $slave['port']);
			if(mysqli_connect_errno()) {
				throw new flDbException("Error connecting to database server.\n\nMySQL Error: " . mysqli_connect_error() . "\n" .
					"MySQL Error Number: " . mysqli_connect_errno());
			}

			$this->slaveDB->query("SET NAMES 'utf8'");
			$this->hasSlave = true;
		}
		else
			$this->slaveDB = $this->masterDB;

		$this->lastDB = $this->masterDB;
	}

	/**
	 * Close the database.
	 */
	function __destruct() {
		@mysqli_close($this->masterDB);
		if($this->hasSlave)
			@mysqli_close($this->slaveDB);
	}

	/**
	 * Escape a value in the driver.
	 *
	 * @param mixed $value The value to escape.
	 * @return mixed The escaped value.
	 */
	function _escape($value) {
		return mysqli_real_escape_string($this->masterDB, $value);
	}

	/**
	 * Perform the SQL query in the driver.
	 *
	 * @param string $query The query string.
	 * @return flDbResult The result object or boolean result.
	 */
	function _query($query) {
		$this->lastDB = !strncasecmp('SELECT', $query, 6) ? $this->slaveDB : $this->masterDB;
		$r = @$this->lastDB->query($query);
		return $r instanceof mysqli_result ? new flDbMySQLiResult($r) : $r;
	}

	/**
	 * Return the id generated by the last insert operation.
	 *
	 * @return int The id.
	 */
	function insertID() {
		return mysqli_insert_id($this->lastDB);
	}

	/**
	 * Get the number of rows affected by the previous operation.
	 *
	 * @return int The number of rows affected.
	 */
	function affectedRows() {
		return mysqli_affected_rows($this->lastDB);
	}

	/**
	 * Get the list of tables in the database.
	 *
	 * @return array The list of tables in the database.
	 */
	function fetchTables() {
		$tables = array();
		$this->queryCount++;
		$result = $this->masterDB->query("SHOW TABLES FROM `$this->dbName`");
		while($row = $result->fetch_row())
			$tables[] = $row[0];
		$result->free();
		return $tables;
	}

	/**
	 * Get the names of all fields in a table.
	 *
	 * @param string $table The table to query.
	 * @return array An array holding the names of all fields.
	 */
	function fetchFields($table) {
		$fields = array();
		$this->queryCount++;
		$result = $this->masterDB->query("SHOW COLUMNS FROM `$table`");
		while($row = $result->fetch_row())
			$fields[] = $row[0];
		$result->free();
		return $fields;
	}

	/**
	 * Optimize or repair tables within the database.
	 *
	 * @param bool $repair true to perform a repair or false to optimize.
	 * @param string $table The name of the table to operate on.
	 * @return string The result.
	 */
	function _maintain($repair, $table) {
		$this->queryCount++;
		$result = $this->masterDB->query(($repair ? 'REPAIR' : 'OPTIMIZE') . " TABLE $table");
		$row = $result->fetch_row();
		$result->free();
		return $row[3];
	}

	/**
	 * The error for the previous operation.
	 *
	 * @return string The error string.
	 */
	function errorString() {
		return $this->lastDB->error;
	}

	/**
	 * The database server version.
	 *
	 * @return string The version string.
	 */
	function version() {
		return mysqli_get_server_info($this->masterDB);
	}
}
