diff --git a/main/inc/lib/database.lib.php b/main/inc/lib/database.lib.php index 55ac92e220..4dfa57a565 100755 --- a/main/inc/lib/database.lib.php +++ b/main/inc/lib/database.lib.php @@ -1,5 +1,5 @@ n; + } + /* ----------------------------------------------------------------------------- - Private Functions - You should not access these from outside the class - No effort is made to keep the names / results the same. + An intermediate API-layer between the system and the dabase server. ----------------------------------------------------------------------------- */ /** - * Glues a course database. - * glue format from local.inc.php. + * Returns the number of affected rows in the last database operation. + * @param resource $connection (optional) The database server connection, for detailed description see the method query(). + * @return int Returns the number of affected rows on success, and -1 if the last query failed. */ - public static function glue_course_database_name($database_name) { - return self::get_course_table_prefix().$database_name.self::get_database_glue(); + public static function affected_rows($connection = null) { + return self::use_default_connection($connection) ? mysql_affected_rows() : mysql_affected_rows($connection); } /** - * @param string $database_name, can be empty to use current course db - * - * @return the glued parameter if it is not empty, - * or the current course database (glued) if the parameter is empty. + * Closes non-persistent database connection. + * @param resource $connection (optional) The database server connection, for detailed description see the method query(). + * @return bool Returns TRUE on success or FALSE on failure. */ - public static function fix_database_parameter($database_name) { - if (empty($database_name)) { - $course_info = api_get_course_info(); - return $course_info['dbNameGlu']; - } - return self::glue_course_database_name($database_name); + public static function close($connection = null) { + return self::use_default_connection($connection) ? mysql_close() : mysql_close($connection); } /** - * Structures a course database and table name to ready them - * for querying. The course database parameter is considered glued: - * e.g. COURSE001`.` + * Opens a connection to a database server. + * @param array $parameters (optional) An array that contains the necessary parameters for accessing the server. + * @return resource/boolean Returns a database connection on success or FALSE on failure. + * Note: Currently the array could contain MySQL-specific parameters: + * $parameters['server'], $parameters['username'], $parameters['password'], + * $parameters['new_link'], $parameters['client_flags'], $parameters['persistent']. + * For details see documentation about the functions mysql_connect() and mysql_pconnect(). + * @link http://php.net/manual/en/function.mysql-connect.php + * @link http://php.net/manual/en/function.mysql-pconnect.php */ - public static function format_glued_course_table_name($database_name_with_glue, $table) { - //$course_info = api_get_course_info(); - return '`'.$database_name_with_glue.$table.'`'; + public static function connect($parameters = array()) { + // A MySQL-specific implementation. + if (!isset($parameters['server'])) { + $parameters['server'] = @ini_get('mysql.default_host'); + if (empty($parameters['server'])) { + $parameters['server'] = 'localhost:3306'; + } + } + if (!isset($parameters['username'])) { + $parameters['username'] = @ini_get('mysql.default_user'); + } + if (!isset($parameters['password'])) { + $parameters['password'] = @ini_get('mysql.default_password'); + } + if (!isset($parameters['new_link'])) { + $parameters['new_link'] = false; + } + if (!isset($parameters['client_flags'])) { + $parameters['client_flags'] = 0; + } + return $parameters['persistent'] + ? mysql_pconnect($parameters['server'], $parameters['username'], $parameters['password'], $parameters['client_flags']) + : mysql_connect($parameters['server'], $parameters['username'], $parameters['password'], $parameters['new_link'], $parameters['client_flags']); } /** - * Structures a database and table name to ready them - * for querying. The database parameter is considered not glued, - * just plain e.g. COURSE001 + * Returns the error number from the last operation done on the database server. + * @param resource $connection (optional) The database server connection, for detailed description see the method query(). + * @return int Returns the error number from the last database (operation, or 0 (zero) if no error occurred. */ - public static function format_table_name($database, $table) { - return '`'.$database.'`.`'.$table.'`'; + public static function errno($connection = null) { + return self::use_default_connection($connection) ? mysql_errno() : mysql_errno($connection); } /** - * Count the number of rows in a table - * @param string $table The table of which the rows should be counted - * @return int The number of rows in the given table. + * Returns the error text from the last operation done on the database server. + * @param resource $connection (optional) The database server connection, for detailed description see the method query(). + * @return string Returns the error text from the last database operation, or '' (empty string) if no error occurred. */ - public static function count_rows($table) { - $obj = self::fetch_object(self::query("SELECT COUNT(*) AS n FROM $table", __FILE__, __LINE__)); - return $obj->n; + public static function error($connection = null) { + return self::use_default_connection($connection) ? mysql_error() : mysql_error($connection); } /** * Escapes a string to insert into the database as text - * @param string The string to escape - * @return string The escaped string + * @param string The string to escape + * @param resource $connection (optional) The database server connection, for detailed description see the method query(). + * @return string The escaped string * @author Yannick Warnier * @author Patrick Cool , Ghent University */ - public static function escape_string($string) { - return get_magic_quotes_gpc() ? mysql_real_escape_string(stripslashes($string)) : mysql_real_escape_string($string); + public static function escape_string($string, $connection = null) { + return get_magic_quotes_gpc() + ? (self::use_default_connection($connection) + ? mysql_real_escape_string(stripslashes($string)) + : mysql_real_escape_string(stripslashes($string), $connection)) + : (self::use_default_connection($connection) + ? mysql_real_escape_string($string) + : mysql_real_escape_string($string, $connection)); } /** @@ -693,7 +744,7 @@ class Database { * @author Yannick Warnier */ public static function fetch_object($res, $class = null, $params = null) { - return !empty($class) ? (is_array($params) ? mysql_fetch_object($res, $class, $params) : mysql_fetch_object($res,$class)) : mysql_fetch_object($res); + return !empty($class) ? (is_array($params) ? mysql_fetch_object($res, $class, $params) : mysql_fetch_object($res, $class)) : mysql_fetch_object($res); } /** @@ -706,6 +757,16 @@ class Database { return mysql_fetch_row($res); } + /** + * Gets the ID of the last item inserted into the database + * @param resource $connection (optional) The database server connection, for detailed description see the method query(). + * @return int The last ID as returned by the DB function + * @comment This should be updated to use ADODB at some point + */ + public static function insert_id($connection = null) { + return self::use_default_connection($connection) ? mysql_insert_id() : mysql_insert_id($connection); + } + /** * Gets the number of rows from the last query result - help achieving database independence * @param resource The result @@ -716,10 +777,6 @@ class Database { return mysql_num_rows($res); } - public static function get_course_chat_connected_table($database_name = '') { - return self::format_glued_course_table_name(self::fix_database_parameter($database_name), CHAT_CONNECTED_TABLE); - } - /** * Acts as the relative *_result() function of most DB drivers and fetches a * specific line and a field @@ -732,70 +789,67 @@ class Database { return mysql_num_rows($resource) > 0 ? (!empty($field) ? mysql_result($resource, $row, $field) : mysql_result($resource, $row)) : null; } - /** - * Gets the ID of the last item inserted into the database - * - * @return integer The last ID as returned by the DB function - * @comment This should be updated to use ADODB at some point - */ - public static function insert_id() { - return mysql_insert_id(); - } - - /** - * Returns the number of affected rows - * @param resource Optional database resource - */ - public static function affected_rows($r = null) { - return !is_null($r) ? mysql_affected_rows($r) : mysql_affected_rows(); - } - /** * This function returns a resource * Documentation has been added by Arthur Portugal - * An adaptation for reliable showing error messages has been done by Ivan Tcholakov, 2009 + * Some adaptations have been implemented by Ivan Tcholakov, 2009, 2010 * @author Olivier Brouckaert - * @param string $query - SQL query - * @param string $file - optional, the file path and name of the error (__FILE__) - * @param string $line - optional, the line of the error (__LINE__) - * @return resource - the return value of the query + * @param string $query The SQL query + * @param resource $connection (optional) The database server (MySQL) connection. + * If it is not specified, the connection opened by mysql_connect() is assumed. + * If no connection is found, the server will try to create one as if mysql_connect() was called with no arguments. + * If no connection is found or established, an E_WARNING level error is generated. + * @param string $file (optional) On error it shows the file in which the error has been trigerred (use the "magic" constant __FILE__ as input parameter) + * @param string $line (optional) On error it shows the line in which the error has been trigerred (use the "magic" constant __LINE__ as input parameter) + * @return resource The returned result from the query + * Note: The parameter $connection could be skipped. Here are examples of this method usage: + * Database::query($query); + * $result = Database::query($query); + * Database::query($query, $connection); + * $result = Database::query($query, $connection); + * Database::query($query, __FILE__, __LINE__); + * $result = Database::query($query, __FILE__, __LINE__); + * Database::query($query, $connection, __FILE__, __LINE__); + * $result = Database::query($query, $connection, __FILE__, __LINE__); */ - - public static function query($query, $file = '', $line = 0) { - if (!($result = @mysql_query($query)) && $line && api_get_setting('server_type') != 'production') { - $security_ok = class_exists('Security') && class_exists('HTMLPurifier'); - $error = self::error(); - $info = '
';
-			$info .= 'DATABASE ERROR :
'; - $info .= $security_ok ? Security::remove_XSS($error) : api_htmlentities($error, ENT_QUOTES); - $info .= '
'; - $info .= 'QUERY :
'; - $info .= $security_ok ? Security::remove_XSS($query) : api_htmlentities($query, ENT_QUOTES); - $info .= '
'; - $info .= 'FILE :
'; - $info .= ($file == '' ? ' unknown ' : $file); - $info .= '
'; - $info .= 'LINE :
'; - $info .= ($line == 0 ? ' unknown ' : $line); - $info .= '
'; - echo $info; + public static function query($query, $connection = null, $file = null, $line = null) { + $use_default_connection = self::use_default_connection($connection); + if ($use_default_connection) { + // Let us do parameter shifting, thus the method would be similar + // (in regard to parameter order) to the original function mysql_query(). + $line = $file; + $file = $connection; + $connection = null; + } + if (!($result = $use_default_connection ? @mysql_query($query) : @mysql_query($query, $connection))) { + $server_type = api_get_setting('server_type'); + if (!empty($line) && !empty($server_type) && $server_type != 'production') { + echo '
' .
+					'DATABASE ERROR #'.self::errno($connection).':
' . + self::remove_XSS(self::error($connection)) . + '
' . + 'QUERY :
' . + self::remove_XSS($query) . + '
' . + 'FILE :
' . + (empty($file) ? ' unknown ' : $file) . + '
' . + 'LINE :
' . + (empty($line) ? ' unknown ' : $line) . + '
'; + } } return $result; } - public static function error() { - return mysql_error(); - } - /** - * Return course code from one given gradebook category's id - * @param int Category ID - * @return string Course code - * @todo move this function in a gradebook-related library + * Selects a database. + * @param string $database_name The name of the database that is to be selected. + * @param resource $connection (optional) The database server connection, for detailed description see the method query(). + * @return bool Returns TRUE on success or FALSE on failure. */ - public static function get_course_by_category($category_id) { - $info = self::fetch_array(self::query('SELECT course_code FROM '.self::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY).' WHERE id='.$category_id, __FILE__, __LINE__), 'ASSOC'); - return $info ? $info['course_code'] : false; + public static function select_db($database_name, $connection = null) { + return self::use_default_connection($connection) ? mysql_select_db($database_name) : mysql_select_db($database_name, $connection); } /** @@ -842,8 +896,7 @@ class Database { } /** - * Constructs a SQL clause about default character set and default collation - * for newly created databases and tables. + * Constructs a SQL clause about default character set and default collation for newly created databases and tables. * Example: Database::make_charset_clause('UTF-8', 'bulgarian') returns * DEFAULT CHARACTER SET `utf8` DEFAULT COLLATE `utf8_general_ci` * @param string $encoding (optional) The default database/table encoding (a system conventional id) to be used. @@ -914,6 +967,111 @@ class Database { return $result[$db_encoding]; } + /** + * Chooses the default MySQL-specific collation from given encoding and language. + * @param string $encoding A conventional encoding id, i.e. 'UTF-8' + * @param string $language (optional) A conventional for the system language id, i.e. 'bulgarian'. If it is empty, the chosen collation is the default server value corresponding to the given encoding. + * @return string Returns a suitable default collation, for example 'utf8_general_ci', or NULL if collation was not found. + * @author Ivan Tcholakov + */ + public static function to_db_collation($encoding, $language = null) { + static $result = array(); + if (!isset($result[$encoding][$language])) { + $result[$encoding][$language] = null; + if (self::is_encoding_supported($encoding)) { + $db_encoding = self::to_db_encoding($encoding); + if (!empty($language)) { + $lang = api_purify_language_id($language); + $res = self::check_db_collation($db_encoding, $lang); + if (empty($res)) { + $db_collation_map = & self::get_db_collation_map(); + if (isset($db_collation_map[$lang])) { + $res = self::check_db_collation($db_encoding, $db_collation_map[$lang]); + } + } + if (empty($res)) { + $res = self::check_db_collation($db_encoding, null); + } + $result[$encoding][$language] = $res; + } else { + $result[$encoding][$language] = self::check_db_collation($db_encoding, null); + } + } + } + return $result[$encoding][$language]; + } + + /* + ----------------------------------------------------------------------------- + Private methods + You should not access these from outside the class + No effort is made to keep the names / results the same. + ----------------------------------------------------------------------------- + */ + + /** + * Glues a course database. + * glue format from local.inc.php. + */ + private static function glue_course_database_name($database_name) { + return self::get_course_table_prefix().$database_name.self::get_database_glue(); + } + + /** + * @param string $database_name, can be empty to use current course db + * + * @return the glued parameter if it is not empty, + * or the current course database (glued) if the parameter is empty. + */ + private static function fix_database_parameter($database_name) { + if (empty($database_name)) { + $course_info = api_get_course_info(); + return $course_info['dbNameGlu']; + } + return self::glue_course_database_name($database_name); + } + + /** + * Structures a course database and table name to ready them + * for querying. The course database parameter is considered glued: + * e.g. COURSE001`.` + */ + private static function format_glued_course_table_name($database_name_with_glue, $table) { + return '`'.$database_name_with_glue.$table.'`'; + } + + /** + * Structures a database and table name to ready them + * for querying. The database parameter is considered not glued, + * just plain e.g. COURSE001 + */ + private static function format_table_name($database, $table) { + return '`'.$database.'`.`'.$table.'`'; + } + + /** + * This private method is to be used by the other methods in this class for + * checking whether the input parameter $connection actually has been provided. + * If the input parameter connection is not a resource or if it is not FALSE (in case of error) + * then the default opened connection should be used by the called method. + * @param resource/boolean $connection The checked parameter $connection. + * @return boolean TRUE means that calling method should use the default connection. + * FALSE means that (valid) parameter $connection has been provided and it should be used. + */ + private static function use_default_connection($connection) { + return !is_resource($connection) && $connection !== false; + } + + /** + * This private method tackles the XSS injections. It is similar to Security::remove_XSS() and works always, + * including the time of initialization when the class Security has not been loaded yet. + * @param string The input variable to be filtered from XSS, in this class it is expected to be a string. + * @return string Returns the filtered string as a result. + */ + private static function remove_XSS(& $var) { + return class_exists('Security') && class_exists('HTMLPurifier') ? Security::remove_XSS($var) : api_htmlentities($var, ENT_QUOTES); + } + /** * This private function encapsulates a table with relations between * conventional and MuSQL-specific encoding identificators. @@ -951,40 +1109,6 @@ class Database { return $encoding_map; } - /** - * Chooses the default MySQL-specific collation from given encoding and language. - * @param string $encoding A conventional encoding id, i.e. 'UTF-8' - * @param string $language (optional) A conventional for the system language id, i.e. 'bulgarian'. If it is empty, the chosen collation is the default server value corresponding to the given encoding. - * @return string Returns a suitable default collation, for example 'utf8_general_ci', or NULL if collation was not found. - * @author Ivan Tcholakov - */ - public static function to_db_collation($encoding, $language = null) { - static $result = array(); - if (!isset($result[$encoding][$language])) { - $result[$encoding][$language] = null; - if (self::is_encoding_supported($encoding)) { - $db_encoding = self::to_db_encoding($encoding); - if (!empty($language)) { - $lang = api_purify_language_id($language); - $res = self::check_db_collation($db_encoding, $lang); - if (empty($res)) { - $db_collation_map = & self::get_db_collation_map(); - if (isset($db_collation_map[$lang])) { - $res = self::check_db_collation($db_encoding, $db_collation_map[$lang]); - } - } - if (empty($res)) { - $res = self::check_db_collation($db_encoding, null); - } - $result[$encoding][$language] = $res; - } else { - $result[$encoding][$language] = self::check_db_collation($db_encoding, null); - } - } - } - return $result[$encoding][$language]; - } - /** * A helper language id translation table for choosing some collations. * @author Ivan Tcholakov