<?php
/**
 * This is a helper class for the SS installer.
 *
 * It does all the specific checking for MySQLDatabase
 * to ensure that the configuration is setup correctly.
 *
 * @package framework
 * @subpackage model
 */
class MySQLDatabaseConfigurationHelper implements DatabaseConfigurationHelper {

	/**
	 * Ensure that the database function for connectivity is available.
	 * If it is, we assume the PHP module for this database has been setup correctly.
	 *
	 * @param array $databaseConfig Associative array of db configuration, e.g. "server", "username" etc
	 * @return boolean
	 */
	public function requireDatabaseFunctions($databaseConfig) {
		return class_exists('MySQLi');
	}

	/**
	 * Ensure that the database server exists.
	 * @param array $databaseConfig Associative array of db configuration, e.g. "server", "username" etc
	 * @return array Result - e.g. array('success' => true, 'error' => 'details of error')
	 */
	public function requireDatabaseServer($databaseConfig) {
		$success = false;
		$error = '';
		$conn = @new MySQLi($databaseConfig['server'], $databaseConfig['username'], $databaseConfig['password']);
		if($conn && $conn->connect_errno) {
			$success = false;
			$error = $conn->connect_error;
		} else {
			$success = true;
		}

		return array(
			'success' => $success,
			'error' => $error
		);
	}

	/**
	 * Get the database version for the MySQL connection, given the
	 * database parameters.
	 * @return mixed string Version number as string | boolean FALSE on failure
	 */
	public function getDatabaseVersion($databaseConfig) {
		$conn = new MySQLi($databaseConfig['server'], $databaseConfig['username'], $databaseConfig['password']);
		if(!$conn) return false;
		$version = $conn->server_info;
		if(!$version) {
			// fallback to trying a query
			$result = $conn->query('SELECT VERSION()');
			$row = $result->fetch_array();
			if($row && isset($row[0])) {
				$version = trim($row[0]);
			}
		}
		return $version;
	}

	/**
	 * Ensure that the MySQL server version is at least 5.0.
	 * @param array $databaseConfig Associative array of db configuration, e.g. "server", "username" etc
	 * @return array Result - e.g. array('success' => true, 'error' => 'details of error')
	 */
	public function requireDatabaseVersion($databaseConfig) {
		$version = $this->getDatabaseVersion($databaseConfig);
		$success = false;
		$error = '';
		if($version) {
			$success = version_compare($version, '5.0', '>=');
			if(!$success) {
				$error = "Your MySQL server version is $version. It's recommended you use at least MySQL 5.0.";
			}
		} else {
			$error = "Could not determine your MySQL version.";
		}
		return array(
			'success' => $success,
			'error' => $error
		);
	}

	/**
	 * Ensure a database connection is possible using credentials provided.
	 * @param array $databaseConfig Associative array of db configuration, e.g. "server", "username" etc
	 * @return array Result - e.g. array('success' => true, 'error' => 'details of error')
	 */
	public function requireDatabaseConnection($databaseConfig) {
		$success = false;
		$error = '';
		$conn = new MySQLi($databaseConfig['server'], $databaseConfig['username'], $databaseConfig['password']);
		if($conn) {
			$success = true;
		} else {
			$success = false;
			$error = ($conn) ? $conn->connect_error : '';
		}
		return array(
			'success' => $success,
			'error' => $error
		);
	}

	/**
	 * Ensure that the database connection is able to use an existing database,
	 * or be able to create one if it doesn't exist.
	 *
	 * @param array $databaseConfig Associative array of db configuration, e.g. "server", "username" etc
	 * @return array Result - e.g. array('success' => true, 'alreadyExists' => 'true')
	 */
	public function requireDatabaseOrCreatePermissions($databaseConfig) {
		$success = false;
		$alreadyExists = false;
		$conn = new MySQLi($databaseConfig['server'], $databaseConfig['username'], $databaseConfig['password']);
		if($conn && $conn->select_db($databaseConfig['database'])) {
			$success = true;
			$alreadyExists = true;
		} else {
			if($conn && $conn->query('CREATE DATABASE testing123')) {
				$conn->query('DROP DATABASE testing123');
				$success = true;
				$alreadyExists = false;
			}
		}
		return array(
			'success' => $success,
			'alreadyExists' => $alreadyExists
		);
	}

	/**
	 * Ensure we have permissions to alter tables.
	 * 
	 * @param array $databaseConfig Associative array of db configuration, e.g. "server", "username" etc
	 * @return array Result - e.g. array('okay' => true, 'applies' => true), where applies is whether
	 * the test is relevant for the database
	 */
	public function requireDatabaseAlterPermissions($databaseConfig) {
		$success = false;
		$conn = new MySQLi($databaseConfig['server'], $databaseConfig['username'], $databaseConfig['password']);
		if($conn) {
			if ($res = $conn->query('SHOW GRANTS')) {
				// Annoyingly, MySQL 'escapes' the database, so we need to do it too.
				$db = str_replace(array('%', '_', '`'), array('\%', '\_', '``'), $databaseConfig['database']);
				while ($row = $res->fetch_array()) {
					if (preg_match('/^GRANT (.+) ON (.+) TO/', $row[0], $matches)) {
						// Need to change to an array of permissions, because ALTER is contained in ALTER ROUTINES.
						$permission = array_map('trim', explode(',', $matches[1]));
						$on_database = $matches[2];
						// The use of both ` and " is because of ANSI mode.
						if (in_array('ALL PRIVILEGES', $permission) and (
								($on_database == '*.*') or ($on_database == '`' . $db . '`.*')
								or ($on_database == '"' . $db . '".*'))) {
							$success = true;
							break;
						}
						if (in_array('ALTER', $permission) and (
								($on_database == '*.*') or ($on_database == '`' . $db . '`.*')
								or ($on_database == '"' . $db . '".*'))) {
							$success = true;
							break;
						}
					}
				}
			}
		}
		return array(
			'success' => $success,
			'applies' => true
		);
	}
}