Merge pull request #1949 from compwright/develop

Added support for timestamp-based migrations
diff --git a/.travis.yml b/.travis.yml
index 62acf05..a1635ea 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,11 +4,6 @@
   - 5.3
   - 5.4
   
-services:
-  - mysql
-  - postgresql
-  - sqlite
-
 env:
   - DB=mysql
   - DB=pgsql
diff --git a/system/core/Input.php b/system/core/Input.php
index adc5f7a..4d73fa4 100644
--- a/system/core/Input.php
+++ b/system/core/Input.php
@@ -768,9 +768,9 @@
 
 			foreach ($_SERVER as $key => $val)
 			{
-				if (strpos($key, 'HTTP_') === 0)
+				if (sscanf($key, 'HTTP_%s', $header) === 1)
 				{
-					$headers[substr($key, 5)] = $this->_fetch_from_array($_SERVER, $key, $xss_clean);
+					$headers[$header] = $this->_fetch_from_array($_SERVER, $key, $xss_clean);
 				}
 			}
 		}
diff --git a/system/core/Output.php b/system/core/Output.php
index 9300df3..6312ccd 100644
--- a/system/core/Output.php
+++ b/system/core/Output.php
@@ -256,9 +256,9 @@
 	{
 		for ($i = 0, $c = count($this->headers); $i < $c; $i++)
 		{
-			if (preg_match('/^Content-Type:\s(.+)$/', $this->headers[$i][0], $matches))
+			if (sscanf($this->headers[$i][0], 'Content-Type: %s', $content_type) === 1)
 			{
-				return $matches[1];
+				return $content_type;
 			}
 		}
 
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index dfaf0e0..4fcb6c6 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -329,9 +329,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' RAND()';
+	protected $_random_keyword = array('RAND()', 'RAND(%d)');
 
 	/**
 	 * COUNT string
diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php
index 3643977..543d5cc 100644
--- a/system/database/DB_query_builder.php
+++ b/system/database/DB_query_builder.php
@@ -1144,13 +1144,16 @@
 	 */
 	public function order_by($orderby, $direction = '', $escape = NULL)
 	{
-		$direction = trim($direction);
+		$direction = strtoupper(trim($direction));
 
-		if (strtolower($direction) === 'random' OR $orderby === $this->_random_keyword)
+		if ($direction === 'RANDOM')
 		{
-			// Random ordered results don't need a field name
-			$orderby = $this->_random_keyword;
 			$direction = '';
+
+			// Do we have a seed value?
+			$orderby = ctype_digit((string) $orderby)
+				? $orderby = sprintf($this->_random_keyword[1], $orderby)
+				: $this->_random_keyword[0];
 		}
 		elseif (empty($orderby))
 		{
@@ -1158,7 +1161,7 @@
 		}
 		elseif ($direction !== '')
 		{
-			$direction = in_array(strtoupper(trim($direction)), array('ASC', 'DESC'), TRUE) ? ' '.$direction : '';
+			$direction = in_array($direction, array('ASC', 'DESC'), TRUE) ? ' '.$direction : '';
 		}
 
 		is_bool($escape) OR $escape = $this->_protect_identifiers;
diff --git a/system/database/DB_utility.php b/system/database/DB_utility.php
index 488ebf3..a32fd44 100644
--- a/system/database/DB_utility.php
+++ b/system/database/DB_utility.php
@@ -106,9 +106,9 @@
 			return $this->db->data_cache['db_names'];
 		}
 
-		for ($i = 0, $c = count($query); $i < $c; $i++)
+		for ($i = 0, $query = $query->result_array(), $c = count($query); $i < $c; $i++)
 		{
-			$this->db->data_cache['db_names'] = current($query[$i]);
+			$this->db->data_cache['db_names'][] = current($query[$i]);
 		}
 
 		return $this->db->data_cache['db_names'];
diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php
index c89a924..8394139 100644
--- a/system/database/drivers/cubrid/cubrid_driver.php
+++ b/system/database/drivers/cubrid/cubrid_driver.php
@@ -64,6 +64,13 @@
 	 */
 	protected $_escape_char = '`';
 
+	/**
+	 * ORDER BY random keyword
+	 *
+	 * @var	array
+	 */
+	protected $_random_keyword = array('RANDOM()', 'RANDOM(%d)');
+
 	// --------------------------------------------------------------------
 
 	/**
diff --git a/system/database/drivers/ibase/ibase_driver.php b/system/database/drivers/ibase/ibase_driver.php
index fc1c28f..02ec354 100644
--- a/system/database/drivers/ibase/ibase_driver.php
+++ b/system/database/drivers/ibase/ibase_driver.php
@@ -53,9 +53,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' Random()';
+	protected $_random_keyword = array('RAND()', 'RAND()');
 
 	/**
 	 * IBase Transaction status flag
diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php
index a9d53fb..53f2e55 100644
--- a/system/database/drivers/mssql/mssql_driver.php
+++ b/system/database/drivers/mssql/mssql_driver.php
@@ -53,9 +53,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' NEWID()';
+	protected $_random_keyword = array('NEWID()', 'RAND(%d)');
 
 	/**
 	 * Quoted identifier flag
diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php
index eb01dd1..62f9191 100644
--- a/system/database/drivers/oci8/oci8_driver.php
+++ b/system/database/drivers/oci8/oci8_driver.php
@@ -102,9 +102,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' ASC'; // not currently supported
+	protected $_random_keyword = array('ASC', 'ASC'); // not currently supported
 
 	/**
 	 * COUNT string
diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php
index 1710aaf..a01a9d6 100644
--- a/system/database/drivers/odbc/odbc_driver.php
+++ b/system/database/drivers/odbc/odbc_driver.php
@@ -73,6 +73,13 @@
 	 */
 	protected $_like_escape_str = " {escape '%s'} ";
 
+	/**
+	 * ORDER BY random keyword
+	 *
+	 * @var	array
+	 */
+	protected $_random_keyword = array('RND()', 'RND(%d)');
+
 	// --------------------------------------------------------------------
 
 	/**
@@ -85,8 +92,6 @@
 	{
 		parent::__construct($params);
 
-		$this->_random_keyword = ' RND('.time().')'; // database specific random keyword
-
 		// Legacy support for DSN in the hostname field
 		if (empty($this->dsn))
 		{
diff --git a/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php b/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
index 4f762b9..5a87cf0 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
@@ -55,6 +55,13 @@
 	 */
 	protected $_escape_char = '`';
 
+	/**
+	 * ORDER BY random keyword
+	 *
+	 * @var	array
+	 */
+	protected $_random_keyword = array('RANDOM()', 'RANDOM(%d)');
+
 	// --------------------------------------------------------------------
 
 	/**
diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
index 7121819..a44fd3a 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
@@ -53,9 +53,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' NEWID()';
+	protected $_random_keyword = array('NEWID()', 'RAND(%d)');
 
 	/**
 	 * Quoted identifier flag
diff --git a/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
index 83b9648..7c38366 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
@@ -53,9 +53,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' RANDOM()';
+	protected $_random_keyword = array('RAND()', 'RAND()');
 
 	// --------------------------------------------------------------------
 
diff --git a/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php b/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
index 8998670..03886f4 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
@@ -51,6 +51,15 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * ORDER BY random keyword
+	 *
+	 * @var	array
+	 */
+	protected $_random_keyword = array('ASC', 'ASC'); // Currently not supported
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Class constructor
 	 *
 	 * Builds the DSN if not already set.
diff --git a/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php b/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
index e1beb36..051b7de 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
@@ -62,9 +62,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' ASC'; // Currently not supported
+	protected $_random_keyword = array('ASC', 'ASC'); // Currently not supported
 
 	/**
 	 * COUNT string
diff --git a/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
index f6e0e21..05b34fa 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
@@ -73,6 +73,13 @@
 	 */
 	protected $_like_escape_str = " {escape '%s'} ";
 
+	/**
+	 * ORDER BY random keyword
+	 *
+	 * @var	array
+	 */
+	protected $_random_keyword = array('RND()', 'RND(%d)');
+
 	// --------------------------------------------------------------------
 
 	/**
diff --git a/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
index c3f5b76..4516521 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
@@ -60,9 +60,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' RANDOM()';
+	protected $_random_keyword = array('RANDOM()', 'RANDOM()');
 
 	// --------------------------------------------------------------------
 
@@ -110,6 +110,41 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * ORDER BY
+	 *
+	 * @param	string	$orderby
+	 * @param	string	$direction	ASC or DESC
+	 * @param	bool	$escape
+	 * @return	object
+	 */
+	public function order_by($orderby, $direction = '', $escape = NULL)
+	{
+		$direction = strtoupper(trim($direction));
+		if ($direction === 'RANDOM')
+		{
+			if ( ! is_float($orderby) && ctype_digit((string) $orderby))
+			{
+				$orderby = ($orderby > 1)
+					? (float) '0.'.$orderby
+					: (float) $orderby;
+			}
+
+			if (is_float($orderby))
+			{
+				$this->simple_query('SET SEED '.$orderby);
+			}
+
+			$orderby = $this->_random_keyword[0];
+			$direction = '';
+			$escape = FALSE;
+		}
+
+		return parent::order_by($orderby, $direction, $escape);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Show table query
 	 *
 	 * Generates a platform-specific query string so that the table names can be fetched
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
index b24b13e..f9d186f 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
@@ -53,7 +53,7 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
 	protected $_random_keyword = ' RANDOM()';
 
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
index 7bb7f88..1a63072 100644
--- a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
@@ -53,9 +53,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' NEWID()';
+	protected $_random_keyword = array('NEWID()', 'RAND(%d)');
 
 	/**
 	 * Quoted identifier flag
diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php
index 40f21b1..a52777b 100644
--- a/system/database/drivers/postgre/postgre_driver.php
+++ b/system/database/drivers/postgre/postgre_driver.php
@@ -60,9 +60,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' RANDOM()'; // database specific random keyword
+	protected $_random_keyword = array('RANDOM()', 'RANDOM()');
 
 	// --------------------------------------------------------------------
 
@@ -482,6 +482,41 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * ORDER BY
+	 *
+	 * @param	string	$orderby
+	 * @param	string	$direction	ASC or DESC
+	 * @param	bool	$escape
+	 * @return	object
+	 */
+	public function order_by($orderby, $direction = '', $escape = NULL)
+	{
+		$direction = strtoupper(trim($direction));
+		if ($direction === 'RANDOM')
+		{
+			if ( ! is_float($orderby) && ctype_digit((string) $orderby))
+			{
+				$orderby = ($orderby > 1)
+					? (float) '0.'.$orderby
+					: (float) $orderby;
+			}
+
+			if (is_float($orderby))
+			{
+				$this->simple_query('SET SEED '.$orderby);
+			}
+
+			$orderby = $this->_random_keyword[0];
+			$direction = '';
+			$escape = FALSE;
+		}
+
+		return parent::order_by($orderby, $direction, $escape);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Update statement
 	 *
 	 * Generates a platform-specific update string from the supplied data
diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php
index 8541173..23f287b 100644
--- a/system/database/drivers/sqlite/sqlite_driver.php
+++ b/system/database/drivers/sqlite/sqlite_driver.php
@@ -53,9 +53,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' Random()'; // database specific random keyword
+	protected $_random_keyword = array('RANDOM()', 'RANDOM()');
 
 	// --------------------------------------------------------------------
 
diff --git a/system/database/drivers/sqlite3/sqlite3_driver.php b/system/database/drivers/sqlite3/sqlite3_driver.php
index 9a2c1ee..f0818ae 100644
--- a/system/database/drivers/sqlite3/sqlite3_driver.php
+++ b/system/database/drivers/sqlite3/sqlite3_driver.php
@@ -54,9 +54,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' RANDOM()';
+	protected $_random_keyword = array('RANDOM()', 'RANDOM()');
 
 	// --------------------------------------------------------------------
 
diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php
index 09ccc02..a3d3177 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_driver.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php
@@ -53,9 +53,9 @@
 	/**
 	 * ORDER BY random keyword
 	 *
-	 * @var	string
+	 * @var	array
 	 */
-	protected $_random_keyword = ' NEWID()';
+	protected $_random_keyword = array('NEWID()', 'RAND(%d)');
 
 	/**
 	 * Quoted identifier flag
diff --git a/system/helpers/date_helper.php b/system/helpers/date_helper.php
index 0fa400d..7a0552b 100644
--- a/system/helpers/date_helper.php
+++ b/system/helpers/date_helper.php
@@ -677,8 +677,8 @@
 		$is_unix = ! ( ! $is_unix OR $is_unix === 'days');
 
 		// Validate input and try strtotime() on invalid timestamps/intervals, just in case
-		if ( ( ! preg_match('/^[0-9]+$/', $unix_start) && ($unix_start = @strtotime($unix_time)) === FALSE)
-			OR ( ! preg_match('/^[0-9]+$/', $mixed) && ($is_unix === FALSE OR ($mixed = @strtotime($mixed)) === FALSE))
+		if ( ( ! ctype_digit((string) $unix_start) && ($unix_start = @strtotime($unix_time)) === FALSE)
+			OR ( ! ctype_digit((string) $mixed) && ($is_unix === FALSE OR ($mixed = @strtotime($mixed)) === FALSE))
 			OR ($is_unix === TRUE && $mixed < $unix_start))
 		{
 			return FALSE;
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index d23be1a..92ccde6 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -1935,7 +1935,7 @@
 
 		$this->_debug_msg[] = '<pre>'.$cmd.': '.$reply.'</pre>';
 
-		if ( (int) substr($reply, 0, 3) !== $resp)
+		if ((int) substr($reply, 0, 3) !== $resp)
 		{
 			$this->_set_error_message('lang:email_smtp_error', $reply);
 			return FALSE;
@@ -2093,7 +2093,7 @@
 		$CI =& get_instance();
 		$CI->lang->load('email');
 
-		if (substr($msg, 0, 5) !== 'lang:' OR FALSE === ($line = $CI->lang->line(substr($msg, 5))))
+		if (sscanf($msg, 'lang:%s', $line) !== 1 OR FALSE === ($line = $CI->lang->line($line)))
 		{
 			$this->_debug_msg[] = str_replace('%s', $val, $msg).'<br />';
 		}
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php
index e50eee4..b0ba8bb 100644
--- a/system/libraries/Form_validation.php
+++ b/system/libraries/Form_validation.php
@@ -199,12 +199,10 @@
 
 		// Is the field name an array? If it is an array, we break it apart
 		// into its components so that we can fetch the corresponding POST data later
+		$indexes = array();
 		if (preg_match_all('/\[(.*?)\]/', $field, $matches))
 		{
-			// Note: Due to a bug in current() that affects some versions
-			// of PHP we can not pass function call directly into it
-			$x = explode('[', $field);
-			$indexes[] = current($x);
+			sscanf($field, '%[^[][', $indexes[0]);
 
 			for ($i = 0, $c = count($matches[0]); $i < $c; $i++)
 			{
@@ -218,7 +216,6 @@
 		}
 		else
 		{
-			$indexes	= array();
 			$is_array	= FALSE;
 		}
 
@@ -673,11 +670,7 @@
 			// Strip the parameter (if exists) from the rule
 			// Rules can contain a parameter: max_length[5]
 			$param = FALSE;
-			if (preg_match('/(.*?)\[(.*)\]/', $rule, $match))
-			{
-				$rule	= $match[1];
-				$param	= $match[2];
-			}
+			sscanf($rule, '%[^[][%[^]]', $rule, $param);
 
 			// Call the function that corresponds to the rule
 			if ($callback === TRUE)
@@ -796,11 +789,8 @@
 	{
 		// Do we need to translate the field name?
 		// We look for the prefix lang: to determine this
-		if (strpos($fieldname, 'lang:') === 0)
+		if (sscanf($fieldname, 'lang:%s', $line) === 1)
 		{
-			// Grab the variable
-			$line = substr($fieldname, 5);
-
 			// Were we able to translate the field name?  If not we use $line
 			if (FALSE === ($fieldname = $this->CI->lang->line($line)))
 			{
@@ -1002,7 +992,7 @@
 	 */
 	public function is_unique($str, $field)
 	{
-		list($table, $field) = explode('.', $field);
+		sscanf($field, '%[^.].%[^.]', $table, $field);
 		if (isset($this->CI->db))
 		{
 			$query = $this->CI->db->limit(1)->get_where($table, array($field => $str));
diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php
index 9379e3e..7f937f9 100644
--- a/system/libraries/Image_lib.php
+++ b/system/libraries/Image_lib.php
@@ -605,7 +605,7 @@
 		// Set the quality
 		$this->quality = trim(str_replace('%', '', $this->quality));
 
-		if ($this->quality === '' OR $this->quality === 0 OR ! preg_match('/^[0-9]+$/', $this->quality))
+		if ($this->quality === '' OR $this->quality === 0 OR ! ctype_digit($this->quality))
 		{
 			$this->quality = 90;
 		}
@@ -1277,8 +1277,8 @@
 		if ($this->wm_use_drop_shadow === FALSE)
 			$this->wm_shadow_distance = 0;
 
-		$this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1));
-		$this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1));
+		$this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);
+		$this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);
 
 		// Set verticle alignment
 		if ($this->wm_vrt_alignment === 'M')
@@ -1518,13 +1518,13 @@
 	public function image_reproportion()
 	{
 		if (($this->width === 0 && $this->height === 0) OR $this->orig_width === 0 OR $this->orig_height === 0
-			OR ( ! preg_match('/^[0-9]+$/', $this->width) && ! preg_match('/^[0-9]+$/', $this->height))
-			OR ! preg_match('/^[0-9]+$/', $this->orig_width) OR ! preg_match('/^[0-9]+$/', $this->orig_height))
+			OR ( ! ctype_digit((string) $this->width) && ! ctype_digit((string) $this->height))
+			OR ! ctype_digit((string) $this->orig_width) OR ! ctype_digit((string) $this->orig_height))
 		{
 			return;
 		}
 
-		// Sanitize so we don't call preg_match() anymore
+		// Sanitize
 		$this->width = (int) $this->width;
 		$this->height = (int) $this->height;
 
diff --git a/system/libraries/Parser.php b/system/libraries/Parser.php
index d29eede..faa94b7 100644
--- a/system/libraries/Parser.php
+++ b/system/libraries/Parser.php
@@ -58,6 +58,20 @@
 	 */
 	protected $CI;
 
+	// --------------------------------------------------------------------
+
+	/**
+	 * Class constructor
+	 *
+	 * @return	void
+	 */
+	public function __construct()
+	{
+		$this->CI =& get_instance();
+	}
+
+	// --------------------------------------------------------------------
+
 	/**
 	 * Parse a template
 	 *
@@ -71,7 +85,6 @@
 	 */
 	public function parse($template, $data, $return = FALSE)
 	{
-		$this->CI =& get_instance();
 		$template = $this->CI->load->view($template, $data, TRUE);
 
 		return $this->_parse($template, $data, $return);
diff --git a/system/libraries/Trackback.php b/system/libraries/Trackback.php
index c923a62..d303503 100644
--- a/system/libraries/Trackback.php
+++ b/system/libraries/Trackback.php
@@ -394,7 +394,7 @@
 			}
 		}
 
-		return preg_match('/^[0-9]+$/', $tb_id) ? $tb_id : FALSE;
+		return ctype_digit((string) $tb_id) ? $tb_id : FALSE;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 22d9748..4f9a70a 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -126,6 +126,7 @@
 	 - Added an optional parameter that allows to disable escaping (useful for custom fields) for methods ``join()``, ``order_by()``, ``where_in()``, ``or_where_in()``, ``where_not_in()``, ``or_where_not_in()``, ``insert()``, ``insert_batch()``.
 	 - Added support for ``join()`` with multiple conditions.
 	 - Added support for *USING* in ``join()``.
+	 - Added seed values support for random ordering with ``order_by(seed, 'RANDOM')``.
 	 - Changed ``limit()`` to ignore NULL values instead of always casting to integer.
 	 - Changed ``offset()`` to ignore empty values instead of always casting to integer.
 	 - Methods ``insert_batch()`` and ``update_batch()`` now return an integer representing the number of rows affected by them.
diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst
index 61cd7df..8fb9060 100644
--- a/user_guide_src/source/database/query_builder.rst
+++ b/user_guide_src/source/database/query_builder.rst
@@ -469,25 +469,41 @@
 $this->db->order_by()
 =====================
 
-Lets you set an ORDER BY clause. The first parameter contains the name
-of the column you would like to order by. The second parameter lets you
-set the direction of the result. Options are asc or desc, or random.
+Lets you set an ORDER BY clause.
+
+The first parameter contains the name of the column you would like to order by.
+
+The second parameter lets you set the direction of the result.
+Options are **ASC**, **DESC** AND **RANDOM**.
 
 ::
 
-	$this->db->order_by("title", "desc");  // Produces: ORDER BY title DESC
+	$this->db->order_by('title', 'DESC');
+	// Produces: ORDER BY `title` DESC
 
 You can also pass your own string in the first parameter::
 
-	$this->db->order_by('title desc, name asc');  // Produces: ORDER BY title DESC, name ASC
+	$this->db->order_by('title DESC, name ASC');
+	// Produces: ORDER BY `title` DESC, `name` ASC
 
 Or multiple function calls can be made if you need multiple fields.
 
 ::
 
-	$this->db->order_by("title", "desc");
-	$this->db->order_by("name", "asc"); // Produces: ORDER BY title DESC, name ASC
+	$this->db->order_by('title', 'DESC');
+	$this->db->order_by('name', 'ASC');
+	// Produces: ORDER BY `title` DESC, `name` ASC
 
+If you choose the **RANDOM** direction option, then the first parameters will
+be ignored, unless you specify a numeric seed value.
+
+::
+
+	$this->db->order_by('title', 'RANDOM');
+	// Produces: ORDER BY RAND()
+
+	$this->db->order_by(42, 'RANDOM');
+	// Produces: ORDER BY RAND(42)
 
 .. note:: order_by() was formerly known as orderby(), which has been
 	removed.