Merge pull request #1398 from gintsmurans/develop

Change error_reporting(0) to ini_set('display_errors', 0);
diff --git a/DCO.txt b/DCO.txt
new file mode 100644
index 0000000..a404c0d
--- /dev/null
+++ b/DCO.txt
@@ -0,0 +1,25 @@
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(1)	The contribution was created in whole or in part by me and I
+	have the right to submit it under the open source license
+	indicated in the file; or
+
+(2)	The contribution is based upon previous work that, to the best
+	of my knowledge, is covered under an appropriate open source
+	license and I have the right under that license to submit that
+	work with modifications, whether created in whole or in part
+	by me, under the same open source license (unless I am
+	permitted to submit under a different license), as indicated
+	in the file; or
+
+(3)	The contribution was provided directly to me by some other
+	person who certified (1), (2) or (3) and I have not modified
+	it.
+
+(4)	I understand and agree that this project and the contribution
+	are public and that a record of the contribution (including all
+	personal information I submit with it, including my sign-off) is
+	maintained indefinitely and may be redistributed consistent with
+	this project or the open source license(s) involved.
diff --git a/application/config/config.php b/application/config/config.php
index 2628885..28fc406 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -204,7 +204,7 @@
 |	4 = All Messages
 |
 | You can also pass in a array with threshold levels to show individual error types
-| 
+|
 | 	array(2) = Debug Messages, without Error Messages
 |
 | For a live site you'll usually only enable Errors (1) to be logged otherwise
@@ -253,7 +253,7 @@
 |
 | If you use the Encryption class or the Session class you
 | MUST set an encryption key.  See the user guide for info.
-|  
+|
 | http://codeigniter.com/user_guide/libraries/encryption.html
 | http://codeigniter.com/user_guide/libraries/sessions.html
 |
@@ -297,12 +297,12 @@
 | 'cookie_domain' = Set to .your-domain.com for site-wide cookies
 | 'cookie_path'   =  Typically will be a forward slash
 | 'cookie_secure' =  Cookies will only be set if a secure HTTPS connection exists.
-| 'cookie_httponly' = Cookie will only be accessible via HTTP(S) (no javascript) 
+| 'cookie_httponly' = Cookie will only be accessible via HTTP(S) (no javascript)
 |
 */
-$config['cookie_prefix']	= "";
-$config['cookie_domain']	= "";
-$config['cookie_path']		= "/";
+$config['cookie_prefix']	= '';
+$config['cookie_domain']	= '';
+$config['cookie_path']		= '/';
 $config['cookie_secure']	= FALSE;
 $config['cookie_httponly'] 	= FALSE;
 
@@ -359,13 +359,25 @@
 
 /*
 |--------------------------------------------------------------------------
+| Minify
+|--------------------------------------------------------------------------
+|
+| Removes extra characters (usually unnecessary spaces) from your
+| output for faster page load speeds.  Makes your outputted HTML source
+| code less readable.
+|
+*/
+$config['minify_output'] = FALSE;
+
+/*
+|--------------------------------------------------------------------------
 | Master Time Reference
 |--------------------------------------------------------------------------
 |
-| Options are 'local' or 'gmt'.  This pref tells the system whether to use
-| your server's local time as the master 'now' reference, or convert it to
-| GMT.  See the 'date helper' page of the user guide for information
-| regarding date handling.
+| Options are 'local' or any PHP supported timezone. This preference tells
+| the system whether to use your server's local time as the master 'now'
+| reference, or convert it to the configured one timezone. See the 'date
+| helper' page of the user guide for information regarding date handling.
 |
 */
 $config['time_reference'] = 'local';
@@ -398,5 +410,6 @@
 $config['proxy_ips'] = '';
 
 
+
 /* End of file config.php */
 /* Location: ./application/config/config.php */
diff --git a/application/config/database.php b/application/config/database.php
index 1949873..bb0d87b 100644
--- a/application/config/database.php
+++ b/application/config/database.php
@@ -42,8 +42,9 @@
 |	['username'] The username used to connect to the database
 |	['password'] The password used to connect to the database
 |	['database'] The name of the database you want to connect to
-|	['dbdriver'] The database type. e.g.: mysql.  Currently supported:
-|				 cubrid, interbase, mssql, mysql, mysqli, oci8, 
+|	['dbdriver'] The database driver. e.g.: mysqli.
+			Currently supported:
+|				 cubrid, ibase, mssql, mysql, mysqli, oci8,
 |				 odbc, pdo, postgre, sqlite, sqlite3, sqlsrv
 |	['dbprefix'] You can add an optional prefix, which will be added
 |				 to the table name when using the  Query Builder class
@@ -84,7 +85,7 @@
 	'database' => '',
 	'dbdriver' => 'mysqli',
 	'dbprefix' => '',
-	'pconnect' => FALSE,
+	'pconnect' => TRUE,
 	'db_debug' => TRUE,
 	'cache_on' => FALSE,
 	'cachedir' => '',
diff --git a/application/config/migration.php b/application/config/migration.php
index 88e3982..7645ade 100644
--- a/application/config/migration.php
+++ b/application/config/migration.php
@@ -44,7 +44,7 @@
 |
 | This is the name of the table that will store the current migrations state.
 | When migrations runs it will store in a database table which migration
-| level the system is at. It then compares the migration level in the this
+| level the system is at. It then compares the migration level in this
 | table to the $config['migration_version'] if they are not the same it
 | will migrate up. This must be set.
 |
diff --git a/application/config/mimes.php b/application/config/mimes.php
index 02e12c0..4b1d6a8 100644
--- a/application/config/mimes.php
+++ b/application/config/mimes.php
@@ -34,136 +34,138 @@
 |
 */
 
-$mimes = array(
-				'hqx'	=>	array('application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'),
-				'cpt'	=>	'application/mac-compactpro',
-				'csv'	=>	array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel'),
-				'bin'	=>	array('application/macbinary', 'application/mac-binary', 'application/octet-stream', 'application/x-binary', 'application/x-macbinary'),
-				'dms'	=>	'application/octet-stream',
-				'lha'	=>	'application/octet-stream',
-				'lzh'	=>	'application/octet-stream',
-				'exe'	=>	array('application/octet-stream', 'application/x-msdownload'),
-				'class'	=>	'application/octet-stream',
-				'psd'	=>	'application/x-photoshop',
-				'so'	=>	'application/octet-stream',
-				'sea'	=>	'application/octet-stream',
-				'dll'	=>	'application/octet-stream',
-				'oda'	=>	'application/oda',
-				'pdf'	=>	array('application/pdf', 'application/x-download'),
-				'ai'	=>	'application/postscript',
-				'eps'	=>	'application/postscript',
-				'ps'	=>	'application/postscript',
-				'smi'	=>	'application/smil',
-				'smil'	=>	'application/smil',
-				'mif'	=>	'application/vnd.mif',
-				'xls'	=>	array('application/excel', 'application/vnd.ms-excel', 'application/msexcel'),
-				'ppt'	=>	array('application/powerpoint', 'application/vnd.ms-powerpoint'),
-				'pptx'	=> 	'application/vnd.openxmlformats-officedocument.presentationml.presentation',
-				'wbxml'	=>	'application/wbxml',
-				'wmlc'	=>	'application/wmlc',
-				'dcr'	=>	'application/x-director',
-				'dir'	=>	'application/x-director',
-				'dxr'	=>	'application/x-director',
-				'dvi'	=>	'application/x-dvi',
-				'gtar'	=>	'application/x-gtar',
-				'gz'	=>	'application/x-gzip',
-				'gzip'  =>	'application/x-gzip',
-				'php'	=>	'application/x-httpd-php',
-				'php4'	=>	'application/x-httpd-php',
-				'php3'	=>	'application/x-httpd-php',
-				'phtml'	=>	'application/x-httpd-php',
-				'phps'	=>	'application/x-httpd-php-source',
-				'js'	=>	'application/x-javascript',
-				'swf'	=>	'application/x-shockwave-flash',
-				'sit'	=>	'application/x-stuffit',
-				'tar'	=>	'application/x-tar',
-				'tgz'	=>	array('application/x-tar', 'application/x-gzip-compressed'),
-				'xhtml'	=>	'application/xhtml+xml',
-				'xht'	=>	'application/xhtml+xml',
-				'zip'	=>	array('application/x-zip', 'application/zip', 'application/x-zip-compressed'),
-				'mid'	=>	'audio/midi',
-				'midi'	=>	'audio/midi',
-				'mpga'	=>	'audio/mpeg',
-				'mp2'	=>	'audio/mpeg',
-				'mp3'	=>	array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'),
-				'aif'	=>	array('audio/x-aiff', 'audio/aiff'),
-				'aiff'	=>	array('audio/x-aiff', 'audio/aiff'),
-				'aifc'	=>	'audio/x-aiff',
-				'ram'	=>	'audio/x-pn-realaudio',
-				'rm'	=>	'audio/x-pn-realaudio',
-				'rpm'	=>	'audio/x-pn-realaudio-plugin',
-				'ra'	=>	'audio/x-realaudio',
-				'rv'	=>	'video/vnd.rn-realvideo',
-				'wav'	=>	array('audio/x-wav', 'audio/wave', 'audio/wav'),
-				'bmp'	=>	array('image/bmp', 'image/x-windows-bmp'),
-				'gif'	=>	'image/gif',
-				'jpeg'	=>	array('image/jpeg', 'image/pjpeg'),
-				'jpg'	=>	array('image/jpeg', 'image/pjpeg'),
-				'jpe'	=>	array('image/jpeg', 'image/pjpeg'),
-				'png'	=>	array('image/png',  'image/x-png'),
-				'tiff'	=>	'image/tiff',
-				'tif'	=>	'image/tiff',
-				'css'	=>	'text/css',
-				'html'	=>	'text/html',
-				'htm'	=>	'text/html',
-				'shtml'	=>	'text/html',
-				'txt'	=>	'text/plain',
-				'text'	=>	'text/plain',
-				'log'	=>	array('text/plain', 'text/x-log'),
-				'rtx'	=>	'text/richtext',
-				'rtf'	=>	'text/rtf',
-				'xml'	=>	array('application/xml', 'text/xml'),
-				'xsl'	=>	array('application/xml', 'text/xsl', 'text/xml'),
-				'mpeg'	=>	'video/mpeg',
-				'mpg'	=>	'video/mpeg',
-				'mpe'	=>	'video/mpeg',
-				'qt'	=>	'video/quicktime',
-				'mov'	=>	'video/quicktime',
-				'avi'	=>	array('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'),
-				'movie'	=>	'video/x-sgi-movie',
-				'doc'	=>	array('application/msword', 'application/vnd.ms-office'),
-				'docx'	=>	array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip'),
-				'xlsx'	=>	array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip'),
-				'word'	=>	array('application/msword', 'application/octet-stream'),
-				'xl'	=>	'application/excel',
-				'eml'	=>	'message/rfc822',
-				'json'  =>	array('application/json', 'text/json'),
-				'pem'   =>	array('application/x-x509-user-cert', 'application/x-pem-file', 'application/octet-stream'),
-				'p10'   =>	array('application/x-pkcs10', 'application/pkcs10'),
-				'p12'   =>	'application/x-pkcs12',
-				'p7a'   =>	'application/x-pkcs7-signature',
-				'p7c'   =>	array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
-				'p7m'   =>	array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
-				'p7r'   =>	'application/x-pkcs7-certreqresp',
-				'p7s'   =>	'application/pkcs7-signature',
-				'crt'   =>	array('application/x-x509-ca-cert', 'application/x-x509-user-cert', 'application/pkix-cert'),
-				'crl'   =>	array('application/pkix-crl', 'application/pkcs-crl'),
-				'der'   =>	'application/x-x509-ca-cert',
-				'kdb'   =>	'application/octet-stream',
-				'pgp'   =>	'application/pgp',
-				'gpg'   =>	'application/gpg-keys',
-				'sst'   =>	'application/octet-stream',
-				'csr'   =>	'application/octet-stream',
-				'rsa'   =>	'application/x-pkcs7',
-				'cer'   =>	array('application/pkix-cert', 'application/x-x509-ca-cert'),
-				'3g2'   =>	'video/3gpp2',
-				'3gp'   =>	'video/3gp',
-				'mp4'   =>	'video/mp4',
-				'm4a'   =>	'audio/x-m4a',
-				'f4v'   =>	'video/mp4',
-				'aac'   =>	'audio/x-acc',
-				'm4u'   =>	'application/vnd.mpegurl',
-				'm3u'   =>	'text/plain',
-				'xspf'  =>	'application/xspf+xml',
-				'vlc'   =>	'application/videolan',
-				'wmv'   =>	'video/x-ms-wmv',
-				'au'    =>	'audio/x-au',
-				'ac3'   =>	'audio/ac3',
-				'flac'  =>	'audio/x-flac',
-				'ogg'   =>	'audio/ogg',
-				'kmz'	=>	array('application/vnd.google-earth.kmz', 'application/zip', 'application/x-zip'),
-				'kml'	=>	array('application/vnd.google-earth.kml+xml', 'application/xml', 'text/xml')
-			);
+return array(
+	'hqx'	=>	array('application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'),
+	'cpt'	=>	'application/mac-compactpro',
+	'csv'	=>	array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel'),
+	'bin'	=>	array('application/macbinary', 'application/mac-binary', 'application/octet-stream', 'application/x-binary', 'application/x-macbinary'),
+	'dms'	=>	'application/octet-stream',
+	'lha'	=>	'application/octet-stream',
+	'lzh'	=>	'application/octet-stream',
+	'exe'	=>	array('application/octet-stream', 'application/x-msdownload'),
+	'class'	=>	'application/octet-stream',
+	'psd'	=>	'application/x-photoshop',
+	'so'	=>	'application/octet-stream',
+	'sea'	=>	'application/octet-stream',
+	'dll'	=>	'application/octet-stream',
+	'oda'	=>	'application/oda',
+	'pdf'	=>	array('application/pdf', 'application/x-download'),
+	'ai'	=>	'application/postscript',
+	'eps'	=>	'application/postscript',
+	'ps'	=>	'application/postscript',
+	'smi'	=>	'application/smil',
+	'smil'	=>	'application/smil',
+	'mif'	=>	'application/vnd.mif',
+	'xls'	=>	array('application/excel', 'application/vnd.ms-excel', 'application/msexcel'),
+	'ppt'	=>	array('application/powerpoint', 'application/vnd.ms-powerpoint'),
+	'pptx'	=> 	'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+	'wbxml'	=>	'application/wbxml',
+	'wmlc'	=>	'application/wmlc',
+	'dcr'	=>	'application/x-director',
+	'dir'	=>	'application/x-director',
+	'dxr'	=>	'application/x-director',
+	'dvi'	=>	'application/x-dvi',
+	'gtar'	=>	'application/x-gtar',
+	'gz'	=>	'application/x-gzip',
+	'gzip'  =>	'application/x-gzip',
+	'php'	=>	array('application/x-httpd-php', 'application/php', 'application/x-php', 'text/php', 'text/x-php', 'application/x-httpd-php-source'),
+	'php4'	=>	'application/x-httpd-php',
+	'php3'	=>	'application/x-httpd-php',
+	'phtml'	=>	'application/x-httpd-php',
+	'phps'	=>	'application/x-httpd-php-source',
+	'js'	=>	'application/x-javascript',
+	'swf'	=>	'application/x-shockwave-flash',
+	'sit'	=>	'application/x-stuffit',
+	'tar'	=>	'application/x-tar',
+	'tgz'	=>	array('application/x-tar', 'application/x-gzip-compressed'),
+	'xhtml'	=>	'application/xhtml+xml',
+	'xht'	=>	'application/xhtml+xml',
+	'zip'	=>	array('application/x-zip', 'application/zip', 'application/x-zip-compressed', 'application/s-compressed', 'multipart/x-zip'),
+	'rar'	=>	array('application/x-rar', 'application/rar', 'application/x-rar-compressed'),
+	'mid'	=>	'audio/midi',
+	'midi'	=>	'audio/midi',
+	'mpga'	=>	'audio/mpeg',
+	'mp2'	=>	'audio/mpeg',
+	'mp3'	=>	array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'),
+	'aif'	=>	array('audio/x-aiff', 'audio/aiff'),
+	'aiff'	=>	array('audio/x-aiff', 'audio/aiff'),
+	'aifc'	=>	'audio/x-aiff',
+	'ram'	=>	'audio/x-pn-realaudio',
+	'rm'	=>	'audio/x-pn-realaudio',
+	'rpm'	=>	'audio/x-pn-realaudio-plugin',
+	'ra'	=>	'audio/x-realaudio',
+	'rv'	=>	'video/vnd.rn-realvideo',
+	'wav'	=>	array('audio/x-wav', 'audio/wave', 'audio/wav'),
+	'bmp'	=>	array('image/bmp', 'image/x-windows-bmp'),
+	'gif'	=>	'image/gif',
+	'jpeg'	=>	array('image/jpeg', 'image/pjpeg'),
+	'jpg'	=>	array('image/jpeg', 'image/pjpeg'),
+	'jpe'	=>	array('image/jpeg', 'image/pjpeg'),
+	'png'	=>	array('image/png',  'image/x-png'),
+	'tiff'	=>	'image/tiff',
+	'tif'	=>	'image/tiff',
+	'css'	=>	'text/css',
+	'html'	=>	'text/html',
+	'htm'	=>	'text/html',
+	'shtml'	=>	'text/html',
+	'txt'	=>	'text/plain',
+	'text'	=>	'text/plain',
+	'log'	=>	array('text/plain', 'text/x-log'),
+	'rtx'	=>	'text/richtext',
+	'rtf'	=>	'text/rtf',
+	'xml'	=>	array('application/xml', 'text/xml'),
+	'xsl'	=>	array('application/xml', 'text/xsl', 'text/xml'),
+	'mpeg'	=>	'video/mpeg',
+	'mpg'	=>	'video/mpeg',
+	'mpe'	=>	'video/mpeg',
+	'qt'	=>	'video/quicktime',
+	'mov'	=>	'video/quicktime',
+	'avi'	=>	array('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'),
+	'movie'	=>	'video/x-sgi-movie',
+	'doc'	=>	array('application/msword', 'application/vnd.ms-office'),
+	'docx'	=>	array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip'),
+	'xlsx'	=>	array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip'),
+	'word'	=>	array('application/msword', 'application/octet-stream'),
+	'xl'	=>	'application/excel',
+	'eml'	=>	'message/rfc822',
+	'json'  =>	array('application/json', 'text/json'),
+	'pem'   =>	array('application/x-x509-user-cert', 'application/x-pem-file', 'application/octet-stream'),
+	'p10'   =>	array('application/x-pkcs10', 'application/pkcs10'),
+	'p12'   =>	'application/x-pkcs12',
+	'p7a'   =>	'application/x-pkcs7-signature',
+	'p7c'   =>	array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
+	'p7m'   =>	array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
+	'p7r'   =>	'application/x-pkcs7-certreqresp',
+	'p7s'   =>	'application/pkcs7-signature',
+	'crt'   =>	array('application/x-x509-ca-cert', 'application/x-x509-user-cert', 'application/pkix-cert'),
+	'crl'   =>	array('application/pkix-crl', 'application/pkcs-crl'),
+	'der'   =>	'application/x-x509-ca-cert',
+	'kdb'   =>	'application/octet-stream',
+	'pgp'   =>	'application/pgp',
+	'gpg'   =>	'application/gpg-keys',
+	'sst'   =>	'application/octet-stream',
+	'csr'   =>	'application/octet-stream',
+	'rsa'   =>	'application/x-pkcs7',
+	'cer'   =>	array('application/pkix-cert', 'application/x-x509-ca-cert'),
+	'3g2'   =>	'video/3gpp2',
+	'3gp'   =>	'video/3gp',
+	'mp4'   =>	'video/mp4',
+	'm4a'   =>	'audio/x-m4a',
+	'f4v'   =>	'video/mp4',
+	'aac'   =>	'audio/x-acc',
+	'm4u'   =>	'application/vnd.mpegurl',
+	'm3u'   =>	'text/plain',
+	'xspf'  =>	'application/xspf+xml',
+	'vlc'   =>	'application/videolan',
+	'wmv'   =>	'video/x-ms-wmv',
+	'au'    =>	'audio/x-au',
+	'ac3'   =>	'audio/ac3',
+	'flac'  =>	'audio/x-flac',
+	'ogg'   =>	'audio/ogg',
+	'kmz'	=>	array('application/vnd.google-earth.kmz', 'application/zip', 'application/x-zip'),
+	'kml'	=>	array('application/vnd.google-earth.kml+xml', 'application/xml', 'text/xml'),
+	'ics'	=>	'text/calendar'
+);
 
 /* End of file mimes.php */
 /* Location: ./application/config/mimes.php */
\ No newline at end of file
diff --git a/application/config/user_agents.php b/application/config/user_agents.php
index 7611461..416ef56 100644
--- a/application/config/user_agents.php
+++ b/application/config/user_agents.php
@@ -29,11 +29,10 @@
 | -------------------------------------------------------------------
 | USER AGENT TYPES
 | -------------------------------------------------------------------
-| This file contains four arrays of user agent data.  It is used by the
+| This file contains four arrays of user agent data. It is used by the
 | User Agent Class to help identify browser, platform, robot, and
-| mobile device data.  The array keys are used to identify the device
+| mobile device data. The array keys are used to identify the device
 | and the array values are used to set the actual name of the item.
-|
 */
 
 $platforms = array(
@@ -179,6 +178,7 @@
 	'operamini'		=> 'Opera Mini',
 	'opera mini'	=> 'Opera Mini',
 	'opera mobi'	=> 'Opera Mobile',
+	'fennec'	=> 'Firefox Mobile',
 
 	// Other
 	'digital paths'	=> 'Digital Paths',
diff --git a/application/views/welcome_message.php b/application/views/welcome_message.php
index 45360da..65f62a9 100644
--- a/application/views/welcome_message.php
+++ b/application/views/welcome_message.php
@@ -107,7 +107,7 @@
 		<p>If you are exploring CodeIgniter for the very first time, you should start by reading the <a href="user_guide/">User Guide</a>.</p>
 	</div>
 
-	<p class="footer">Page rendered in <strong>{elapsed_time}</strong> seconds. <?php echo  (ENVIRONMENT == 'development') ?  'CodeIgniter Version <strong>' . CI_VERSION . '</strong>' : '' ?></p>
+	<p class="footer">Page rendered in <strong>{elapsed_time}</strong> seconds. <?php echo  (ENVIRONMENT === 'development') ?  'CodeIgniter Version <strong>' . CI_VERSION . '</strong>' : '' ?></p>
 </div>
 
 </body>
diff --git a/index.php b/index.php
index a11c5c1..c379f72 100644
--- a/index.php
+++ b/index.php
@@ -42,7 +42,7 @@
  *
  * NOTE: If you change these, also change the error_reporting() code below
  */
-	define('ENVIRONMENT', 'development');
+	define('ENVIRONMENT', isset($_SERVER['CI_ENV']) ? $_SERVER['CI_ENV'] : 'development');
 /*
  *---------------------------------------------------------------
  * ERROR REPORTING
@@ -175,17 +175,20 @@
 		chdir(dirname(__FILE__));
 	}
 
-	if (realpath($system_path) !== FALSE)
+	if (($_temp = realpath($system_path)) !== FALSE)
 	{
-		$system_path = realpath($system_path).'/';
+		$system_path = $_temp.'/';
 	}
-
-	// ensure there's a trailing slash
-	$system_path = rtrim($system_path, '/').'/';
+	else
+	{
+		// Ensure there's a trailing slash
+		$system_path = rtrim($system_path, '/').'/';
+	}
 
 	// Is the system path correct?
 	if ( ! is_dir($system_path))
 	{
+		header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
 		exit('Your system folder path does not appear to be set correctly. Please open the following file and correct this: '.pathinfo(__FILE__, PATHINFO_BASENAME));
 	}
 
@@ -197,10 +200,6 @@
 	// The name of THIS file
 	define('SELF', pathinfo(__FILE__, PATHINFO_BASENAME));
 
-	// The PHP file extension
-	// this global constant is deprecated.
-	define('EXT', '.php');
-
 	// Path to the system folder
 	define('BASEPATH', str_replace('\\', '/', $system_path));
 
@@ -213,13 +212,18 @@
 	// The path to the "application" folder
 	if (is_dir($application_folder))
 	{
+		if (($_temp = realpath($application_folder)) !== FALSE)
+		{
+			$application_folder = $_temp;
+		}
+
 		define('APPPATH', $application_folder.'/');
 	}
 	else
 	{
 		if ( ! is_dir(BASEPATH.$application_folder.'/'))
 		{
-			header('HTTP/1.1 503 Service Unavailable.', TRUE, '503');
+			header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
 			exit('Your application folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF);
 		}
 
@@ -227,21 +231,34 @@
 	}
 
 	// The path to the "views" folder
-	if (is_dir($view_folder))
+	if ( ! is_dir($view_folder))
 	{
-		define ('VIEWPATH', $view_folder .'/');
+		if ( ! empty($view_folder) && is_dir(APPPATH.$view_folder.'/'))
+		{
+			$view_folder = APPPATH.$view_folder;
+		}
+		elseif ( ! is_dir(APPPATH.'views/'))
+		{
+			header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
+			exit('Your view folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF);
+		}
+		else
+		{
+			$view_folder = APPPATH.'views';
+		}
+	}
+
+	if (($_temp = realpath($view_folder)) !== FALSE)
+	{
+		$view_folder = realpath($view_folder).'/';
 	}
 	else
 	{
-		if ( ! is_dir(APPPATH.'views/'))
-		{
-			header('HTTP/1.1 503 Service Unavailable.', TRUE, '503');
-			exit('Your view folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF);
-		}
-
-		define ('VIEWPATH', APPPATH.'views/' );
+		$view_folder = rtrim($view_folder, '/').'/';
 	}
 
+	define('VIEWPATH', $view_folder);
+
 /*
  * --------------------------------------------------------------------
  * LOAD THE BOOTSTRAP FILE
diff --git a/readme.rst b/readme.rst
index 4707847..b211ad7 100644
--- a/readme.rst
+++ b/readme.rst
@@ -115,6 +115,34 @@
 cannot merge the request. Using the Git-Flow branching model you can create
 new branches for both of these features and send two requests.
 
+Signing
+=======
+You must sign your work, certifying that you either wrote the work or
+otherwise have the right to pass it on to an open source project. git makes
+this trivial as you merely have to use `--signoff` on your commits to your
+CodeIgniter fork.
+
+::
+
+	git commit --signoff
+
+or simply::
+
+	git commit -s
+
+This will sign your commits with the information setup in your git config, e.g.
+
+	Signed-off-by: John Q Public <john.public@example.com>
+
+If you are using Tower there is a "Sign-Off" checkbox in the commit window. You 
+could even alias git commit to use the -s flag so you don’t have to think about 
+it.
+
+By signing your work in this manner, you certify to a "Developer's Certificate 
+or Origin". The current version of this certificate is in the `DCO.txt` file
+in the root of this repository.
+
+
 ************
 How-to Guide
 ************
@@ -183,8 +211,6 @@
 
 -  `User Guide <http://codeigniter.com/user_guide/>`_
 -  `Community Forums <http://codeigniter.com/forums/>`_
--  `User
-   Voice <http://codeigniter.uservoice.com/forums/40508-codeigniter-reactor>`_
 -  `Community Wiki <http://codeigniter.com/wiki/>`_
 -  `Community IRC <http://codeigniter.com/irc/>`_
 
diff --git a/system/core/Benchmark.php b/system/core/Benchmark.php
old mode 100755
new mode 100644
index bb630f4..2fabdf4
--- a/system/core/Benchmark.php
+++ b/system/core/Benchmark.php
@@ -79,7 +79,7 @@
 	 */
 	public function elapsed_time($point1 = '', $point2 = '', $decimals = 4)
 	{
-		if ($point1 == '')
+		if ($point1 === '')
 		{
 			return '{elapsed_time}';
 		}
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php
old mode 100755
new mode 100644
index c8245fc..8159b19
--- a/system/core/CodeIgniter.php
+++ b/system/core/CodeIgniter.php
@@ -73,9 +73,9 @@
  */
 	set_error_handler('_exception_handler');
 
-	if ( ! is_php('5.3'))
+	if ( ! is_php('5.4'))
 	{
-		@set_magic_quotes_runtime(0); // Kill magic quotes
+		@ini_set('magic_quotes_runtime', 0); // Kill magic quotes
 	}
 
 /*
@@ -94,7 +94,7 @@
  * Note: Since the config file data is cached it doesn't
  * hurt to load it here.
  */
-	if (isset($assign_to_config['subclass_prefix']) && $assign_to_config['subclass_prefix'] != '')
+	if ( ! empty($assign_to_config['subclass_prefix']))
 	{
 		get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));
 	}
@@ -182,7 +182,7 @@
  * ------------------------------------------------------
  */
 	if ($EXT->call_hook('cache_override') === FALSE
-		&& $OUT->_display_cache($CFG, $URI) == TRUE)
+		&& $OUT->_display_cache($CFG, $URI) === TRUE)
 	{
 		exit;
 	}
diff --git a/system/core/Common.php b/system/core/Common.php
index 4b733ac..7e93ed4 100644
--- a/system/core/Common.php
+++ b/system/core/Common.php
@@ -44,13 +44,13 @@
 	/**
 	 * Determines if the current version of PHP is greater then the supplied value
 	 *
-	 * Since there are a few places where we conditionally test for PHP > 5
+	 * Since there are a few places where we conditionally test for PHP > 5.3
 	 * we'll set a static variable.
 	 *
 	 * @param	string
 	 * @return	bool	TRUE if the current version is $version or higher
 	 */
-	function is_php($version = '5.0.0')
+	function is_php($version = '5.3.0')
 	{
 		static $_is_php;
 		$version = (string) $version;
@@ -200,7 +200,7 @@
 	{
 		static $_is_loaded = array();
 
-		if ($class != '')
+		if ($class !== '')
 		{
 			$_is_loaded[strtolower($class)] = $class;
 		}
@@ -231,21 +231,25 @@
 			return $_config[0];
 		}
 
-		// Is the config file in the environment folder?
-		if ( ! defined('ENVIRONMENT') OR ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))
+		$file_path = APPPATH.'config/config.php';
+		$found = FALSE;
+		if (file_exists($file_path))
 		{
-			$file_path = APPPATH.'config/config.php';
+			$found = TRUE;
+			require($file_path);
 		}
 
-		// Fetch the config file
-		if ( ! file_exists($file_path))
+		// Is the config file in the environment folder?
+		if (defined('ENVIRONMENT') && file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))
+		{
+			require($file_path);
+		}
+		elseif ( ! $found)
 		{
 			set_status_header(503);
 			exit('The configuration file does not exist.');
 		}
 
-		require($file_path);
-
 		// Does the $config array exist in the file?
 		if ( ! isset($config) OR ! is_array($config))
 		{
@@ -300,6 +304,32 @@
 
 // ------------------------------------------------------------------------
 
+if ( ! function_exists('get_mimes'))
+{
+	/**
+	 * Returns the MIME types array from config/mimes.php
+	 *
+	 * @return	array
+	 */
+	function &get_mimes()
+	{
+		static $_mimes = array();
+
+		if (defined('ENVIRONMENT') && is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
+		{
+			$_mimes = include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
+		}
+		elseif (is_file(APPPATH.'config/mimes.php'))
+		{
+			$_mimes = include(APPPATH.'config/mimes.php');
+		}
+
+		return $_mimes;
+	}
+}
+
+// ------------------------------------------------------------------------
+
 if ( ! function_exists('show_error'))
 {
 	/**
@@ -366,7 +396,7 @@
 	{
 		static $_log;
 
-		if (config_item('log_threshold') == 0)
+		if (config_item('log_threshold') === 0)
 		{
 			return;
 		}
@@ -401,6 +431,7 @@
 			300	=> 'Multiple Choices',
 			301	=> 'Moved Permanently',
 			302	=> 'Found',
+			303	=> 'See Other',
 			304	=> 'Not Modified',
 			305	=> 'Use Proxy',
 			307	=> 'Temporary Redirect',
@@ -432,19 +463,23 @@
 			505	=> 'HTTP Version Not Supported'
 		);
 
-		if ($code == '' OR ! is_numeric($code))
+		if (empty($code) OR ! is_numeric($code))
 		{
 			show_error('Status codes must be numeric', 500);
 		}
 
-		if (isset($stati[$code]) && $text == '')
-		{
-			$text = $stati[$code];
-		}
+		is_int($code) OR $code = (int) $code;
 
-		if ($text == '')
+		if (empty($text))
 		{
-			show_error('No status text available. Please check your status code number or supply your own message text.', 500);
+			if (isset($stati[$code]))
+			{
+				$text = $stati[$code];
+			}
+			else
+			{
+				show_error('No status text available. Please check your status code number or supply your own message text.', 500);
+			}
 		}
 
 		$server_protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : FALSE;
@@ -491,13 +526,14 @@
 
 		// Should we display the error? We'll get the current error_reporting
 		// level and add its bits with the severity bits to find out.
-		if (($severity & error_reporting()) == $severity)
+		// And respect display_errors
+		if (($severity & error_reporting()) === $severity && (bool) ini_get('display_errors') === TRUE)
 		{
 			$_error->show_php_error($severity, $message, $filepath, $line);
 		}
 
 		// Should we log the error? No? We're done...
-		if (config_item('log_threshold') == 0)
+		if (config_item('log_threshold') === 0)
 		{
 			return;
 		}
diff --git a/system/core/Config.php b/system/core/Config.php
old mode 100755
new mode 100644
index c07ffa5..2f6a9e0
--- a/system/core/Config.php
+++ b/system/core/Config.php
@@ -43,7 +43,7 @@
 	 *
 	 * @var array
 	 */
-	public $config =	array();
+	public $config = array();
 
 	/**
 	 * List of all loaded config files
@@ -100,15 +100,15 @@
 	 */
 	public function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
 	{
-		$file = ($file == '') ? 'config' : str_replace('.php', '', $file);
+		$file = ($file === '') ? 'config' : str_replace('.php', '', $file);
 		$found = $loaded = FALSE;
+		
+		$check_locations = defined('ENVIRONMENT')
+			? array(ENVIRONMENT.'/'.$file, $file)
+			: array($file);
 
 		foreach ($this->_config_paths as $path)
 		{
-			$check_locations = defined('ENVIRONMENT')
-				? array(ENVIRONMENT.'/'.$file, $file)
-				: array($file);
-
 			foreach ($check_locations as $location)
 			{
 				$file_path = $path.'config/'.$location.'.php';
@@ -172,7 +172,7 @@
 			{
 				return FALSE;
 			}
-			show_error('The configuration file '.$file.'.php'.' does not exist.');
+			show_error('The configuration file '.$file.'.php does not exist.');
 		}
 
 		return TRUE;
@@ -211,7 +211,7 @@
 		{
 			return FALSE;
 		}
-		elseif (trim($this->config[$item]) == '')
+		elseif (trim($this->config[$item]) === '')
 		{
 			return '';
 		}
@@ -225,25 +225,39 @@
 	 * Site URL
 	 * Returns base_url . index_page [. uri_string]
 	 *
-	 * @param	string	the URI string
+	 * @param	mixed	the URI string or an array of segments
 	 * @return	string
 	 */
 	public function site_url($uri = '')
 	{
-		if ($uri == '')
+		if (empty($uri))
 		{
 			return $this->slash_item('base_url').$this->item('index_page');
 		}
 
-		if ($this->item('enable_query_strings') == FALSE)
+		$uri = $this->_uri_string($uri);
+
+		if ($this->item('enable_query_strings') === FALSE)
 		{
-			$suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix');
-			return $this->slash_item('base_url').$this->slash_item('index_page').$this->_uri_string($uri).$suffix;
+			$suffix = ($this->item('url_suffix') === FALSE) ? '' : $this->item('url_suffix');
+
+			if ($suffix !== '' && ($offset = strpos($uri, '?')) !== FALSE)
+			{
+				$uri = substr($uri, 0, $offset).$suffix.substr($uri, $offset);
+			}
+			else
+			{
+				$uri .= $suffix;
+			}
+
+			return $this->slash_item('base_url').$this->slash_item('index_page').$uri;
 		}
-		else
+		elseif (strpos($uri, '?') === FALSE)
 		{
-			return $this->slash_item('base_url').$this->item('index_page').'?'.$this->_uri_string($uri);
+			$uri = '?'.$uri;
 		}
+
+		return $this->slash_item('base_url').$this->item('index_page').$uri;
 	}
 
 	// -------------------------------------------------------------
@@ -257,7 +271,7 @@
 	 */
 	public function base_url($uri = '')
 	{
-		return $this->slash_item('base_url').ltrim($this->_uri_string($uri),'/');
+		return $this->slash_item('base_url').ltrim($this->_uri_string($uri), '/');
 	}
 
 	// -------------------------------------------------------------
@@ -270,7 +284,7 @@
 	 */
 	protected function _uri_string($uri)
 	{
-		if ($this->item('enable_query_strings') == FALSE)
+		if ($this->item('enable_query_strings') === FALSE)
 		{
 			if (is_array($uri))
 			{
@@ -280,15 +294,7 @@
 		}
 		elseif (is_array($uri))
 		{
-			$i = 0;
-			$str = '';
-			foreach ($uri as $key => $val)
-			{
-				$prefix = ($i === 0) ? '' : '&';
-				$str .= $prefix.$key.'='.$val;
-				$i++;
-			}
-			return $str;
+			return http_build_query($uri);
 		}
 
 		return $uri;
diff --git a/system/core/Exceptions.php b/system/core/Exceptions.php
old mode 100755
new mode 100644
index 8c32085..bd9178d
--- a/system/core/Exceptions.php
+++ b/system/core/Exceptions.php
@@ -143,7 +143,7 @@
 			ob_end_flush();
 		}
 		ob_start();
-		include(APPPATH.'views/errors/'.$template.'.php');
+		include(VIEWPATH.'errors/'.$template.'.php');
 		$buffer = ob_get_contents();
 		ob_end_clean();
 		return $buffer;
@@ -177,7 +177,7 @@
 			ob_end_flush();
 		}
 		ob_start();
-		include(APPPATH.'views/errors/error_php.php');
+		include(VIEWPATH.'errors/error_php.php');
 		$buffer = ob_get_contents();
 		ob_end_clean();
 		echo $buffer;
diff --git a/system/core/Hooks.php b/system/core/Hooks.php
old mode 100755
new mode 100644
index 5bbb000..afbf4b4
--- a/system/core/Hooks.php
+++ b/system/core/Hooks.php
@@ -39,7 +39,7 @@
 class CI_Hooks {
 
 	/**
-	 * Determines wether hooks are enabled
+	 * Determines whether hooks are enabled
 	 *
 	 * @var bool
 	 */
@@ -53,7 +53,7 @@
 	public $hooks =	array();
 
 	/**
-	 * Determines wether hook is in progress, used to prevent infinte loops
+	 * Determines whether hook is in progress, used to prevent infinte loops
 	 *
 	 * @var bool
 	 */
@@ -72,7 +72,7 @@
 
 		// If hooks are not enabled in the config file
 		// there is nothing else to do
-		if ($CFG->item('enable_hooks') == FALSE)
+		if ($CFG->item('enable_hooks') === FALSE)
 		{
 			return;
 		}
diff --git a/system/core/Input.php b/system/core/Input.php
old mode 100755
new mode 100644
index 97be9e6..162e40c
--- a/system/core/Input.php
+++ b/system/core/Input.php
@@ -263,23 +263,27 @@
 			}
 		}
 
-		if ($prefix == '' && config_item('cookie_prefix') != '')
+		if ($prefix === '' && config_item('cookie_prefix') !== '')
 		{
 			$prefix = config_item('cookie_prefix');
 		}
+
 		if ($domain == '' && config_item('cookie_domain') != '')
 		{
 			$domain = config_item('cookie_domain');
 		}
-		if ($path == '/' && config_item('cookie_path') !== '/')
+
+		if ($path === '/' && config_item('cookie_path') !== '/')
 		{
 			$path = config_item('cookie_path');
 		}
-		if ($secure == FALSE && config_item('cookie_secure') != FALSE)
+
+		if ($secure === FALSE && config_item('cookie_secure') !== FALSE)
 		{
 			$secure = config_item('cookie_secure');
 		}
-		if ($httponly == FALSE && config_item('cookie_httponly') != FALSE)
+
+		if ($httponly === FALSE && config_item('cookie_httponly') !== FALSE)
 		{
 			$httponly = config_item('cookie_httponly');
 		}
@@ -372,14 +376,26 @@
 	/**
 	 * Validate IP Address
 	 *
-	 * Updated version suggested by Geert De Deckere
-	 *
 	 * @param	string
+	 * @param	string	'ipv4' or 'ipv6'
 	 * @return	bool
 	 */
-	public function valid_ip($ip)
+	public function valid_ip($ip, $which = '')
 	{
-		return (bool) filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
+		switch (strtolower($which))
+		{
+			case 'ipv4':
+				$which = FILTER_FLAG_IPV4;
+				break;
+			case 'ipv6':
+				$which = FILTER_FLAG_IPV6;
+				break;
+			default:
+				$which = NULL;
+				break;
+		}
+
+		return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which);
 	}
 
 	// --------------------------------------------------------------------
@@ -459,7 +475,7 @@
 		}
 
 		// Is $_GET data allowed? If not we'll set the $_GET to an empty array
-		if ($this->_allow_get_array == FALSE)
+		if ($this->_allow_get_array === FALSE)
 		{
 			$_GET = array();
 		}
@@ -502,7 +518,7 @@
 		$_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);
 
 		// CSRF Protection check
-		if ($this->_enable_csrf == TRUE)
+		if ($this->_enable_csrf === TRUE)
 		{
 			$this->security->csrf_verify();
 		}
@@ -559,7 +575,7 @@
 		}
 
 		// Standardize newlines if needed
-		if ($this->_standardize_newlines == TRUE && strpos($str, "\r") !== FALSE)
+		if ($this->_standardize_newlines === TRUE && strpos($str, "\r") !== FALSE)
 		{
 			return str_replace(array("\r\n", "\r", "\r\n\n"), PHP_EOL, $str);
 		}
diff --git a/system/core/Lang.php b/system/core/Lang.php
old mode 100755
new mode 100644
index 73c9127..3001f1b
--- a/system/core/Lang.php
+++ b/system/core/Lang.php
@@ -76,26 +76,26 @@
 	{
 		$langfile = str_replace('.php', '', $langfile);
 
-		if ($add_suffix == TRUE)
+		if ($add_suffix === TRUE)
 		{
 			$langfile = str_replace('_lang', '', $langfile).'_lang';
 		}
 
 		$langfile .= '.php';
 
-		if ($idiom == '')
+		if ($idiom === '')
 		{
 			$config =& get_config();
 			$idiom = ( ! empty($config['language'])) ? $config['language'] : 'english';
 		}
 
-		if ($return == FALSE && isset($this->is_loaded[$langfile]) && $this->is_loaded[$langfile] === $idiom)
+		if ($return === FALSE && isset($this->is_loaded[$langfile]) && $this->is_loaded[$langfile] === $idiom)
 		{
 			return;
 		}
 
 		// Determine where the language file is and load it
-		if ($alt_path != '' && file_exists($alt_path.'language/'.$idiom.'/'.$langfile))
+		if ($alt_path !== '' && file_exists($alt_path.'language/'.$idiom.'/'.$langfile))
 		{
 			include($alt_path.'language/'.$idiom.'/'.$langfile);
 		}
@@ -124,14 +124,14 @@
 		{
 			log_message('error', 'Language file contains no data: language/'.$idiom.'/'.$langfile);
 
-			if ($return == TRUE)
+			if ($return === TRUE)
 			{
 				return array();
 			}
 			return;
 		}
 
-		if ($return == TRUE)
+		if ($return === TRUE)
 		{
 			return $lang;
 		}
@@ -153,7 +153,7 @@
 	 */
 	public function line($line = '')
 	{
-		$value = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line];
+		$value = ($line === '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line];
 
 		// Because killer robots like unicorns!
 		if ($value === FALSE)
diff --git a/system/core/Loader.php b/system/core/Loader.php
index 3eb09e6..d51ee0b 100644
--- a/system/core/Loader.php
+++ b/system/core/Loader.php
@@ -208,7 +208,7 @@
 			return;
 		}
 
-		if ($library == '' OR isset($this->_base_classes[$library]))
+		if ($library === '' OR isset($this->_base_classes[$library]))
 		{
 			return FALSE;
 		}
@@ -244,7 +244,7 @@
 			return;
 		}
 
-		if ($model == '')
+		if ($model === '')
 		{
 			return;
 		}
@@ -261,7 +261,7 @@
 			$model = substr($model, $last_slash);
 		}
 
-		if ($name == '')
+		if (empty($name))
 		{
 			$name = $model;
 		}
@@ -329,7 +329,7 @@
 		$CI =& get_instance();
 
 		// Do we even need to load the database class?
-		if (class_exists('CI_DB') && $return == FALSE && $query_builder == NULL && isset($CI->db) && is_object($CI->db))
+		if (class_exists('CI_DB') && $return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db))
 		{
 			return FALSE;
 		}
@@ -452,7 +452,7 @@
 	 */
 	public function vars($vars = array(), $val = '')
 	{
-		if ($val != '' && is_string($vars))
+		if ($val !== '' && is_string($vars))
 		{
 			$vars = array($vars => $val);
 		}
@@ -642,7 +642,7 @@
 			require BASEPATH.'libraries/Driver.php';
 		}
 
-		if ($library == '')
+		if ($library === '')
 		{
 			return FALSE;
 		}
@@ -668,7 +668,7 @@
 	 * @param 	bool
 	 * @return	void
 	 */
-	public function add_package_path($path, $view_cascade=TRUE)
+	public function add_package_path($path, $view_cascade = TRUE)
 	{
 		$path = rtrim($path, '/').'/';
 
@@ -714,7 +714,7 @@
 	{
 		$config =& $this->_ci_get_component('config');
 
-		if ($path == '')
+		if ($path === '')
 		{
 			array_shift($this->_ci_library_paths);
 			array_shift($this->_ci_model_paths);
@@ -775,7 +775,7 @@
 		$file_exists = FALSE;
 
 		// Set the path to the requested file
-		if ($_ci_path != '')
+		if (is_string($_ci_path) && $_ci_path !== '')
 		{
 			$_ci_x = explode('/', $_ci_path);
 			$_ci_file = end($_ci_x);
@@ -783,7 +783,7 @@
 		else
 		{
 			$_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
-			$_ci_file = ($_ci_ext == '') ? $_ci_view.'.php' : $_ci_view;
+			$_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view;
 
 			foreach ($this->_ci_view_paths as $view_file => $cascade)
 			{
@@ -820,7 +820,7 @@
 		/*
 		 * Extract and cache variables
 		 *
-		 * You can either set variables using the dedicated $this->load_vars()
+		 * You can either set variables using the dedicated $this->load->vars()
 		 * function or via the second parameter of this function. We'll merge
 		 * the two types and cache them so that views that are embedded within
 		 * other views can have access to these variables.
@@ -847,7 +847,7 @@
 		// If the PHP installation does not support short tags we'll
 		// do a little string replacement, changing the short tags
 		// to standard PHP echo statements.
-		if ( ! is_php('5.4') && (bool) @ini_get('short_open_tag') === FALSE && config_item('rewrite_short_tags') == TRUE)
+		if ( ! is_php('5.4') && (bool) @ini_get('short_open_tag') === FALSE && config_item('rewrite_short_tags') === TRUE)
 		{
 			echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
 		}
@@ -1000,7 +1000,7 @@
 		} // END FOREACH
 
 		// One last attempt. Maybe the library is in a subdirectory, but it wasn't specified?
-		if ($subdir == '')
+		if ($subdir === '')
 		{
 			$path = strtolower($class).'/'.$class;
 			return $this->_ci_load_class($path, $params);
@@ -1008,7 +1008,7 @@
 
 		// If we got this far we were unable to find the requested class.
 		// We do not issue errors if the load call failed due to a duplicate request
-		if ($is_duplicate == FALSE)
+		if ($is_duplicate === FALSE)
 		{
 			log_message('error', 'Unable to load the requested class: '.$class);
 			show_error('Unable to load the requested class: '.$class);
@@ -1067,7 +1067,7 @@
 			}
 		}
 
-		if ($prefix == '')
+		if ($prefix === '')
 		{
 			if (class_exists('CI_'.$class))
 			{
diff --git a/system/core/Model.php b/system/core/Model.php
old mode 100755
new mode 100644
diff --git a/system/core/Output.php b/system/core/Output.php
old mode 100755
new mode 100644
index c8feb4e..5ec8c4b
--- a/system/core/Output.php
+++ b/system/core/Output.php
@@ -64,10 +64,17 @@
 	 *
 	 * @var array
 	 */
-	public $mime_types =	array();
+	public $mimes =		array();
 
 	/**
-	 * Determines wether profiler is enabled
+	 * Mime-type for the current page
+	 *
+	 * @var string
+	 */
+	protected $mime_type		= 'text/html';
+
+	/**
+	 * Determines whether profiler is enabled
 	 *
 	 * @var book
 	 */
@@ -78,7 +85,7 @@
 	 *
 	 * @var bool
 	 */
-	protected $_zlib_oc =	FALSE;
+	protected $_zlib_oc =		FALSE;
 
 	/**
 	 * List of profiler sections
@@ -101,20 +108,11 @@
 	 */
 	public function __construct()
 	{
-		$this->_zlib_oc = @ini_get('zlib.output_compression');
+		$this->_zlib_oc = (bool) @ini_get('zlib.output_compression');
 
 		// Get mime types for later
-		if (defined('ENVIRONMENT') && file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
-		{
-			include APPPATH.'config/'.ENVIRONMENT.'/mimes.php';
-		}
-		else
-		{
-			include APPPATH.'config/mimes.php';
-		}
+		$this->mimes =& get_mimes();
 
-
-		$this->mime_types = $mimes;
 		log_message('debug', 'Output Class Initialized');
 	}
 
@@ -183,7 +181,7 @@
 	 * how to permit header data to be saved with the cache data...
 	 *
 	 * @param	string
-	 * @param 	bool
+	 * @param	bool
 	 * @return	void
 	 */
 	public function set_header($header, $replace = TRUE)
@@ -192,7 +190,7 @@
 		// but it will not modify the content-length header to compensate for
 		// the reduction, causing the browser to hang waiting for more data.
 		// We'll just skip content-length in those cases.
-		if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) == 0)
+		if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0)
 		{
 			return;
 		}
@@ -209,16 +207,16 @@
 	 * @param	string	extension of the file we're outputting
 	 * @return	void
 	 */
-	public function set_content_type($mime_type)
+	public function set_content_type($mime_type, $charset = NULL)
 	{
 		if (strpos($mime_type, '/') === FALSE)
 		{
 			$extension = ltrim($mime_type, '.');
 
 			// Is this extension supported?
-			if (isset($this->mime_types[$extension]))
+			if (isset($this->mimes[$extension]))
 			{
-				$mime_type =& $this->mime_types[$extension];
+				$mime_type =& $this->mimes[$extension];
 
 				if (is_array($mime_type))
 				{
@@ -227,7 +225,15 @@
 			}
 		}
 
-		$header = 'Content-Type: '.$mime_type;
+		$this->mime_type = $mime_type;
+
+		if (empty($charset))
+		{
+			$charset = config_item('charset');
+		}
+
+		$header = 'Content-Type: '.$mime_type
+			.(empty($charset) ? NULL : '; charset='.strtolower($charset));
 
 		$this->headers[] = array($header, TRUE);
 		return $this;
@@ -295,6 +301,12 @@
 	 */
 	public function set_profiler_sections($sections)
 	{
+		if (isset($sections['query_toggle_count']))
+		{
+			$this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count'];
+			unset($sections['query_toggle_count']);
+		}
+
 		foreach ($sections as $section => $enable)
 		{
 			$this->_profiler_sections[$section] = ($enable !== FALSE);
@@ -330,7 +342,7 @@
 	 * with any server headers and profile data. It also stops the
 	 * benchmark timer so the page rendering speed and memory usage can be shown.
 	 *
-	 * @param 	string
+	 * @param	string
 	 * @return	mixed
 	 */
 	public function _display($output = '')
@@ -349,13 +361,22 @@
 		// --------------------------------------------------------------------
 
 		// Set the output data
-		if ($output == '')
+		if ($output === '')
 		{
 			$output =& $this->final_output;
 		}
 
 		// --------------------------------------------------------------------
 
+		// Is minify requested?
+		if ($CFG->item('minify_output') === TRUE)
+		{
+			$output = $this->minify($output, $this->mime_type);
+		}
+
+
+		// --------------------------------------------------------------------
+
 		// Do we need to write a cache file?  Only if the controller does not have its
 		// own _output() method and we are not dealing with a cache file, which we
 		// can determine by the existence of the $CI object above
@@ -373,7 +394,7 @@
 
 		if ($this->parse_exec_vars === TRUE)
 		{
-			$memory	= function_exists('memory_get_usage') ? round(memory_get_usage()/1024/1024, 2).'MB' : '0';
+			$memory	= round(memory_get_usage() / 1024 / 1024, 2).'MB';
 
 			$output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
 		}
@@ -381,7 +402,7 @@
 		// --------------------------------------------------------------------
 
 		// Is compression requested?
-		if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE
+		if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc === FALSE
 			&& extension_loaded('zlib')
 			&& isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
 		{
@@ -416,7 +437,7 @@
 
 		// Do we need to generate profile data?
 		// If so, load the Profile class and run it.
-		if ($this->enable_profiler == TRUE)
+		if ($this->enable_profiler === TRUE)
 		{
 			$CI->load->library('profiler');
 			if ( ! empty($this->_profiler_sections))
@@ -453,14 +474,14 @@
 	/**
 	 * Write a Cache File
 	 *
-	 * @param 	string
+	 * @param	string
 	 * @return	void
 	 */
 	public function _write_cache($output)
 	{
 		$CI =& get_instance();
 		$path = $CI->config->item('cache_path');
-		$cache_path = ($path == '') ? APPPATH.'cache/' : $path;
+		$cache_path = ($path === '') ? APPPATH.'cache/' : $path;
 
 		if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
 		{
@@ -496,6 +517,9 @@
 		@chmod($cache_path, FILE_WRITE_MODE);
 
 		log_message('debug', 'Cache file written: '.$cache_path);
+
+		// Send HTTP cache-control headers to browser to match file cache settings.
+		$this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
 	}
 
 	// --------------------------------------------------------------------
@@ -503,13 +527,13 @@
 	/**
 	 * Update/serve a cached file
 	 *
-	 * @param 	object	config class
-	 * @param 	object	uri class
-	 * @return	void
+	 * @param	object	config class
+	 * @param	object	uri class
+	 * @return	bool
 	 */
 	public function _display_cache(&$CFG, &$URI)
 	{
-		$cache_path = ($CFG->item('cache_path') == '') ? APPPATH.'cache/' : $CFG->item('cache_path');
+		$cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path');
 
 		// Build the file path. The file name is an MD5 hash of the full URI
 		$uri =	$CFG->item('base_url').$CFG->item('index_page').$URI->uri_string;
@@ -533,13 +557,22 @@
 			return FALSE;
 		}
 
-		// Has the file expired? If so we'll delete it.
-		if (time() >= trim(str_replace('TS--->', '', $match[1])) && is_really_writable($cache_path))
+		$last_modified = filemtime($cache_path);
+		$expire = trim(str_replace('TS--->', '', $match[1]));
+
+		// Has the file expired?
+		if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
 		{
+			// If so we'll delete it.
 			@unlink($filepath);
 			log_message('debug', 'Cache file has expired. File deleted.');
 			return FALSE;
 		}
+		else
+		{
+			// Or else send the HTTP cache control headers.
+			$this->set_cache_header($last_modified, $expire);
+		}
 
 		// Display the cache
 		$this->_display(str_replace($match[0], '', $cache));
@@ -547,6 +580,140 @@
 		return TRUE;
 	}
 
+	// --------------------------------------------------------------------
+
+	/**
+	 * Set the HTTP headers to match the server-side file cache settings
+	 * in order to reduce bandwidth.
+	 *
+	 * @param	int	timestamp of when the page was last modified
+	 * @param	int	timestamp of when should the requested page expire from cache
+	 * @return	void
+	 */
+	public function set_cache_header($last_modified, $expiration)
+	{
+		$max_age = $expiration - $_SERVER['REQUEST_TIME'];
+
+		if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']))
+		{
+			$this->set_status_header(304);
+			exit;
+		}
+		else
+		{
+			header('Pragma: public');
+			header('Cache-Control: max-age=' . $max_age . ', public');
+			header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');
+			header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Reduce excessive size of HTML content.
+	 *
+	 * @param	string
+	 * @param	string
+	 * @return	string
+	 */
+	public function minify($output, $type = 'text/html')
+	{
+		switch ($type)
+		{
+			case 'text/html':
+
+				$size_before = strlen($output);
+
+				if ($size_before === 0)
+				{
+					return '';
+				}
+
+				// Find all the <pre>,<code>,<textarea>, and <javascript> tags
+				// We'll want to return them to this unprocessed state later.
+				preg_match_all('{<pre.+</pre>}msU', $output, $pres_clean);
+				preg_match_all('{<code.+</code>}msU', $output, $codes_clean);
+				preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_clean);
+				preg_match_all('{<script.+</script>}msU', $output, $javascript_clean);
+
+				// Minify the CSS in all the <style> tags.
+				preg_match_all('{<style.+</style>}msU', $output, $style_clean);
+				foreach ($style_clean[0] as $s)
+				{
+					$output = str_replace($s, $this->minify($s, 'text/css'), $output);
+				}
+
+				// Minify the javascript in <script> tags.
+				foreach ($javascript_clean[0] as $s)
+				{
+					$javascript_mini[] = $this->minify($s, 'text/javascript');
+				}
+
+				// Replace multiple spaces with a single space.
+				$output = preg_replace('!\s{2,}!', ' ', $output);
+
+				// Remove comments (non-MSIE conditionals)
+				$output = preg_replace('{\s*<!--[^\[].*-->\s*}msU', '', $output);
+
+				// Remove spaces around block-level elements.
+				$output = preg_replace('/\s*(<\/?(html|head|title|meta|script|link|style|body|h[1-6]|div|p|br)[^>]*>)\s*/is', '$1', $output);
+
+				// Replace mangled <pre> etc. tags with unprocessed ones.
+
+				if ( ! empty($pres_clean))
+				{
+					preg_match_all('{<pre.+</pre>}msU', $output, $pres_messed);
+					$output = str_replace($pres_messed[0], $pres_clean[0], $output);
+				}
+
+				if ( ! empty($codes_clean))
+				{
+					preg_match_all('{<code.+</code>}msU', $output, $codes_messed);
+					$output = str_replace($codes_messed[0], $codes_clean[0], $output);
+				}
+
+				if ( ! empty($codes_clean))
+				{
+					preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_messed);
+					$output = str_replace($textareas_messed[0], $textareas_clean[0], $output);
+				}
+
+				if (isset($javascript_mini))
+				{
+					preg_match_all('{<script.+</script>}msU', $output, $javascript_messed);
+					$output = str_replace($javascript_messed[0], $javascript_mini, $output);
+				}
+
+				$size_removed = $size_before - strlen($output);
+				$savings_percent = round(($size_removed / $size_before * 100));
+
+				log_message('debug', 'Minifier shaved '.($size_removed / 1000).'KB ('.$savings_percent.'%) off final HTML output.');
+
+			break;
+
+			case 'text/css':
+
+				//Remove CSS comments
+				$output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $output);
+
+				// Remove spaces around curly brackets, colons,
+				// semi-colons, parenthesis, commas
+				$output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!', '$1', $output);
+
+			break;
+
+			case 'text/javascript':
+
+				// Currently leaves JavaScript untouched.
+			break;
+
+			default: break;
+		}
+
+		return $output;
+	}
+
 }
 
 /* End of file Output.php */
diff --git a/system/core/Router.php b/system/core/Router.php
old mode 100755
new mode 100644
index 5ea1379..5bc0530
--- a/system/core/Router.php
+++ b/system/core/Router.php
@@ -435,7 +435,7 @@
 	 */
 	public function fetch_method()
 	{
-		return ($this->method == $this->fetch_class()) ? 'index' : $this->method;
+		return ($this->method === $this->fetch_class()) ? 'index' : $this->method;
 	}
 
 	// --------------------------------------------------------------------
@@ -483,7 +483,7 @@
 			$this->set_directory($routing['directory']);
 		}
 
-		if (isset($routing['controller']) && $routing['controller'] != '')
+		if ( ! empty($routing['controller']))
 		{
 			$this->set_class($routing['controller']);
 		}
diff --git a/system/core/Security.php b/system/core/Security.php
old mode 100755
new mode 100644
index 9b7ba57..b22d2cf
--- a/system/core/Security.php
+++ b/system/core/Security.php
@@ -162,7 +162,7 @@
 
 		// Do the tokens exist in both the _POST and _COOKIE arrays?
 		if ( ! isset($_POST[$this->_csrf_token_name]) OR ! isset($_COOKIE[$this->_csrf_cookie_name])
-			OR $_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name]) // Do the tokens match?
+			OR $_POST[$this->_csrf_token_name] !== $_COOKIE[$this->_csrf_cookie_name]) // Do the tokens match?
 		{
 			$this->csrf_show_error();
 		}
@@ -395,20 +395,20 @@
 
 			if (preg_match('/<a/i', $str))
 			{
-				$str = preg_replace_callback('#<a\s+([^>]*?)(>|$)#si', array($this, '_js_link_removal'), $str);
+				$str = preg_replace_callback('#<a\s+([^>]*?)(?:>|$)#si', array($this, '_js_link_removal'), $str);
 			}
 
 			if (preg_match('/<img/i', $str))
 			{
-				$str = preg_replace_callback('#<img\s+([^>]*?)(\s?/?>|$)#si', array($this, '_js_img_removal'), $str);
+				$str = preg_replace_callback('#<img\s+([^>]*?)(?:\s?/?>|$)#si', array($this, '_js_img_removal'), $str);
 			}
 
-			if (preg_match('/(script|xss)/i', $str))
+			if (preg_match('/script|xss/i', $str))
 			{
-				$str = preg_replace('#<(/*)(script|xss)(.*?)\>#si', '[removed]', $str);
+				$str = preg_replace('#</*(?:script|xss).*?>#si', '[removed]', $str);
 			}
 		}
-		while($original != $str);
+		while ($original !== $str);
 
 		unset($original);
 
@@ -475,7 +475,7 @@
 	 */
 	public function xss_hash()
 	{
-		if ($this->_xss_hash == '')
+		if ($this->_xss_hash === '')
 		{
 			mt_srand();
 			$this->_xss_hash = md5(time() + mt_rand(0, 1999999999));
@@ -561,6 +561,19 @@
 	// ----------------------------------------------------------------
 
 	/**
+	 * Strip Image Tags
+	 *
+	 * @param	string
+	 * @return	string
+	 */
+	public function strip_image_tags($str)
+	{
+		return preg_replace(array('#<img\s+.*?src\s*=\s*["\'](.+?)["\'].*?\>#', '#<img\s+.*?src\s*=\s*(.+?).*?\>#'), '\\1', $str);
+	}
+
+	// ----------------------------------------------------------------
+
+	/**
 	 * Compact Exploded Words
 	 *
 	 * Callback function for xss_clean() to remove whitespace from
@@ -670,7 +683,7 @@
 	protected function _js_link_removal($match)
 	{
 		return str_replace($match[1],
-					preg_replace('#href=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si',
+					preg_replace('#href=.*?(?:alert\(|alert&\#40;|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si',
 							'',
 							$this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
 					),
@@ -693,7 +706,7 @@
 	protected function _js_img_removal($match)
 	{
 		return str_replace($match[1],
-					preg_replace('#src=.*?(alert\(|alert&\#40;|javascript\:|livescript\:|mocha\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si',
+					preg_replace('#src=.*?(?:alert\(|alert&\#40;|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si',
 							'',
 							$this->_filter_attributes(str_replace(array('<', '>'), '', $match[1]))
 					),
@@ -825,7 +838,7 @@
 	 */
 	protected function _csrf_set_hash()
 	{
-		if ($this->_csrf_hash == '')
+		if ($this->_csrf_hash === '')
 		{
 			// If the cookie exists we will use it's value.
 			// We don't necessarily want to regenerate it with
@@ -847,4 +860,4 @@
 }
 
 /* End of file Security.php */
-/* Location: ./system/core/Security.php */
+/* Location: ./system/core/Security.php */
\ No newline at end of file
diff --git a/system/core/URI.php b/system/core/URI.php
old mode 100755
new mode 100644
index a9432e0..6a8b1a5
--- a/system/core/URI.php
+++ b/system/core/URI.php
@@ -111,23 +111,23 @@
 
 			// Is there a PATH_INFO variable?
 			// Note: some servers seem to have trouble with getenv() so we'll test it two ways
-			$path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
-			if (trim($path, '/') != '' && $path !== '/'.SELF)
+			$path = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
+			if (trim($path, '/') !== '' && $path !== '/'.SELF)
 			{
 				$this->_set_uri_string($path);
 				return;
 			}
 
 			// No PATH_INFO?... What about QUERY_STRING?
-			$path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
-			if (trim($path, '/') != '')
+			$path = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
+			if (trim($path, '/') !== '')
 			{
 				$this->_set_uri_string($path);
 				return;
 			}
 
 			// As a last ditch effort lets try using the $_GET array
-			if (is_array($_GET) && count($_GET) === 1 && trim(key($_GET), '/') != '')
+			if (is_array($_GET) && count($_GET) === 1 && trim(key($_GET), '/') !== '')
 			{
 				$this->_set_uri_string(key($_GET));
 				return;
@@ -163,7 +163,7 @@
 	 * @param 	string
 	 * @return	void
 	 */
-	public function _set_uri_string($str)
+	protected function _set_uri_string($str)
 	{
 		// Filter out control characters
 		$str = remove_invisible_characters($str, FALSE);
@@ -177,8 +177,8 @@
 	/**
 	 * Detects the URI
 	 *
-	 * This function will detect the URI automatically and fix the query string
-	 * if necessary.
+	 * This function will detect the URI automatically
+	 * and fix the query string if necessary.
 	 *
 	 * @return	string
 	 */
@@ -189,23 +189,27 @@
 			return '';
 		}
 
-		$uri = $_SERVER['REQUEST_URI'];
-		if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
+		if (strpos($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']) === 0)
 		{
-			$uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
+			$uri = substr($_SERVER['REQUEST_URI'], strlen($_SERVER['SCRIPT_NAME']));
 		}
-		elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
+		elseif (strpos($_SERVER['REQUEST_URI'], dirname($_SERVER['SCRIPT_NAME'])) === 0)
 		{
-			$uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
+			$uri = substr($_SERVER['REQUEST_URI'], strlen(dirname($_SERVER['SCRIPT_NAME'])));
+		}
+		else
+		{
+			$uri = $_SERVER['REQUEST_URI'];
 		}
 
 		// This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct
 		// URI is found, and also fixes the QUERY_STRING server var and $_GET array.
-		if (strncmp($uri, '?/', 2) === 0)
+		if (strpos($uri, '?/') === 0)
 		{
 			$uri = substr($uri, 2);
 		}
-		$parts = preg_split('#\?#i', $uri, 2);
+
+		$parts = explode('?', $uri, 2);
 		$uri = $parts[0];
 		if (isset($parts[1]))
 		{
@@ -218,12 +222,12 @@
 			$_GET = array();
 		}
 
-		if ($uri == '/' OR empty($uri))
+		if ($uri === '/' OR empty($uri))
 		{
 			return '/';
 		}
 
-		$uri = parse_url($uri, PHP_URL_PATH);
+		$uri = parse_url('pseudo://hostname/'.$uri, PHP_URL_PATH);
 
 		// Do some final cleaning of the URI and return it
 		return str_replace(array('//', '../'), '/', trim($uri, '/'));
@@ -270,11 +274,11 @@
 	 */
 	public function _filter_uri($str)
 	{
-		if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
+		if ($str !== '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') === FALSE)
 		{
 			// preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
 			// compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
-			if ( ! preg_match('|^['.str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-')).']+$|i', $str))
+			if ( ! preg_match('|^['.str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-')).']+$|i', urldecode($str)))
 			{
 				show_error('The URI you submitted has disallowed characters.', 400);
 			}
@@ -298,9 +302,11 @@
 	 */
 	public function _remove_url_suffix()
 	{
-		if  ($this->config->item('url_suffix') != '')
+		$suffix = (string) $this->config->item('url_suffix');
+
+		if ($suffix !== '' && ($offset = strrpos($this->uri_string, $suffix)) !== FALSE)
 		{
-			$this->uri_string = preg_replace('|'.preg_quote($this->config->item('url_suffix')).'$|', '', $this->uri_string);
+			$this->uri_string = substr_replace($this->uri_string, '', $offset, strlen($suffix));
 		}
 	}
 
@@ -321,7 +327,7 @@
 			// Filter segments for security
 			$val = trim($this->_filter_uri($val));
 
-			if ($val != '')
+			if ($val !== '')
 			{
 				$this->segments[] = $val;
 			}
diff --git a/system/core/Utf8.php b/system/core/Utf8.php
index a6faa84..0a7ec50 100644
--- a/system/core/Utf8.php
+++ b/system/core/Utf8.php
@@ -54,7 +54,7 @@
 		if (
 			@preg_match('/./u', 'é') === 1		// PCRE must support UTF-8
 			&& function_exists('iconv')			// iconv must be installed
-			&& @ini_get('mbstring.func_overload') != 1	// Multibyte string function overloading cannot be enabled
+			&& (bool) @ini_get('mbstring.func_overload') !== TRUE	// Multibyte string function overloading cannot be enabled
 			&& $CFG->item('charset') === 'UTF-8'		// Application charset must be UTF-8
 			)
 		{
diff --git a/system/database/DB.php b/system/database/DB.php
old mode 100755
new mode 100644
index 1fe44c0..d751325
--- a/system/database/DB.php
+++ b/system/database/DB.php
@@ -29,8 +29,8 @@
  * Initialize the database
  *
  * @category	Database
- * @author		EllisLab Dev Team
- * @link		http://codeigniter.com/user_guide/database/
+ * @author	EllisLab Dev Team
+ * @link	http://codeigniter.com/user_guide/database/
  * @param 	string
  * @param 	bool	Determines if query builder should be used or not
  */
@@ -47,13 +47,28 @@
 		}
 
 		include($file_path);
+		//make packages contain database config files
+		foreach(get_instance()->load->get_package_paths() as $path)
+		{
+			if ($path !== APPPATH)
+			{
+				if (file_exists ($file_path = $path.'config/'.ENVIRONMENT.'/database.php'))
+				{
+					include ($file_path);
+				}
+				elseif ( file_exists ($file_path = $path.'config/database.php'))
+				{
+					include ($file_path);
+				}
+			}
+		}
 
 		if ( ! isset($db) OR count($db) === 0)
 		{
 			show_error('No database connection settings were found in the database config file.');
 		}
 
-		if ($params != '')
+		if ($params !== '')
 		{
 			$active_group = $params;
 		}
@@ -106,7 +121,7 @@
 	}
 
 	// No DB specified yet? Beat them senseless...
-	if ( ! isset($params['dbdriver']) OR $params['dbdriver'] == '')
+	if (empty($params['dbdriver']))
 	{
 		show_error('You have not selected a database type to connect to.');
 	}
@@ -128,7 +143,7 @@
 
 	require_once(BASEPATH.'database/DB_driver.php');
 
-	if ( ! isset($query_builder) OR $query_builder == TRUE)
+	if ( ! isset($query_builder) OR $query_builder === TRUE)
 	{
 		require_once(BASEPATH.'database/DB_query_builder.php');
 		if ( ! class_exists('CI_DB'))
@@ -144,7 +159,10 @@
 	// Load the DB driver
 	$driver_file = BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php';
 
-	if ( ! file_exists($driver_file)) show_error('Invalid DB driver');
+	if ( ! file_exists($driver_file))
+	{
+		show_error('Invalid DB driver');
+	}
 
 	require_once($driver_file);
 
@@ -152,12 +170,25 @@
 	$driver = 'CI_DB_'.$params['dbdriver'].'_driver';
 	$DB = new $driver($params);
 
-	if ($DB->autoinit == TRUE)
+	// Check for a subdriver
+	if ( ! empty($DB->subdriver))
+	{
+		$driver_file = BASEPATH.'database/drivers/'.$DB->dbdriver.'/subdrivers/'.$DB->dbdriver.'_'.$DB->subdriver.'_driver.php';
+
+		if (file_exists($driver_file))
+		{
+			require_once($driver_file);
+			$driver = 'CI_DB_'.$DB->dbdriver.'_'.$DB->subdriver.'_driver';
+			$DB = new $driver($params);
+		}
+	}
+
+	if ($DB->autoinit === TRUE)
 	{
 		$DB->initialize();
 	}
 
-	if (isset($params['stricton']) && $params['stricton'] == TRUE)
+	if ( ! empty($params['stricton']))
 	{
 		$DB->query('SET SESSION sql_mode="STRICT_ALL_TABLES"');
 	}
diff --git a/system/database/DB_cache.php b/system/database/DB_cache.php
index ff94285..ba91103 100644
--- a/system/database/DB_cache.php
+++ b/system/database/DB_cache.php
@@ -55,9 +55,9 @@
 	 */
 	public function check_path($path = '')
 	{
-		if ($path == '')
+		if ($path === '')
 		{
-			if ($this->db->cachedir == '')
+			if ($this->db->cachedir === '')
 			{
 				return $this->db->cache_off();
 			}
@@ -99,7 +99,7 @@
 		$segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2);
 		$filepath = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'.md5($sql);
 
-		if (FALSE === ($cachedata = read_file($filepath)))
+		if (FALSE === ($cachedata = file_get_contents($filepath)))
 		{
 			return FALSE;
 		}
@@ -154,12 +154,12 @@
 	 */
 	public function delete($segment_one = '', $segment_two = '')
 	{
-		if ($segment_one == '')
+		if ($segment_one === '')
 		{
 			$segment_one  = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1);
 		}
 
-		if ($segment_two == '')
+		if ($segment_two === '')
 		{
 			$segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2);
 		}
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index bbb7b7a..e9efc89 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -45,7 +45,8 @@
 	public $password;
 	public $hostname;
 	public $database;
-	public $dbdriver		= 'mysql';
+	public $dbdriver		= 'mysqli';
+	public $subdriver;
 	public $dbprefix		= '';
 	public $char_set		= 'utf8';
 	public $dbcollat		= 'utf8_general_ci';
@@ -77,6 +78,19 @@
 	protected $_protect_identifiers		= TRUE;
 	protected $_reserved_identifiers	= array('*'); // Identifiers that should NOT be escaped
 
+	/**
+	 * The syntax to count rows is slightly different across different
+	 * database engines, so this string appears in each driver and is
+	 * used for the count_all() and count_all_results() functions.
+	 */
+	protected $_count_string = 'SELECT COUNT(*) AS ';
+
+	/**
+	 * Constructor
+	 *
+	 * @param	array
+	 * @return	void
+	 */
 	public function __construct($params)
 	{
 		if (is_array($params))
@@ -113,7 +127,7 @@
 		// ----------------------------------------------------------------
 
 		// Connect to the database and set the connection ID
-		$this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect();
+		$this->conn_id = ($this->pconnect === FALSE) ? $this->db_connect() : $this->db_pconnect();
 
 		// No connection resource? Check if there is a failover else throw an error
 		if ( ! $this->conn_id)
@@ -131,7 +145,7 @@
 					}
 
 					// Try to connect
-					$this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect();
+					$this->conn_id = ($this->pconnect === FALSE) ? $this->db_connect() : $this->db_pconnect();
 
 					// If a connection is made break the foreach loop
 					if ($this->conn_id)
@@ -295,17 +309,21 @@
 	 * @param	array	An array of binding data
 	 * @return	mixed
 	 */
-	public function query($sql, $binds = FALSE, $return_object = TRUE)
+	public function query($sql, $binds = FALSE, $return_object = NULL)
 	{
-		if ($sql == '')
+		if ($sql === '')
 		{
 			log_message('error', 'Invalid query: '.$sql);
 
 			return ($this->db_debug) ? $this->display_error('db_invalid_query') : FALSE;
 		}
+		elseif ( ! is_bool($return_object))
+		{
+			$return_object = ! $this->is_write_type($sql);
+		}
 
 		// Verify table prefix and replace if necessary
-		if ($this->dbprefix != '' && $this->swap_pre != '' && $this->dbprefix != $this->swap_pre)
+		if ($this->dbprefix !== '' && $this->swap_pre !== '' && $this->dbprefix !== $this->swap_pre)
 		{
 			$sql = preg_replace('/(\W)'.$this->swap_pre.'(\S+?)/', '\\1'.$this->dbprefix.'\\2', $sql);
 		}
@@ -319,7 +337,7 @@
 		// Is query caching enabled? If the query is a "read type"
 		// we will load the caching class and return the previously
 		// cached query if it exists
-		if ($this->cache_on == TRUE && stripos($sql, 'SELECT') !== FALSE && $this->_cache_init())
+		if ($this->cache_on === TRUE && $return_object === TRUE && $this->_cache_init())
 		{
 			$this->load_rdriver();
 			if (FALSE !== ($cache = $this->CACHE->read($sql)))
@@ -328,8 +346,8 @@
 			}
 		}
 
-		// Save the  query for debugging
-		if ($this->save_queries == TRUE)
+		// Save the query for debugging
+		if ($this->save_queries === TRUE)
 		{
 			$this->queries[] = $sql;
 		}
@@ -340,7 +358,7 @@
 		// Run the Query
 		if (FALSE === ($this->result_id = $this->simple_query($sql)))
 		{
-			if ($this->save_queries == TRUE)
+			if ($this->save_queries === TRUE)
 			{
 				$this->query_times[] = 0;
 			}
@@ -352,7 +370,7 @@
 			$error = $this->error();
 
 			// Log errors
-			log_message('error', 'Query error: '.$error['message']);
+			log_message('error', 'Query error: '.$error['message'].' - Invalid query: '.$sql);
 
 			if ($this->db_debug)
 			{
@@ -373,7 +391,7 @@
 		$time_end = microtime(TRUE);
 		$this->benchmark += $time_end - $time_start;
 
-		if ($this->save_queries == TRUE)
+		if ($this->save_queries === TRUE)
 		{
 			$this->query_times[] = $time_end - $time_start;
 		}
@@ -381,13 +399,11 @@
 		// Increment the query counter
 		$this->query_count++;
 
-		// Was the query a "write" type?
-		// If so we'll simply return true
-		if ($this->is_write_type($sql) === TRUE)
+		// Will we have a result object instantiated? If not - we'll simply return TRUE
+		if ($return_object !== TRUE)
 		{
-			// If caching is enabled we'll auto-cleanup any
-			// existing files related to this particular URI
-			if ($this->cache_on == TRUE && $this->cache_autodel == TRUE && $this->_cache_init())
+			// If caching is enabled we'll auto-cleanup any existing files related to this particular URI
+			if ($this->cache_on === TRUE && $this->cache_autodel === TRUE && $this->_cache_init())
 			{
 				$this->CACHE->delete();
 			}
@@ -396,8 +412,6 @@
 		}
 
 		// Return TRUE if we don't need to create a result object
-		// Currently only the Oracle driver uses this when stored
-		// procedures are used
 		if ($return_object !== TRUE)
 		{
 			return TRUE;
@@ -409,7 +423,7 @@
 
 		// Is query caching enabled? If so, we'll serialize the
 		// result object and save it to a cache file.
-		if ($this->cache_on == TRUE && $this->_cache_init())
+		if ($this->cache_on === TRUE && $this->_cache_init())
 		{
 			// We'll create a new instance of the result object
 			// only without the platform specific driver since
@@ -596,35 +610,53 @@
 	 */
 	public function compile_binds($sql, $binds)
 	{
-		if (strpos($sql, $this->bind_marker) === FALSE)
+		if (empty($binds) OR empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE)
+		{
+			return $sql;
+		}
+		elseif ( ! is_array($binds))
+		{
+			$binds = array($binds);
+			$bind_count = 1;
+		}
+		else
+		{
+			// Make sure we're using numeric keys
+			$binds = array_values($binds);
+			$bind_count = count($binds);
+		}
+
+		// We'll need the marker length later
+		$ml = strlen($this->bind_marker);
+
+		// Make sure not to replace a chunk inside a string that happens to match the bind marker
+		if ($c = preg_match_all("/'[^']*'/i", $sql, $matches))
+		{
+			$c = preg_match_all('/'.preg_quote($this->bind_marker).'/i',
+				str_replace($matches[0],
+					str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]),
+					$sql, $c),
+				$matches, PREG_OFFSET_CAPTURE);
+
+			// Bind values' count must match the count of markers in the query
+			if ($bind_count !== $c)
+			{
+				return $sql;
+			}
+		}
+		elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker).'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count)
 		{
 			return $sql;
 		}
 
-		if ( ! is_array($binds))
+		do
 		{
-			$binds = array($binds);
+			$c--;
+			$sql = substr_replace($sql, $this->escape($binds[$c]), $matches[0][$c][1], $ml);
 		}
+		while ($c !== 0);
 
-		// Get the sql segments around the bind markers
-		$segments = explode($this->bind_marker, $sql);
-
-		// The count of bind should be 1 less then the count of segments
-		// If there are more bind arguments trim it down
-		if (count($binds) >= count($segments))
-		{
-			$binds = array_slice($binds, 0, count($segments)-1);
-		}
-
-		// Construct the binded query
-		$result = $segments[0];
-		$i = 0;
-		foreach ($binds as $bind)
-		{
-			$result .= $this->escape($bind).$segments[++$i];
-		}
-
-		return $result;
+		return $sql;
 	}
 
 	// --------------------------------------------------------------------
@@ -752,13 +784,13 @@
 	 */
 	public function count_all($table = '')
 	{
-		if ($table == '')
+		if ($table === '')
 		{
 			return 0;
 		}
 
 		$query = $this->query($this->_count_string.$this->escape_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE));
-		if ($query->num_rows() == 0)
+		if ($query->num_rows() === 0)
 		{
 			return 0;
 		}
@@ -837,7 +869,7 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Fetch MySQL Field Names
+	 * Fetch Field Names
 	 *
 	 * @param	string	the table name
 	 * @return	array
@@ -850,7 +882,7 @@
 			return $this->data_cache['field_names'][$table];
 		}
 
-		if ($table == '')
+		if ($table === '')
 		{
 			return ($this->db_debug) ? $this->display_error('db_field_param_missing') : FALSE;
 		}
@@ -918,7 +950,7 @@
 	 */
 	public function field_data($table = '')
 	{
-		if ($table == '')
+		if ($table === '')
 		{
 			return ($this->db_debug) ? $this->display_error('db_field_param_missing') : FALSE;
 		}
@@ -934,34 +966,57 @@
 	 *
 	 * This function escapes column and table names
 	 *
-	 * @param	string
-	 * @return	string
+	 * @param	mixed
+	 * @return	mixed
 	 */
 	public function escape_identifiers($item)
 	{
-		if ($this->_escape_char == '')
+		if ($this->_escape_char === '' OR empty($item))
 		{
 			return $item;
 		}
+		elseif (is_array($item))
+		{
+			foreach ($item as $key => $value)
+			{
+				$item[$key] = $this->escape_identifiers($value);
+			}
+
+			return $item;
+		}
+		// Avoid breaking functions and literal values inside queries
+		elseif (ctype_digit($item) OR $item[0] === "'" OR strpos($item, '(') !== FALSE)
+		{
+			return $item;
+		}
+
+		static $preg_ec = array();
+
+		if (empty($preg_ec))
+		{
+			if (is_array($this->_escape_char))
+			{
+				$preg_ec = array(
+						preg_quote($this->_escape_char[0]), preg_quote($this->_escape_char[1]),
+						$this->_escape_char[0], $this->_escape_char[1]
+						);
+			}
+			else
+			{
+				$preg_ec[0] = $preg_ec[1] = preg_quote($this->_escape_char);
+				$preg_ec[2] = $preg_ec[3] = $this->_escape_char;
+			}
+		}
 
 		foreach ($this->_reserved_identifiers as $id)
 		{
 			if (strpos($item, '.'.$id) !== FALSE)
 			{
-				$item = str_replace('.', $this->_escape_char.'.', $item);
-
-				// remove duplicates if the user already included the escape
-				return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $this->_escape_char.$item);
+				return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?\./i', $preg_ec[2].'$1'.$preg_ec[3].'.', $item);
 			}
 		}
 
-		if (strpos($item, '.') !== FALSE)
-		{
-			$item = str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item);
-		}
-
-		// remove duplicates if the user already included the escape
-		return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $this->_escape_char.$item.$this->_escape_char);
+		return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?(\.)?/i', $preg_ec[2].'$1'.$preg_ec[3].'$2', $item);
 	}
 
 	// --------------------------------------------------------------------
@@ -989,6 +1044,23 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Insert statement
+	 *
+	 * Generates a platform-specific insert string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the insert keys
+	 * @param	array	the insert values
+	 * @return	string
+	 */
+	protected function _insert($table, $keys, $values)
+	{
+		return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Generate an update string
 	 *
 	 * @param	string	the table upon which the query will be performed
@@ -998,7 +1070,7 @@
 	 */
 	public function update_string($table, $data, $where)
 	{
-		if ($where == '')
+		if ($where === '')
 		{
 			return FALSE;
 		}
@@ -1018,7 +1090,7 @@
 			$dest = array();
 			foreach ($where as $key => $val)
 			{
-				$prefix = (count($dest) == 0) ? '' : ' AND ';
+				$prefix = (count($dest) === 0) ? '' : ' AND ';
 				$key = $this->protect_identifiers($key);
 
 				if ($val !== '')
@@ -1041,6 +1113,41 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause
+	 * @param	array	the limit clause
+	 * @param	array	the like clause
+	 * @return	string
+	 */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr)
+			.$where
+			.(count($orderby) > 0 ? ' ORDER BY '.implode(', ', $orderby) : '')
+			.($limit ? ' LIMIT '.$limit : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Tests whether the string has an SQL operator
 	 *
 	 * @param	string
@@ -1048,7 +1155,21 @@
 	 */
 	protected function _has_operator($str)
 	{
-		return (bool) preg_match('/(\s|<|>|!|=|IS NULL|IS NOT NULL)/i', trim($str));
+		return (bool) preg_match('/(\s|<|>|!|=|IS NULL|IS NOT NULL|BETWEEN)/i', trim($str));
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Returns the SQL string operator
+	 *
+	 * @param	string
+	 * @return	string
+	 */
+	protected function _get_operator($str)
+	{
+		return preg_match('/(=|!|<|>| IS NULL| IS NOT NULL| BETWEEN)/i', $str, $match)
+			? $match[1] : FALSE;
 	}
 
 	// --------------------------------------------------------------------
@@ -1062,7 +1183,7 @@
 	 */
 	public function call_function($function)
 	{
-		$driver = ($this->dbdriver == 'postgre') ? 'pg_' : $this->dbdriver.'_';
+		$driver = ($this->dbdriver === 'postgre') ? 'pg_' : $this->dbdriver.'_';
 
 		if (FALSE === strpos($driver, $function))
 		{
@@ -1116,7 +1237,6 @@
 		return $this->cache_on = FALSE;
 	}
 
-
 	// --------------------------------------------------------------------
 
 	/**
@@ -1217,7 +1337,7 @@
 
 		$heading = $LANG->line('db_error_heading');
 
-		if ($native == TRUE)
+		if ($native === TRUE)
 		{
 			$message = (array) $error;
 		}
@@ -1235,7 +1355,7 @@
 			if (isset($call['file']) && strpos($call['file'], BASEPATH.'database') === FALSE)
 			{
 				// Found it - use a relative path for safety
-				$message[] = 'Filename: '.str_replace(array(BASEPATH, APPPATH), '', $call['file']);
+				$message[] = 'Filename: '.str_replace(array(APPPATH, BASEPATH), '', $call['file']);
 				$message[] = 'Line Number: '.$call['line'];
 				break;
 			}
@@ -1286,34 +1406,43 @@
 			$escaped_array = array();
 			foreach ($item as $k => $v)
 			{
-				$escaped_array[$this->protect_identifiers($k)] = $this->protect_identifiers($v);
+				$escaped_array[$this->protect_identifiers($k)] = $this->protect_identifiers($v, $prefix_single, $protect_identifiers, $field_exists);
 			}
 
 			return $escaped_array;
 		}
 
-		// Convert tabs or multiple spaces into single spaces
-		$item = preg_replace('/[\t ]+/', ' ', $item);
-
-		// If the item has an alias declaration we remove it and set it aside.
-		// Basically we remove everything to the right of the first space
-		if (strpos($item, ' ') !== FALSE)
-		{
-			$alias = strstr($item, ' ');
-			$item = substr($item, 0, - strlen($alias));
-		}
-		else
-		{
-			$alias = '';
-		}
-
 		// This is basically a bug fix for queries that use MAX, MIN, etc.
 		// If a parenthesis is found we know that we do not need to
 		// escape the data or add a prefix. There's probably a more graceful
 		// way to deal with this, but I'm not thinking of it -- Rick
 		if (strpos($item, '(') !== FALSE)
 		{
-			return $item.$alias;
+			return $item;
+		}
+
+		// Convert tabs or multiple spaces into single spaces
+		$item = preg_replace('/\s+/', ' ', $item);
+
+		// If the item has an alias declaration we remove it and set it aside.
+		// Note: strripos() is used in order to support spaces in table names
+		if ($offset = strripos($item, ' AS '))
+		{
+			$alias = ($protect_identifiers)
+					? substr($item, $offset, 4).$this->escape_identifiers(substr($item, $offset + 4))
+					: substr($item, $offset);
+			$item = substr($item, 0, $offset);
+		}
+		elseif ($offset = strrpos($item, ' '))
+		{
+			$alias = ($protect_identifiers)
+					? ' '.$this->escape_identifiers(substr($item, $offset + 1))
+					: substr($item, $offset);
+			$item = substr($item, 0, $offset);
+		}
+		else
+		{
+			$alias = '';
 		}
 
 		// Break the string apart if it contains periods, then insert the table prefix
@@ -1345,7 +1474,7 @@
 			}
 
 			// Is there a table prefix defined in the config file? If not, no need to do anything
-			if ($this->dbprefix != '')
+			if ($this->dbprefix !== '')
 			{
 				// We now add the table prefix based on some logic.
 				// Do we have 4 segments (hostname.database.table.column)?
@@ -1369,13 +1498,13 @@
 
 				// This flag is set when the supplied $item does not contain a field name.
 				// This can happen when this function is being called from a JOIN.
-				if ($field_exists == FALSE)
+				if ($field_exists === FALSE)
 				{
 					$i++;
 				}
 
 				// Verify table prefix and replace if necessary
-				if ($this->swap_pre != '' && strpos($parts[$i], $this->swap_pre) === 0)
+				if ($this->swap_pre !== '' && strpos($parts[$i], $this->swap_pre) === 0)
 				{
 					$parts[$i] = preg_replace('/^'.$this->swap_pre.'(\S+?)/', $this->dbprefix.'\\1', $parts[$i]);
 				}
@@ -1398,15 +1527,15 @@
 		}
 
 		// Is there a table prefix? If not, no need to insert it
-		if ($this->dbprefix != '')
+		if ($this->dbprefix !== '')
 		{
 			// Verify table prefix and replace if necessary
-			if ($this->swap_pre != '' && strpos($item, $this->swap_pre) === 0)
+			if ($this->swap_pre !== '' && strpos($item, $this->swap_pre) === 0)
 			{
 				$item = preg_replace('/^'.$this->swap_pre.'(\S+?)/', $this->dbprefix.'\\1', $item);
 			}
 			// Do we prefix an item with no segments?
-			elseif ($prefix_single == TRUE && strpos($item, $this->dbprefix) !== 0)
+			elseif ($prefix_single === TRUE && strpos($item, $this->dbprefix) !== 0)
 			{
 				$item = $this->dbprefix.$item;
 			}
diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php
index a519575..91f9d56 100644
--- a/system/database/DB_forge.php
+++ b/system/database/DB_forge.php
@@ -72,6 +72,11 @@
 			return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
 		}
 
+		if ( ! empty($this->db->data_cache['db_names']))
+		{
+			$this->db->data_cache['db_names'][] = $db_name;
+		}
+
 		return TRUE;
 	}
 
@@ -85,7 +90,7 @@
 	 */
 	public function drop_database($db_name)
 	{
-		if ($db_name == '')
+		if ($db_name === '')
 		{
 			show_error('A table name is required for that operation.');
 			return FALSE;
@@ -99,6 +104,15 @@
 			return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
 		}
 
+		if ( ! empty($this->db->data_cache['db_names']))
+		{
+			$key = array_search(strtolower($db_name), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
+			if ($key !== FALSE)
+			{
+				unset($this->db->data_cache['db_names'][$key]);
+			}
+		}
+
 		return TRUE;
 	}
 
@@ -123,7 +137,7 @@
 			return;
 		}
 
-		if ($key == '')
+		if ($key === '')
 		{
 			show_error('Key information is required for that operation.');
 		}
@@ -150,7 +164,7 @@
 	 */
 	public function add_field($field = '')
 	{
-		if ($field == '')
+		if ($field === '')
 		{
 			show_error('Field information is required.');
 		}
@@ -197,7 +211,7 @@
 	 */
 	public function create_table($table = '', $if_not_exists = FALSE)
 	{
-		if ($table == '')
+		if ($table === '')
 		{
 			show_error('A table name is required for that operation.');
 		}
@@ -209,7 +223,18 @@
 
 		$sql = $this->_create_table($this->db->dbprefix.$table, $this->fields, $this->primary_keys, $this->keys, $if_not_exists);
 		$this->_reset();
-		return is_bool($sql) ? $sql : $this->db->query($sql);
+
+		if (is_bool($sql))
+		{
+			return $sql;
+		}
+
+		if (($result = $this->db->query($sql)) !== FALSE && ! empty($this->db->data_cache['table_names']))
+		{
+			$this->db->data_cache['table_names'][] = $this->db->dbprefix.$table;
+		}
+
+		return $result;
 	}
 
 	// --------------------------------------------------------------------
@@ -222,7 +247,7 @@
 	 */
 	public function drop_table($table_name)
 	{
-		if ($table_name == '')
+		if ($table_name === '')
 		{
 			return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE;
 		}
@@ -231,7 +256,19 @@
 			return ($this->db->db_debug) ? $this->db->display_error('db_unsuported_feature') : FALSE;
 		}
 
-		return $this->db->query(sprintf($this->_drop_table, $this->db->escape_identifiers($this->db->dbprefix.$table_name)));
+		$result = $this->db->query(sprintf($this->_drop_table, $this->db->escape_identifiers($this->db->dbprefix.$table_name)));
+
+		// Update table list cache
+		if ($result && ! empty($this->db->data_cache['table_names']))
+		{
+			$key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
+			if ($key !== FALSE)
+			{
+				unset($this->db->data_cache['table_names'][$key]);
+			}
+		}
+
+		return $result;
 	}
 
 	// --------------------------------------------------------------------
@@ -245,7 +282,7 @@
 	 */
 	public function rename_table($table_name, $new_table_name)
 	{
-		if ($table_name == '' OR $new_table_name == '')
+		if ($table_name === '' OR $new_table_name === '')
 		{
 			show_error('A table name is required for that operation.');
 			return FALSE;
@@ -255,10 +292,21 @@
 			return ($this->db->db_debug) ? $this->db->display_error('db_unsuported_feature') : FALSE;
 		}
 
-		return $this->db->query(sprintf($this->_rename_table,
+		$result = $this->db->query(sprintf($this->_rename_table,
 						$this->db->escape_identifiers($this->db->dbprefix.$table_name),
 						$this->db->escape_identifiers($this->db->dbprefix.$new_table_name))
 					);
+
+		if ($result && ! empty($this->db->data_cache['table_names']))
+		{
+			$key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
+			if ($key !== FALSE)
+			{
+				$this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name;
+			}
+		}
+
+		return $result;
 	}
 
 	// --------------------------------------------------------------------
@@ -273,7 +321,7 @@
 	 */
 	public function add_column($table = '', $field = array(), $after_field = '')
 	{
-		if ($table == '')
+		if ($table === '')
 		{
 			show_error('A table name is required for that operation.');
 		}
@@ -284,7 +332,7 @@
 		{
 			$this->add_field(array($k => $field[$k]));
 
-			if (count($this->fields) == 0)
+			if (count($this->fields) === 0)
 			{
 				show_error('Field information is required.');
 			}
@@ -312,12 +360,12 @@
 	 */
 	public function drop_column($table = '', $column_name = '')
 	{
-		if ($table == '')
+		if ($table === '')
 		{
 			show_error('A table name is required for that operation.');
 		}
 
-		if ($column_name == '')
+		if ($column_name === '')
 		{
 			show_error('A column name is required for that operation.');
 		}
@@ -337,7 +385,7 @@
 	 */
 	public function modify_column($table = '', $field = array())
 	{
-		if ($table == '')
+		if ($table === '')
 		{
 			show_error('A table name is required for that operation.');
 		}
diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php
index cee4354..479b7f2 100644
--- a/system/database/DB_query_builder.php
+++ b/system/database/DB_query_builder.php
@@ -53,7 +53,6 @@
 	protected $qb_keys			= array();
 	protected $qb_limit			= FALSE;
 	protected $qb_offset			= FALSE;
-	protected $qb_order			= FALSE;
 	protected $qb_orderby			= array();
 	protected $qb_set			= array();
 	protected $qb_wherein			= array();
@@ -84,6 +83,7 @@
 	 * Generates the SELECT portion of the query
 	 *
 	 * @param	string
+	 * @param	mixed
 	 * @return	object
 	 */
 	public function select($select = '*', $escape = NULL)
@@ -93,11 +93,14 @@
 			$select = explode(',', $select);
 		}
 
+		// If the escape value was not set will will base it on the global setting
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
+
 		foreach ($select as $val)
 		{
 			$val = trim($val);
 
-			if ($val != '')
+			if ($val !== '')
 			{
 				$this->qb_select[] = $val;
 				$this->qb_no_escape[] = $escape;
@@ -194,7 +197,7 @@
 	 */
 	protected function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX')
 	{
-		if ( ! is_string($select) OR $select == '')
+		if ( ! is_string($select) OR $select === '')
 		{
 			$this->display_error('db_invalid_query');
 		}
@@ -206,7 +209,7 @@
 			show_error('Invalid function type: '.$type);
 		}
 
-		if ($alias == '')
+		if ($alias === '')
 		{
 			$alias = $this->_create_alias_from_table(trim($select));
 		}
@@ -321,15 +324,16 @@
 	 * @param	string
 	 * @param	string	the join condition
 	 * @param	string	the type of join
+	 * @param	string	whether not to try to escape identifiers
 	 * @return	object
 	 */
-	public function join($table, $cond, $type = '')
+	public function join($table, $cond, $type = '', $escape = NULL)
 	{
-		if ($type != '')
+		if ($type !== '')
 		{
 			$type = strtoupper(trim($type));
 
-			if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER')))
+			if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'), TRUE))
 			{
 				$type = '';
 			}
@@ -343,14 +347,51 @@
 		// in the protect_identifiers to know whether to add a table prefix
 		$this->_track_aliases($table);
 
-		// Strip apart the condition and protect the identifiers
-		if (preg_match('/([\[\w\.]+)([\W\s]+)(.+)/', $cond, $match))
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+		// Split multiple conditions
+		if ($escape === TRUE && preg_match_all('/\sAND\s|\sOR\s/i', $cond, $m, PREG_OFFSET_CAPTURE))
 		{
-			$cond = $this->protect_identifiers($match[1]).$match[2].$this->protect_identifiers($match[3]);
+			$newcond = '';
+			$m[0][] = array('', strlen($cond));
+
+			for ($i = 0, $c = count($m[0]), $s = 0;
+				$i < $c;
+				$s = $m[0][$i][1] + strlen($m[0][$i][0]), $i++)
+			{
+				$temp = substr($cond, $s, ($m[0][$i][1] - $s));
+
+				$newcond .= preg_match("/([\[\]\w\.'-]+)(\s*[^\"\[`'\w]+\s*)(.+)/i", $temp, $match)
+						? $this->protect_identifiers($match[1]).$match[2].$this->protect_identifiers($match[3])
+						: $temp;
+
+				$newcond .= $m[0][$i][0];
+			}
+
+			$cond = ' ON '.$newcond;
+		}
+		// Split apart the condition and protect the identifiers
+		elseif ($escape === TRUE && preg_match("/([\[\]\w\.'-]+)(\s*[^\"\[`'\w]+\s*)(.+)/i", $cond, $match))
+		{
+			$cond = ' ON '.$this->protect_identifiers($match[1]).$match[2].$this->protect_identifiers($match[3]);
+		}
+		elseif ( ! $this->_has_operator($cond))
+		{
+			$cond = ' USING ('.($escape ? $this->escape_identifiers($cond) : $cond).')';
+		}
+		else
+		{
+			$cond = ' ON '.$cond;
+		}
+
+		// Do we want to escape the table name?
+		if ($escape === TRUE)
+		{
+			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
 		}
 
 		// Assemble the JOIN statement
-		$this->qb_join[] = $join = $type.'JOIN '.$this->protect_identifiers($table, TRUE, NULL, FALSE).' ON '.$cond;
+		$this->qb_join[] = $join = $type.'JOIN '.$table.$cond;
 
 		if ($this->qb_caching === TRUE)
 		{
@@ -371,9 +412,10 @@
 	 *
 	 * @param	mixed
 	 * @param	mixed
+	 * @param	bool
 	 * @return	object
 	 */
-	public function where($key, $value = NULL, $escape = TRUE)
+	public function where($key, $value = NULL, $escape = NULL)
 	{
 		return $this->_where($key, $value, 'AND ', $escape);
 	}
@@ -388,9 +430,10 @@
 	 *
 	 * @param	mixed
 	 * @param	mixed
+	 * @param	bool
 	 * @return	object
 	 */
-	public function or_where($key, $value = NULL, $escape = TRUE)
+	public function or_where($key, $value = NULL, $escape = NULL)
 	{
 		return $this->_where($key, $value, 'OR ', $escape);
 	}
@@ -405,26 +448,31 @@
 	 * @param	mixed
 	 * @param	mixed
 	 * @param	string
+	 * @param	mixed
 	 * @return	object
 	 */
 	protected function _where($key, $value = NULL, $type = 'AND ', $escape = NULL)
 	{
-		$type = $this->_group_get_type($type);
-
 		if ( ! is_array($key))
 		{
 			$key = array($key => $value);
 		}
 
 		// If the escape value was not set will will base it on the global setting
-		if ( ! is_bool($escape))
-		{
-			$escape = $this->_protect_identifiers;
-		}
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
 
 		foreach ($key as $k => $v)
 		{
-			$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) ? '' : $type;
+			$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)
+				? $this->_group_get_type('')
+				: $this->_group_get_type($type);
+
+			if ($escape === TRUE)
+			{
+				$k = (($op = $this->_get_operator($k)) !== FALSE)
+					? $this->escape_identifiers(trim(substr($k, 0, strpos($k, $op)))).' '.strstr($k, $op)
+					: $this->escape_identifiers(trim($k));
+			}
 
 			if (is_null($v) && ! $this->_has_operator($k))
 			{
@@ -436,7 +484,6 @@
 			{
 				if ($escape === TRUE)
 				{
-					$k = $this->protect_identifiers($k, FALSE, $escape);
 					$v = ' '.$this->escape($v);
 				}
 
@@ -445,10 +492,6 @@
 					$k .= ' = ';
 				}
 			}
-			else
-			{
-				$k = $this->protect_identifiers($k, FALSE, $escape);
-			}
 
 			$this->qb_where[] = $prefix.$k.$v;
 			if ($this->qb_caching === TRUE)
@@ -474,9 +517,9 @@
 	 * @param	array	The values searched on
 	 * @return	object
 	 */
-	public function where_in($key = NULL, $values = NULL)
+	public function where_in($key = NULL, $values = NULL, $escape = NULL)
 	{
-		return $this->_where_in($key, $values);
+		return $this->_where_in($key, $values, FALSE, 'AND ', $escape);
 	}
 
 	// --------------------------------------------------------------------
@@ -491,9 +534,9 @@
 	 * @param	array	The values searched on
 	 * @return	object
 	 */
-	public function or_where_in($key = NULL, $values = NULL)
+	public function or_where_in($key = NULL, $values = NULL, $escape = NULL)
 	{
-		return $this->_where_in($key, $values, FALSE, 'OR ');
+		return $this->_where_in($key, $values, FALSE, 'OR ', $escape);
 	}
 
 	// --------------------------------------------------------------------
@@ -508,9 +551,9 @@
 	 * @param	array	The values searched on
 	 * @return	object
 	 */
-	public function where_not_in($key = NULL, $values = NULL)
+	public function where_not_in($key = NULL, $values = NULL, $escape = NULL)
 	{
-		return $this->_where_in($key, $values, TRUE);
+		return $this->_where_in($key, $values, TRUE, 'AND ', $escape);
 	}
 
 	// --------------------------------------------------------------------
@@ -525,9 +568,9 @@
 	 * @param	array	The values searched on
 	 * @return	object
 	 */
-	public function or_where_not_in($key = NULL, $values = NULL)
+	public function or_where_not_in($key = NULL, $values = NULL, $escape = NULL)
 	{
-		return $this->_where_in($key, $values, TRUE, 'OR ');
+		return $this->_where_in($key, $values, TRUE, 'OR ', $escape);
 	}
 
 	// --------------------------------------------------------------------
@@ -543,20 +586,20 @@
 	 * @param	string
 	 * @return	object
 	 */
-	protected function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ')
+	protected function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ', $escape = NULL)
 	{
 		if ($key === NULL OR $values === NULL)
 		{
-			return;
+			return $this;
 		}
 
-		$type = $this->_group_get_type($type);
-
 		if ( ! is_array($values))
 		{
 			$values = array($values);
 		}
 
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
+
 		$not = ($not) ? ' NOT' : '';
 
 		foreach ($values as $value)
@@ -564,8 +607,13 @@
 			$this->qb_wherein[] = $this->escape($value);
 		}
 
-		$prefix = (count($this->qb_where) === 0) ? '' : $type;
-		$this->qb_where[] = $where_in = $prefix.$this->protect_identifiers($key).$not.' IN ('.implode(', ', $this->qb_wherein).') ';
+		if ($escape === TRUE)
+		{
+			$key = $this->escape_identifiers(trim($key));
+		}
+
+		$prefix = (count($this->qb_where) === 0) ? $this->_group_get_type('') : $this->_group_get_type($type);
+		$this->qb_where[] = $where_in = $prefix.$key.$not.' IN ('.implode(', ', $this->qb_wherein).') ';
 
 		if ($this->qb_caching === TRUE)
 		{
@@ -660,8 +708,6 @@
 	 */
 	protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '')
 	{
-		$type = $this->_group_get_type($type);
-
 		if ( ! is_array($field))
 		{
 			$field = array($field => $match);
@@ -670,28 +716,28 @@
 		foreach ($field as $k => $v)
 		{
 			$k = $this->protect_identifiers($k);
-			$prefix = (count($this->qb_like) === 0) ? '' : $type;
+			$prefix = (count($this->qb_like) === 0) ? $this->_group_get_type('') : $this->_group_get_type($type);
 			$v = $this->escape_like_str($v);
 
 			if ($side === 'none')
 			{
-				$like_statement = $prefix." $k $not LIKE '{$v}'";
+				$like_statement = "{$prefix} $k $not LIKE '{$v}'";
 			}
 			elseif ($side === 'before')
 			{
-				$like_statement = $prefix." $k $not LIKE '%{$v}'";
+				$like_statement = "{$prefix} $k $not LIKE '%{$v}'";
 			}
 			elseif ($side === 'after')
 			{
-				$like_statement = $prefix." $k $not LIKE '{$v}%'";
+				$like_statement = "{$prefix} $k $not LIKE '{$v}%'";
 			}
 			else
 			{
-				$like_statement = $prefix." $k $not LIKE '%{$v}%'";
+				$like_statement = "{$prefix} $k $not LIKE '%{$v}%'";
 			}
 
 			// some platforms require an escape sequence definition for LIKE wildcards
-			if ($this->_like_escape_str != '')
+			if ($this->_like_escape_str !== '')
 			{
 				$like_statement = $like_statement.sprintf($this->_like_escape_str, $this->_like_escape_chr);
 			}
@@ -829,7 +875,7 @@
 		{
 			$val = trim($val);
 
-			if ($val != '')
+			if ($val !== '')
 			{
 				$this->qb_groupby[] = $val = $this->protect_identifiers($val);
 
@@ -853,9 +899,10 @@
 	 *
 	 * @param	string
 	 * @param	string
+	 * @param	bool
 	 * @return	object
 	 */
-	public function having($key, $value = '', $escape = TRUE)
+	public function having($key, $value = '', $escape = NULL)
 	{
 		return $this->_having($key, $value, 'AND ', $escape);
 	}
@@ -869,9 +916,10 @@
 	 *
 	 * @param	string
 	 * @param	string
+	 * @param	bool
 	 * @return	object
 	 */
-	public function or_having($key, $value = '', $escape = TRUE)
+	public function or_having($key, $value = '', $escape = NULL)
 	{
 		return $this->_having($key, $value, 'OR ', $escape);
 	}
@@ -885,30 +933,33 @@
 	 *
 	 * @param	string
 	 * @param	string
+	 * @param	string
+	 * @param	bool
 	 * @return	object
 	 */
-	protected function _having($key, $value = '', $type = 'AND ', $escape = TRUE)
+	protected function _having($key, $value = '', $type = 'AND ', $escape = NULL)
 	{
 		if ( ! is_array($key))
 		{
 			$key = array($key => $value);
 		}
 
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
+
 		foreach ($key as $k => $v)
 		{
 			$prefix = (count($this->qb_having) === 0) ? '' : $type;
 
-			if ($escape === TRUE)
-			{
-				$k = $this->protect_identifiers($k);
-			}
+			$k = $this->_has_operator($k)
+				? $this->protect_identifiers(substr($k, 0, strpos(rtrim($k), ' ')), FALSE, $escape).strchr(rtrim($k), ' ')
+				: $this->protect_identifiers($k, FALSE, $escape);
 
 			if ( ! $this->_has_operator($k))
 			{
 				$k .= ' = ';
 			}
 
-			if ($v != '')
+			if ($v !== '')
 			{
 				$v = ' '.$this->escape($v);
 			}
@@ -934,20 +985,21 @@
 	 * @param	bool	enable field name escaping
 	 * @return	object
 	 */
-	public function order_by($orderby, $direction = '', $escape = TRUE)
+	public function order_by($orderby, $direction = '', $escape = NULL)
 	{
 		if (strtolower($direction) === 'random')
 		{
 			$orderby = ''; // Random results want or don't need a field name
 			$direction = $this->_random_keyword;
 		}
-		elseif (trim($direction) != '')
+		elseif (trim($direction) !== '')
 		{
-			$direction = (in_array(strtoupper(trim($direction)), array('ASC', 'DESC'), TRUE)) ? ' '.$direction : ' ASC';
+			$direction = in_array(strtoupper(trim($direction)), array('ASC', 'DESC'), TRUE) ? ' '.$direction : ' ASC';
 		}
 
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
 
-		if ((strpos($orderby, ',') !== FALSE) && $escape === TRUE)
+		if ($escape === TRUE && strpos($orderby, ',') !== FALSE)
 		{
 			$temp = array();
 			foreach (explode(',', $orderby) as $part)
@@ -955,7 +1007,9 @@
 				$part = trim($part);
 				if ( ! in_array($part, $this->qb_aliased_tables))
 				{
-					$part = $this->protect_identifiers(trim($part));
+					$part = preg_match('/^(.+)\s+(ASC|DESC)$/i', $part, $matches)
+						? $this->protect_identifiers(rtrim($matches[1])).' '.$matches[2]
+						: $this->protect_identifiers($part);
 				}
 
 				$temp[] = $part;
@@ -963,12 +1017,11 @@
 
 			$orderby = implode(', ', $temp);
 		}
-		elseif ($direction != $this->_random_keyword)
+		elseif ($direction !== $this->_random_keyword && $escape === TRUE)
 		{
-			if ($escape === TRUE)
-			{
-				$orderby = $this->protect_identifiers($orderby);
-			}
+			$orderby = preg_match('/^(.+)\s+(ASC|DESC)$/i', $orderby, $matches)
+				? $this->protect_identifiers(rtrim($matches[1])).' '.$matches[2]
+				: $this->protect_identifiers($orderby);
 		}
 
 		$this->qb_orderby[] = $orderby_statement = $orderby.$direction;
@@ -993,12 +1046,8 @@
 	 */
 	public function limit($value, $offset = NULL)
 	{
-		$this->qb_limit = (int) $value;
-
-		if ( ! is_null($offset))
-		{
-			$this->qb_offset = (int) $offset;
-		}
+		is_null($value) OR $this->qb_limit = (int) $value;
+		empty($offset) OR $this->qb_offset = (int) $offset;
 
 		return $this;
 	}
@@ -1013,21 +1062,40 @@
 	 */
 	public function offset($offset)
 	{
-		$this->qb_offset = (int) $offset;
+		empty($offset) OR $this->qb_offset = (int) $offset;
 		return $this;
 	}
 
 	// --------------------------------------------------------------------
 
 	/**
-	 * The "set" function.  Allows key/value pairs to be set for inserting or updating
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		return $sql.' LIMIT '.($offset ? $offset.', ' : '').$limit;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * The "set" function.
+	 *
+	 * Allows key/value pairs to be set for inserting or updating
 	 *
 	 * @param	mixed
 	 * @param	string
 	 * @param	bool
 	 * @return	object
 	 */
-	public function set($key, $value = '', $escape = TRUE)
+	public function set($key, $value = '', $escape = NULL)
 	{
 		$key = $this->_object_to_array($key);
 
@@ -1036,16 +1104,12 @@
 			$key = array($key => $value);
 		}
 
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
+
 		foreach ($key as $k => $v)
 		{
-			if ($escape === FALSE)
-			{
-				$this->qb_set[$this->protect_identifiers($k)] = $v;
-			}
-			else
-			{
-				$this->qb_set[$this->protect_identifiers($k, FALSE, TRUE)] = $this->escape($v);
-			}
+			$this->qb_set[$this->protect_identifiers($k, FALSE, $escape)] = ($escape)
+				? $this->escape($v) : $v;
 		}
 
 		return $this;
@@ -1064,13 +1128,13 @@
 	 */
 	public function get_compiled_select($table = '', $reset = TRUE)
 	{
-		if ($table != '')
+		if ($table !== '')
 		{
 			$this->_track_aliases($table);
 			$this->from($table);
 		}
 
-		$select =  $this->_compile_select();
+		$select = $this->_compile_select();
 
 		if ($reset === TRUE)
 		{
@@ -1093,15 +1157,15 @@
 	 * @param	string	the offset clause
 	 * @return	object
 	 */
-	public function get($table = '', $limit = null, $offset = null)
+	public function get($table = '', $limit = NULL, $offset = NULL)
 	{
-		if ($table != '')
+		if ($table !== '')
 		{
 			$this->_track_aliases($table);
 			$this->from($table);
 		}
 
-		if ( ! is_null($limit))
+		if ( ! empty($limit))
 		{
 			$this->limit($limit, $offset);
 		}
@@ -1124,7 +1188,7 @@
 	 */
 	public function count_all_results($table = '')
 	{
-		if ($table != '')
+		if ($table !== '')
 		{
 			$this->_track_aliases($table);
 			$this->from($table);
@@ -1154,9 +1218,9 @@
 	 * @param	string	the offset clause
 	 * @return	object
 	 */
-	public function get_where($table = '', $where = null, $limit = null, $offset = null)
+	public function get_where($table = '', $where = NULL, $limit = NULL, $offset = NULL)
 	{
-		if ($table != '')
+		if ($table !== '')
 		{
 			$this->from($table);
 		}
@@ -1166,7 +1230,7 @@
 			$this->where($where);
 		}
 
-		if ( ! is_null($limit))
+		if ( ! empty($limit))
 		{
 			$this->limit($limit, $offset);
 		}
@@ -1204,7 +1268,7 @@
 			return FALSE;
 		}
 
-		if ($table == '')
+		if ($table === '')
 		{
 			if ( ! isset($this->qb_from[0]))
 			{
@@ -1251,7 +1315,7 @@
 	 * @param	bool
 	 * @return	object
 	 */
-	public function set_insert_batch($key, $value = '', $escape = TRUE)
+	public function set_insert_batch($key, $value = '', $escape = NULL)
 	{
 		$key = $this->_object_to_array_batch($key);
 
@@ -1260,6 +1324,8 @@
 			$key = array($key => $value);
 		}
 
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
+
 		$keys = array_keys($this->_object_to_array(current($key)));
 		sort($keys);
 
@@ -1275,11 +1341,7 @@
 
 			ksort($row); // puts $row in the same order as our keys
 
-			if ($escape === FALSE)
-			{
-				$this->qb_set[] =  '('.implode(',', $row).')';
-			}
-			else
+			if ($escape !== FALSE)
 			{
 				$clean = array();
 				foreach ($row as $value)
@@ -1287,13 +1349,15 @@
 					$clean[] = $this->escape($value);
 				}
 
-				$this->qb_set[] =  '('.implode(',', $clean).')';
+				$row = $clean;
 			}
+
+			$this->qb_set[] =  '('.implode(',', $row).')';
 		}
 
 		foreach ($keys as $k)
 		{
-			$this->qb_keys[] = $this->protect_identifiers($k);
+			$this->qb_keys[] = $this->protect_identifiers($k, FALSE, $escape);
 		}
 
 		return $this;
@@ -1371,23 +1435,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Insert statement
-	 *
-	 * Generates a platform-specific insert string from the supplied data
-	 *
-	 * @param	string	the table name
-	 * @param	array	the insert keys
-	 * @param	array	the insert values
-	 * @return	string
-	 */
-	protected function _insert($table, $keys, $values)
-	{
-		return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Validate Insert
 	 *
 	 * This method is used by both insert() and get_compiled_insert() to
@@ -1404,7 +1451,7 @@
 			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
 		}
 
-		if ($table != '')
+		if ($table !== '')
 		{
 			$this->qb_from[0] = $table;
 		}
@@ -1439,7 +1486,7 @@
 			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
 		}
 
-		if ($table == '')
+		if ($table === '')
 		{
 			if ( ! isset($this->qb_from[0]))
 			{
@@ -1475,6 +1522,24 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * From Tables
+	 *
+	 * This public function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param       array
+	 * @return      string
+	 */
+	protected function _from_tables($tables)
+	{
+		is_array($tables) OR $tables = array($tables);
+
+		return (count($tables) === 1) ? $tables[0] : '('.implode(', ', $tables).')';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Get UPDATE query string
 	 *
 	 * Compiles an update query and returns the sql
@@ -1530,12 +1595,12 @@
 			return FALSE;
 		}
 
-		if ($where != NULL)
+		if ($where !== NULL)
 		{
 			$this->where($where);
 		}
 
-		if ($limit != NULL)
+		if ( ! empty($limit))
 		{
 			$this->limit($limit);
 		}
@@ -1549,41 +1614,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Update statement
-	 *
-	 * Generates a platform-specific update string from the supplied data
-	 *
-	 * @param	string	the table name
-	 * @param	array	the update data
-	 * @param	array	the where clause
-	 * @param	array	the orderby clause
-	 * @param	array	the limit clause
-	 * @param	array	the like clause
-	 * @return	string
-	 */
-	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
-	{
-		foreach ($values as $key => $val)
-		{
-			$valstr[] = $key.' = '.$val;
-		}
-
-		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
-
-		if ( ! empty($like))
-		{
-			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
-		}
-
-		return 'UPDATE '.$table.' SET '.implode(', ', $valstr)
-			.$where
-			.(count($orderby) > 0 ? ' ORDER BY '.implode(', ', $orderby) : '')
-			.($limit ? ' LIMIT '.$limit : '');
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Validate Update
 	 *
 	 * This method is used by both update() and get_compiled_update() to
@@ -1595,12 +1625,12 @@
 	 */
 	protected function _validate_update($table = '')
 	{
-		if (count($this->qb_set) == 0)
+		if (count($this->qb_set) === 0)
 		{
 			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
 		}
 
-		if ($table != '')
+		if ($table !== '')
 		{
 			$this->qb_from[0] = $table;
 		}
@@ -1644,7 +1674,7 @@
 			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
 		}
 
-		if ($table == '')
+		if ($table === '')
 		{
 			if ( ! isset($this->qb_from[0]))
 			{
@@ -1674,7 +1704,7 @@
 	 * @param	bool
 	 * @return	object
 	 */
-	public function set_update_batch($key, $index = '', $escape = TRUE)
+	public function set_update_batch($key, $index = '', $escape = NULL)
 	{
 		$key = $this->_object_to_array_batch($key);
 
@@ -1683,18 +1713,20 @@
 			// @todo error
 		}
 
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
+
 		foreach ($key as $k => $v)
 		{
 			$index_set = FALSE;
 			$clean = array();
 			foreach ($v as $k2 => $v2)
 			{
-				if ($k2 == $index)
+				if ($k2 === $index)
 				{
 					$index_set = TRUE;
 				}
 
-				$clean[$this->protect_identifiers($k2)] = ($escape === FALSE) ? $v2 : $this->escape($v2);
+				$clean[$this->protect_identifiers($k2, FALSE, $escape)] = ($escape === FALSE) ? $v2 : $this->escape($v2);
 			}
 
 			if ($index_set === FALSE)
@@ -1720,7 +1752,7 @@
 	 */
 	public function empty_table($table = '')
 	{
-		if ($table == '')
+		if ($table === '')
 		{
 			if ( ! isset($this->qb_from[0]))
 			{
@@ -1753,7 +1785,7 @@
 	 */
 	public function truncate($table = '')
 	{
-		if ($table == '')
+		if ($table === '')
 		{
 			if ( ! isset($this->qb_from[0]))
 			{
@@ -1827,7 +1859,7 @@
 		// Combine any cached components with the current statements
 		$this->_merge_cache();
 
-		if ($table == '')
+		if ($table === '')
 		{
 			if ( ! isset($this->qb_from[0]))
 			{
@@ -1851,12 +1883,12 @@
 			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
 		}
 
-		if ($where != '')
+		if ($where !== '')
 		{
 			$this->where($where);
 		}
 
-		if ($limit != NULL)
+		if ( ! empty($limit))
 		{
 			$this->limit($limit);
 		}
@@ -1897,7 +1929,7 @@
 
 		return 'DELETE FROM '.$table
 			.(count($conditions) > 0 ? ' WHERE '.implode(' AND ', $conditions) : '')
-			.($limit ? ' LIMIT '.$limit : '');
+			.($limit ? ' LIMIT '.(int) $limit : '');
 	}
 
 	// --------------------------------------------------------------------
@@ -1912,7 +1944,7 @@
 	 */
 	public function dbprefix($table = '')
 	{
-		if ($table == '')
+		if ($table === '')
 		{
 			$this->display_error('db_table_name_required');
 		}
@@ -1967,7 +1999,7 @@
 		if (strpos($table, ' ') !== FALSE)
 		{
 			// if the alias is written with the AS keyword, remove it
-			$table = preg_replace('/ AS /i', ' ', $table);
+			$table = preg_replace('/\s+AS\s+/i', ' ', $table);
 
 			// Grab the alias
 			$table = trim(strrchr($table, ' '));
@@ -2070,10 +2102,6 @@
 		if (count($this->qb_orderby) > 0)
 		{
 			$sql .= "\nORDER BY ".implode(', ', $this->qb_orderby);
-			if ($this->qb_order !== FALSE)
-			{
-				$sql .= ($this->qb_order == 'desc') ? ' DESC' : ' ASC';
-			}
 		}
 
 		// Write the "LIMIT" portion of the query
@@ -2106,7 +2134,7 @@
 		foreach (get_object_vars($object) as $key => $val)
 		{
 			// There are some built in keys we need to ignore for this conversion
-			if ( ! is_object($val) && ! is_array($val) && $key != '_parent_name')
+			if ( ! is_object($val) && ! is_array($val) && $key !== '_parent_name')
 			{
 				$array[$key] = $val;
 			}
@@ -2303,8 +2331,7 @@
 					'qb_no_escape'		=> array(),
 					'qb_distinct'		=> FALSE,
 					'qb_limit'		=> FALSE,
-					'qb_offset'		=> FALSE,
-					'qb_order'		=> FALSE
+					'qb_offset'		=> FALSE
 					)
 				);
 	}
@@ -2327,8 +2354,7 @@
 			'qb_like'	=> array(),
 			'qb_orderby'	=> array(),
 			'qb_keys'	=> array(),
-			'qb_limit'	=> FALSE,
-			'qb_order'	=> FALSE
+			'qb_limit'	=> FALSE
 			)
 		);
 	}
diff --git a/system/database/DB_result.php b/system/database/DB_result.php
index 334e08c..d44df6c 100644
--- a/system/database/DB_result.php
+++ b/system/database/DB_result.php
@@ -38,32 +38,74 @@
  */
 class CI_DB_result {
 
-	public $conn_id				= NULL;
-	public $result_id			= NULL;
+	public $conn_id;
+	public $result_id;
 	public $result_array			= array();
 	public $result_object			= array();
 	public $custom_result_object		= array();
 	public $current_row			= 0;
-	public $num_rows			= 0;
-	public $row_data			= NULL;
+	public $num_rows;
+	public $row_data;
 
+	/**
+	 * Constructor
+	 *
+	 * @param	object
+	 * @return	void
+	 */
 	public function __construct(&$driver_object)
 	{
 		$this->conn_id = $driver_object->conn_id;
 		$this->result_id = $driver_object->result_id;
 	}
 
+	// --------------------------------------------------------------------
+
 	/**
-	 * Query result.  Acts as a wrapper function for the following functions.
+	 * Number of rows in the result set
 	 *
-	 * @param	string	can be "object" or "array"
-	 * @return	object
+	 * @return	int
+	 */
+	public function num_rows()
+	{
+		if (is_int($this->num_rows))
+		{
+			return $this->num_rows;
+		}
+		elseif (count($this->result_array) > 0)
+		{
+			return $this->num_rows = count($this->result_array);
+		}
+		elseif (count($this->result_object) > 0)
+		{
+			return $this->num_rows = count($this->result_object);
+		}
+
+		return $this->num_rows = count($this->result_array());
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Query result. Acts as a wrapper function for the following functions.
+	 *
+	 * @param	string	'object', 'array' or a custom class name
+	 * @return	array
 	 */
 	public function result($type = 'object')
 	{
-		if ($type === 'array') return $this->result_array();
-		elseif ($type === 'object') return $this->result_object();
-		else return $this->custom_result_object($type);
+		if ($type === 'array')
+		{
+			return $this->result_array();
+		}
+		elseif ($type === 'object')
+		{
+			return $this->result_object();
+		}
+		else
+		{
+			return $this->custom_result_object($type);
+		}
 	}
 
 	// --------------------------------------------------------------------
@@ -76,33 +118,50 @@
 	 */
 	public function custom_result_object($class_name)
 	{
-		if (array_key_exists($class_name, $this->custom_result_object))
+		if (isset($this->custom_result_object[$class_name]))
 		{
 			return $this->custom_result_object[$class_name];
 		}
-
-		if ($this->result_id === FALSE OR $this->num_rows() == 0)
+		elseif ( ! $this->result_id OR $this->num_rows === 0)
 		{
 			return array();
 		}
 
-		// add the data to the object
-		$this->_data_seek(0);
-		$result_object = array();
-
-		while ($row = $this->_fetch_object())
+		// Don't fetch the result set again if we already have it
+		$_data = NULL;
+		if (($c = count($this->result_array)) > 0)
 		{
-			$object = new $class_name();
-			foreach ($row as $key => $value)
-			{
-				$object->$key = $value;
-			}
-
-			$result_object[] = $object;
+			$_data = 'result_array';
+		}
+		elseif (($c = count($this->result_object)) > 0)
+		{
+			$_data = 'result_object';
 		}
 
-		// return the array
-		return $this->custom_result_object[$class_name] = $result_object;
+		if ($_data !== NULL)
+		{
+			for ($i = 0; $i < $c; $i++)
+			{
+				$this->custom_result_object[$class_name][$i] = new $class_name();
+
+				foreach ($this->{$_data}[$i] as $key => $value)
+				{
+					$this->custom_result_object[$class_name][$i]->$key = $value;
+				}
+			}
+
+			return $this->custom_result_object[$class_name];
+		}
+
+		$this->_data_seek(0);
+		$this->custom_result_object[$class_name] = array();
+
+		while ($row = $this->_fetch_object($class_name))
+		{
+			$this->custom_result_object[$class_name][] = $row;
+		}
+
+		return $this->custom_result_object[$class_name];
 	}
 
 	// --------------------------------------------------------------------
@@ -119,14 +178,24 @@
 			return $this->result_object;
 		}
 
-		// In the event that query caching is on the result_id variable
-		// will return FALSE since there isn't a valid SQL resource so
-		// we'll simply return an empty array.
-		if ($this->result_id === FALSE OR $this->num_rows() == 0)
+		// In the event that query caching is on, the result_id variable
+		// will not be a valid resource so we'll simply return an empty
+		// array.
+		if ( ! $this->result_id OR $this->num_rows === 0)
 		{
 			return array();
 		}
 
+		if (($c = count($this->result_array)) > 0)
+		{
+			for ($i = 0; $i < $c; $i++)
+			{
+				$this->result_object[$i] = (object) $this->result_array[$i];
+			}
+
+			return $this->result_object;
+		}
+
 		$this->_data_seek(0);
 		while ($row = $this->_fetch_object())
 		{
@@ -139,7 +208,7 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Query result.  "array" version.
+	 * Query result. "array" version.
 	 *
 	 * @return	array
 	 */
@@ -150,14 +219,24 @@
 			return $this->result_array;
 		}
 
-		// In the event that query caching is on the result_id variable
-		// will return FALSE since there isn't a valid SQL resource so
-		// we'll simply return an empty array.
-		if ($this->result_id === FALSE OR $this->num_rows() == 0)
+		// In the event that query caching is on, the result_id variable
+		// will not be a valid resource so we'll simply return an empty
+		// array.
+		if ( ! $this->result_id OR $this->num_rows === 0)
 		{
 			return array();
 		}
 
+		if (($c = count($this->result_object)) > 0)
+		{
+			for ($i = 0; $i < $c; $i++)
+			{
+				$this->result_array[$i] = (array) $this->result_object[$i];
+			}
+
+			return $this->result_array;
+		}
+
 		$this->_data_seek(0);
 		while ($row = $this->_fetch_assoc())
 		{
@@ -224,7 +303,7 @@
 			return;
 		}
 
-		if ($key != '' && ! is_null($value))
+		if ($key !== '' && ! is_null($value))
 		{
 			$this->row_data[$key] = $value;
 		}
@@ -239,18 +318,19 @@
 	 */
 	public function custom_row_object($n, $type)
 	{
-		$result = $this->custom_result_object($type);
-		if (count($result) === 0)
+		isset($this->custom_result_object[$type]) OR $this->custom_result_object($type);
+
+		if (count($this->custom_result_object[$type]) === 0)
 		{
 			return NULL;
 		}
 
-		if ($n != $this->current_row && isset($result[$n]))
+		if ($n !== $this->current_row && isset($this->custom_result_object[$type][$n]))
 		{
 			$this->current_row = $n;
 		}
 
-		return $result[$this->current_row];
+		return $this->custom_result_object[$type][$this->current_row];
 	}
 
 	// --------------------------------------------------------------------
@@ -268,7 +348,7 @@
 			return NULL;
 		}
 
-		if ($n != $this->current_row && isset($result[$n]))
+		if ($n !== $this->current_row && isset($result[$n]))
 		{
 			$this->current_row = $n;
 		}
@@ -291,7 +371,7 @@
 			return NULL;
 		}
 
-		if ($n != $this->current_row && isset($result[$n]))
+		if ($n !== $this->current_row && isset($result[$n]))
 		{
 			$this->current_row = $n;
 		}
@@ -375,11 +455,21 @@
 	/**
 	 * Returns an unbuffered row and move pointer to next row
 	 *
+	 * @param	string	'array', 'object' or a custom class name
 	 * @return	mixed	either a result object or array
 	 */
 	public function unbuffered_row($type = 'object')
 	{
-		return ($type !== 'array') ? $this->_fetch_object() : $this->_fetch_assoc();
+		if ($type === 'array')
+		{
+			return $this->_fetch_assoc();
+		}
+		elseif ($type === 'object')
+		{
+			return $this->_fetch_object();
+		}
+
+		return $this->_fetch_object($type);
 	}
 
 	// --------------------------------------------------------------------
@@ -393,7 +483,6 @@
 	 * operational due to the unavailability of the database resource IDs with
 	 * cached results.
 	 */
-	public function num_rows() { return $this->num_rows; }
 	public function num_fields() { return 0; }
 	public function list_fields() { return array(); }
 	public function field_data() { return array(); }
diff --git a/system/database/DB_utility.php b/system/database/DB_utility.php
index cb97ff4..6a3b407 100644
--- a/system/database/DB_utility.php
+++ b/system/database/DB_utility.php
@@ -35,7 +35,6 @@
 abstract class CI_DB_utility extends CI_DB_forge {
 
 	public $db;
-	public $data_cache		= array();
 
 	// Platform specific SQL strings
 	// Just setting those defaults to FALSE as they are mostly MySQL-specific
@@ -60,29 +59,29 @@
 	public function list_databases()
 	{
 		// Is there a cached result?
-		if (isset($this->data_cache['db_names']))
+		if (isset($this->db->data_cache['db_names']))
 		{
-			return $this->data_cache['db_names'];
+			return $this->db->data_cache['db_names'];
 		}
 		elseif ($this->_list_databases === FALSE)
 		{
 			return ($this->db->db_debug) ? $this->db->display_error('db_unsuported_feature') : FALSE;
 		}
 
-		$this->data_cache['db_names'] = array();
+		$this->db->data_cache['db_names'] = array();
 
 		$query = $this->db->query($this->_list_databases);
 		if ($query === FALSE)
 		{
-			return $this->data_cache['db_names'];
+			return $this->db->data_cache['db_names'];
 		}
 
 		for ($i = 0, $c = count($query); $i < $c; $i++)
 		{
-			$this->data_cache['db_names'] = current($query[$i]);
+			$this->db->data_cache['db_names'] = current($query[$i]);
 		}
 
-		return $this->data_cache['db_names'];
+		return $this->db->data_cache['db_names'];
 	}
 
 	// --------------------------------------------------------------------
@@ -343,7 +342,7 @@
 		if ($prefs['format'] === 'zip')
 		{
 			// Set the filename if not provided (only needed with Zip files)
-			if ($prefs['filename'] == '')
+			if ($prefs['filename'] === '')
 			{
 				$prefs['filename'] = (count($prefs['tables']) === 1 ? $prefs['tables'] : $this->db->database)
 							.date('Y-m-d_H-i', time()).'.sql';
@@ -369,7 +368,7 @@
 			$CI->zip->add_data($prefs['filename'], $this->_backup($prefs));
 			return $CI->zip->get_zip();
 		}
-		elseif ($prefs['format'] == 'txt') // Was a text file requested?
+		elseif ($prefs['format'] === 'txt') // Was a text file requested?
 		{
 			return $this->_backup($prefs);
 		}
diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php
index 817dfdc..a3d0287 100644
--- a/system/database/drivers/cubrid/cubrid_driver.php
+++ b/system/database/drivers/cubrid/cubrid_driver.php
@@ -49,12 +49,6 @@
 	protected $_like_escape_str = '';
 	protected $_like_escape_chr = '';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' RAND()'; // database specific random keyword
 
 	// CUBRID-specific properties
@@ -74,7 +68,7 @@
 		else
 		{
 			// If no port is defined by the user, use the default value
-			$this->port == '' OR $this->port = 33000;
+			empty($this->port) OR $this->port = 33000;
 		}
 	}
 
@@ -340,7 +334,7 @@
 	{
 		$sql = 'SHOW TABLES';
 
-		if ($prefix_limit !== FALSE && $this->dbprefix != '')
+		if ($prefix_limit !== FALSE && $this->dbprefix !== '')
 		{
 			return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
 		}
@@ -396,27 +390,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		if ( ! is_array($tables))
-		{
-			$tables = array($tables);
-		}
-
-		return '('.implode(', ', $tables).')';
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update_Batch statement
 	 *
 	 * Generates a platform-specific batch update string from the supplied data
@@ -435,7 +408,7 @@
 
 			foreach (array_keys($val) as $field)
 			{
-				if ($field != $index)
+				if ($field !== $index)
 				{
 					$final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
 				}
@@ -451,30 +424,13 @@
 		}
 
 		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2)
-			.' WHERE '.(($where != '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
+			.' WHERE '.(($where !== '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
 			.$index.' IN ('.implode(',', $ids).')';
 	}
 
 	// --------------------------------------------------------------------
 
 	/**
-	 * Limit string
-	 *
-	 * Generates a platform-specific LIMIT clause
-	 *
-	 * @param	string	the sql query string
-	 * @param	int	the number of rows to limit the query to
-	 * @param	int	the offset value
-	 * @return	string
-	 */
-	protected function _limit($sql, $limit, $offset)
-	{
-		return $sql.'LIMIT '.($offset == 0 ? '' : $offset.', ').$limit;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Close DB Connection
 	 *
 	 * @return	void
diff --git a/system/database/drivers/cubrid/cubrid_forge.php b/system/database/drivers/cubrid/cubrid_forge.php
index 4e66f81..d328aa2 100644
--- a/system/database/drivers/cubrid/cubrid_forge.php
+++ b/system/database/drivers/cubrid/cubrid_forge.php
@@ -60,8 +60,10 @@
 			else
 			{
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
-				$sql .= "\n\t".$this->db->protect_identifiers($field)
-					.( ! empty($attributes['NAME']) ? ' '.$this->db->protect_identifiers($attributes['NAME']).' ' : '');
+
+				$sql .= "\n\t".$this->db->escape_identifiers($field);
+
+				empty($attributes['NAME']) OR $sql .= ' '.$this->db->escape_identifiers($attributes['NAME']).' ';
 
 				if ( ! empty($attributes['TYPE']))
 				{
@@ -69,7 +71,7 @@
 
 					if ( ! empty($attributes['CONSTRAINT']))
 					{
-						switch ($attributes['TYPE'])
+						switch (strtolower($attributes['TYPE']))
 						{
 							case 'decimal':
 							case 'float':
@@ -98,10 +100,23 @@
 				}
 			 */
 
-				$sql .= (isset($attributes['DEFAULT']) ? " DEFAULT '".$attributes['DEFAULT']."'" : '')
-					.(( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE) ? ' NULL' : ' NOT NULL')
-					.(( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE) ? ' AUTO_INCREMENT' : '')
-					.(( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE) ? ' UNIQUE' : '');
+				if (isset($attributes['DEFAULT']))
+				{
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
+				}
+
+				$sql .= ( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE)
+					? ' NULL' : ' NOT NULL';
+
+				if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
+				{
+					$sql .= ' AUTO_INCREMENT';
+				}
+
+				if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
+				{
+					$sql .= ' UNIQUE';
+				}
 			}
 
 			// don't add a comma on the end of the last field
@@ -142,8 +157,8 @@
 		// If there is a PK defined
 		if (count($primary_keys) > 0)
 		{
-			$key_name = $this->db->protect_identifiers('pk_'.$table.'_'.implode('_', $primary_keys));
-			$sql .= ",\n\tCONSTRAINT ".$key_name.' PRIMARY KEY('.implode(', ', $this->db->protect_identifiers($primary_keys)).')';
+			$key_name = $this->db->escape_identifiers('pk_'.$table.'_'.implode('_', $primary_keys));
+			$sql .= ",\n\tCONSTRAINT ".$key_name.' PRIMARY KEY('.implode(', ', $this->db->escape_identifiers($primary_keys)).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
@@ -152,12 +167,12 @@
 			{
 				if (is_array($key))
 				{
-					$key_name = $this->db->protect_identifiers('idx_'.$table.implode('_', $key));
-					$key = $this->db->protect_identifiers($key);
+					$key_name = $this->db->escape_identifiers('idx_'.$table.implode('_', $key));
+					$key = $this->db->escape_identifiers($key);
 				}
 				else
 				{
-					$key_name = $this->db->protect_identifiers('idx_'.$table.$key);
+					$key_name = $this->db->escape_identifiers('idx_'.$table.$key);
 					$key = array($key_name);
 				}
 
@@ -184,16 +199,16 @@
 	 */
 	protected function _alter_table($alter_type, $table, $fields, $after_field = '')
 	{
-		$sql = 'ALTER TABLE '.$this->db->protect_identifiers($table).' '.$alter_type.' ';
+		$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' ';
 
 		// DROP has everything it needs now.
 		if ($alter_type === 'DROP')
 		{
-			return $sql.$this->db->protect_identifiers($fields);
+			return $sql.$this->db->escape_identifiers($fields);
 		}
 
 		return $sql.$this->_process_fields($fields)
-			.($after_field != '' ? ' AFTER '.$this->db->protect_identifiers($after_field) : '');
+			.($after_field !== '' ? ' AFTER '.$this->db->escape_identifiers($after_field) : '');
 	}
 
 }
diff --git a/system/database/drivers/cubrid/cubrid_result.php b/system/database/drivers/cubrid/cubrid_result.php
index 3eb9f7e..4a06a2d 100644
--- a/system/database/drivers/cubrid/cubrid_result.php
+++ b/system/database/drivers/cubrid/cubrid_result.php
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		Esen Sagynov
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	2.1
  */
 class CI_DB_cubrid_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return @cubrid_num_rows($this->result_id);
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = @cubrid_num_rows($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -157,11 +160,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return cubrid_fetch_object($this->result_id);
+		return cubrid_fetch_object($this->result_id, $class_name);
 	}
 
 }
diff --git a/system/database/drivers/cubrid/cubrid_utility.php b/system/database/drivers/cubrid/cubrid_utility.php
index c8cee99..ea8feb4 100644
--- a/system/database/drivers/cubrid/cubrid_utility.php
+++ b/system/database/drivers/cubrid/cubrid_utility.php
@@ -41,12 +41,12 @@
 	 */
 	public function list_databases()
 	{
-		if (isset($this->data_cache['db_names']))
+		if (isset($this->db->data_cache['db_names']))
 		{
-			return $this->data_cache['db_names'];
+			return $this->db->data_cache['db_names'];
 		}
 
-		return $this->data_cache['db_names'] = cubrid_list_dbs($this->db->conn_id);
+		return $this->db->data_cache['db_names'] = cubrid_list_dbs($this->db->conn_id);
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/interbase/interbase_driver.php b/system/database/drivers/ibase/ibase_driver.php
similarity index 89%
rename from system/database/drivers/interbase/interbase_driver.php
rename to system/database/drivers/ibase/ibase_driver.php
index 49d3cda..c902767 100644
--- a/system/database/drivers/interbase/interbase_driver.php
+++ b/system/database/drivers/ibase/ibase_driver.php
@@ -38,9 +38,9 @@
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
  */
-class CI_DB_interbase_driver extends CI_DB {
+class CI_DB_ibase_driver extends CI_DB {
 
-	public $dbdriver = 'interbase';
+	public $dbdriver = 'ibase';
 
 	// The character used to escape with
 	protected $_escape_char = '"';
@@ -49,13 +49,7 @@
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string	= 'SELECT COUNT(*) AS ';
-	protected $_random_keyword	= ' Random()'; // database specific random keyword
+	protected $_random_keyword = ' Random()'; // database specific random keyword
 
 	// Keeps track of the resource for the current transaction
 	protected $trans;
@@ -235,7 +229,7 @@
 	 * @param	int	$inc_by
 	 * @return	int
 	 */
-	public function insert_id($generator_name, $inc_by=0)
+	public function insert_id($generator_name, $inc_by = 0)
 	{
 		//If a generator hasn't been used before it will return 0
 		return ibase_gen_id('"'.$generator_name.'"', $inc_by);
@@ -255,9 +249,10 @@
 	{
 		$sql = 'SELECT "RDB$RELATION_NAME" FROM "RDB$RELATIONS" WHERE "RDB$RELATION_NAME" NOT LIKE \'RDB$%\' AND "RDB$RELATION_NAME" NOT LIKE \'MON$%\'';
 
-		if ($prefix_limit !== FALSE && $this->dbprefix != '')
+		if ($prefix_limit !== FALSE && $this->dbprefix !== '')
 		{
-			return $sql.' AND "RDB$RELATION_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
+			return $sql.' AND "RDB$RELATION_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
 		}
 
 		return $sql;
@@ -275,7 +270,7 @@
 	 */
 	protected function _list_columns($table = '')
 	{
-		return 'SELECT "RDB$FIELD_NAME" FROM "RDB$RELATION_FIELDS" WHERE "RDB$RELATION_NAME" = \''.$this->escape_str($table)."'";
+		return 'SELECT "RDB$FIELD_NAME" FROM "RDB$RELATION_FIELDS" WHERE "RDB$RELATION_NAME" = '.$this->escape($table);
 	}
 
 	// --------------------------------------------------------------------
@@ -290,10 +285,7 @@
 	 */
 	protected function _field_data($table)
 	{
-		// Need to find a more efficient way to do this
-		// but Interbase/Firebird seems to lack the
-		// limit clause
-		return 'SELECT * FROM '.$table;
+		return $this->_limit('SELECT * FROM '.$this->protect_identifiers($table), 1, NULL);
 	}
 
 	// --------------------------------------------------------------------
@@ -324,13 +316,7 @@
 	 */
 	protected function _from_tables($tables)
 	{
-		if ( ! is_array($tables))
-		{
-			$tables = array($tables);
-		}
-
-		//Interbase/Firebird doesn't like grouped tables
-		return implode(', ', $tables);
+		return is_array($tables) ? implode(', ', $tables) : $tables;
 	}
 
 	// --------------------------------------------------------------------
@@ -367,7 +353,6 @@
 			.(count($orderby) > 0 ? ' ORDER BY '.implode(', ', $orderby) : '');
 	}
 
-
 	// --------------------------------------------------------------------
 
 	/**
@@ -427,12 +412,12 @@
 		if (stripos($this->version(), 'firebird') !== FALSE)
 		{
 			$select = 'FIRST '. (int) $limit
-				.($offset > 0 ? ' SKIP '. (int) $offset : '');
+				.($offset ? ' SKIP '. (int) $offset : '');
 		}
 		else
 		{
 			$select = 'ROWS '
-				.($offset > 0 ? (int) $offset.' TO '.($limit + $offset) : (int) $limit);
+				.($offset ? (int) $offset.' TO '.($limit + $offset) : (int) $limit);
 		}
 
 		return preg_replace('`SELECT`i', 'SELECT '.$select, $sql);
@@ -452,5 +437,5 @@
 
 }
 
-/* End of file interbase_driver.php */
-/* Location: ./system/database/drivers/interbase/interbase_driver.php */
\ No newline at end of file
+/* End of file ibase_driver.php */
+/* Location: ./system/database/drivers/ibase/ibase_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/interbase/interbase_forge.php b/system/database/drivers/ibase/ibase_forge.php
similarity index 69%
rename from system/database/drivers/interbase/interbase_forge.php
rename to system/database/drivers/ibase/ibase_forge.php
index c850656..da75eb9 100644
--- a/system/database/drivers/interbase/interbase_forge.php
+++ b/system/database/drivers/ibase/ibase_forge.php
@@ -32,7 +32,7 @@
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
  */
-class CI_DB_interbase_forge extends CI_DB_forge {
+class CI_DB_ibase_forge extends CI_DB_forge {
 
 	protected $_drop_table	= 'DROP TABLE %s';
 
@@ -67,6 +67,14 @@
 		{
 			return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
 		}
+		elseif ( ! empty($this->db->data_cache['db_names']))
+		{
+			$key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
+			if ($key !== FALSE)
+			{
+				unset($this->db->data_cache['db_names'][$key]);
+			}
+		}
 
 		return TRUE;
 	}
@@ -87,7 +95,7 @@
 	{
 		$sql = 'CREATE TABLE ';
 
-		$sql .= $this->db->protect_identifiers($table)."(";
+		$sql .= $this->db->escape_identifiers($table).'(';
 		$current_field_count = 0;
 
 		foreach ($fields as $field => $attributes)
@@ -97,41 +105,30 @@
 			// entered the field information, so we'll simply add it to the list
 			if (is_numeric($field))
 			{
-				$sql .= "\n\t$attributes";
+				$sql .= "\n\t".$attributes;
 			}
 			else
 			{
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
 
-				$sql .= "\n\t".$this->db->protect_identifiers($field);
+				$sql .= "\n\t".$this->db->escape_identifiers($field).' '.$attributes['TYPE'];
 
-				$sql .=  ' '.$attributes['TYPE'];
+				empty($attributes['CONSTRAINT']) OR $sql .= '('.$attributes['CONSTRAINT'].')';
 
-				if (array_key_exists('CONSTRAINT', $attributes))
-				{
-					$sql .= '('.$attributes['CONSTRAINT'].')';
-				}
-
-				if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
+				if ( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE)
 				{
 					$sql .= ' UNSIGNED';
 				}
 
-				if (array_key_exists('DEFAULT', $attributes))
+				if (isset($attributes['DEFAULT']))
 				{
-					$sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
 				}
 
-				if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
-				{
-					$sql .= ' NULL';
-				}
-				else
-				{
-					$sql .= ' NOT NULL';
-				}
+				$sql .= ( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE)
+					? ' NULL' : ' NOT NULL';
 
-				if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
+				if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
 				{
 					$sql .= ' AUTO_INCREMENT';
 				}
@@ -146,30 +143,23 @@
 
 		if (count($primary_keys) > 0)
 		{
-			$primary_keys = $this->db->protect_identifiers($primary_keys);
-			$sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
+			$primary_keys = $this->db->escape_identifiers($primary_keys);
+			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $primary_keys).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
 		{
 			foreach ($keys as $key)
 			{
-				if (is_array($key))
-				{
-					$key = $this->db->protect_identifiers($key);
-				}
-				else
-				{
-					$key = array($this->db->protect_identifiers($key));
-				}
+				$key = is_array($key)
+					? $this->db->escape_identifiers($key)
+					: array($this->db->escape_identifiers($key));
 
-				$sql .= ",\n\tUNIQUE (" . implode(', ', $key) . ")";
+				$sql .= ",\n\tUNIQUE (".implode(', ', $key).')';
 			}
 		}
 
-		$sql .= "\n)";
-
-		return $sql;
+		return $sql."\n)";
 	}
 
 	// --------------------------------------------------------------------
@@ -191,34 +181,14 @@
 	 */
 	protected function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
 	{
-		$sql = 'ALTER TABLE '.$this->db->protect_identifiers($table)." $alter_type ".$this->db->protect_identifiers($column_name);
-
-		$sql .= " {$column_definition}";
-
-		if ($default_value != '')
-		{
-			$sql .= " DEFAULT \"{$default_value}\"";
-		}
-
-		if ($null === NULL)
-		{
-			$sql .= ' NULL';
-		}
-		else
-		{
-			$sql .= ' NOT NULL';
-		}
-
-		if ($after_field != '')
-		{
-			$sql .= ' AFTER ' . $this->db->protect_identifiers($after_field);
-		}
-
-		return $sql;
-
+		return 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' '.$this->db->escape_identifiers($column_name)
+			.' '.$column_definition
+			.($default_value !== '' ? ' DEFAULT "'.$default_value.'"' : '')
+			.($null === NULL ? ' NULL' : ' NOT NULL')
+			.($after_field !== '' ? ' AFTER '.$this->db->escape_identifiers($after_field) : '');
 	}
 
 }
 
-/* End of file interbase_forge.php */
-/* Location: ./system/database/drivers/interbase/interbase_forge.php */
\ No newline at end of file
+/* End of file ibase_forge.php */
+/* Location: ./system/database/drivers/ibase/ibase_forge.php */
\ No newline at end of file
diff --git a/system/database/drivers/ibase/ibase_result.php b/system/database/drivers/ibase/ibase_result.php
new file mode 100644
index 0000000..95e5571
--- /dev/null
+++ b/system/database/drivers/ibase/ibase_result.php
@@ -0,0 +1,155 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 1.0
+ * @filesource
+ */
+
+/**
+ * Interbase/Firebird Result Class
+ *
+ * This class extends the parent result class: CI_DB_result
+ *
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ * @since	3.0
+ */
+class CI_DB_ibase_result extends CI_DB_result {
+
+	/**
+	 * Number of fields in the result set
+	 *
+	 * @return	int
+	 */
+	public function num_fields()
+	{
+		return @ibase_num_fields($this->result_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Fetch Field Names
+	 *
+	 * Generates an array of column names
+	 *
+	 * @return	array
+	 */
+	public function list_fields()
+	{
+		$field_names = array();
+		for ($i = 0, $num_fields = $this->num_fields(); $i < $num_fields; $i++)
+		{
+			$info = ibase_field_info($this->result_id, $i);
+			$field_names[] = $info['name'];
+		}
+
+		return $field_names;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data
+	 *
+	 * Generates an array of objects containing field meta-data
+	 *
+	 * @return	array
+	 */
+	public function field_data()
+	{
+		$retval = array();
+		for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
+		{
+			$info = ibase_field_info($this->result_id, $i);
+
+			$retval[$i]			= new stdClass();
+			$retval[$i]->name		= $info['name'];
+			$retval[$i]->type		= $info['type'];
+			$retval[$i]->max_length		= $info['length'];
+			$retval[$i]->primary_key	= 0;
+			$retval[$i]->default		= '';
+		}
+
+		return $retval;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Free the result
+	 *
+	 * @return	void
+	 */
+	public function free_result()
+	{
+		@ibase_free_result($this->result_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Result - associative array
+	 *
+	 * Returns the result set as an array
+	 *
+	 * @return	array
+	 */
+	protected function _fetch_assoc()
+	{
+		return @ibase_fetch_assoc($this->result_id, IBASE_FETCH_BLOBS);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Result - object
+	 *
+	 * Returns the result set as an object
+	 *
+	 * @param	string
+	 * @return	object
+	 */
+	protected function _fetch_object($class_name = 'stdClass')
+	{
+		$row = @ibase_fetch_object($this->result_id, IBASE_FETCH_BLOBS);
+
+		if ($class_name === 'stdClass' OR ! $row)
+		{
+			return $row;
+		}
+
+		$class_name = new $class_name();
+		foreach ($row as $key => $value)
+		{
+			$class_name->$key = $value;
+		}
+
+		return $class_name;
+	}
+
+}
+
+/* End of file ibase_result.php */
+/* Location: ./system/database/drivers/ibase/ibase_result.php */
\ No newline at end of file
diff --git a/system/database/drivers/interbase/interbase_utility.php b/system/database/drivers/ibase/ibase_utility.php
similarity index 90%
rename from system/database/drivers/interbase/interbase_utility.php
rename to system/database/drivers/ibase/ibase_utility.php
index 1642118..d0e84a7 100644
--- a/system/database/drivers/interbase/interbase_utility.php
+++ b/system/database/drivers/ibase/ibase_utility.php
@@ -32,7 +32,7 @@
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
  */
-class CI_DB_interbase_utility extends CI_DB_utility {
+class CI_DB_ibase_utility extends CI_DB_utility {
 
 	protected $_list_databases	= FALSE;
 
@@ -58,5 +58,5 @@
 
 }
 
-/* End of file interbase_utility.php */
-/* Location: ./system/database/drivers/interbase/interbase_utility.php */
\ No newline at end of file
+/* End of file ibase_utility.php */
+/* Location: ./system/database/drivers/ibase/ibase_utility.php */
\ No newline at end of file
diff --git a/system/database/drivers/interbase/index.html b/system/database/drivers/ibase/index.html
similarity index 100%
rename from system/database/drivers/interbase/index.html
rename to system/database/drivers/ibase/index.html
diff --git a/system/database/drivers/interbase/interbase_result.php b/system/database/drivers/interbase/interbase_result.php
deleted file mode 100644
index 5ddb6fa..0000000
--- a/system/database/drivers/interbase/interbase_result.php
+++ /dev/null
@@ -1,260 +0,0 @@
-<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.2.4 or newer
- *
- * NOTICE OF LICENSE
- *
- * Licensed under the Open Software License version 3.0
- *
- * This source file is subject to the Open Software License (OSL 3.0) that is
- * bundled with this package in the files license.txt / license.rst.  It is
- * also available through the world wide web at this URL:
- * http://opensource.org/licenses/OSL-3.0
- * If you did not receive a copy of the license and are unable to obtain it
- * through the world wide web, please send an email to
- * licensing@ellislab.com so we can send you a copy immediately.
- *
- * @package		CodeIgniter
- * @author		EllisLab Dev Team
- * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
- * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
- * @link		http://codeigniter.com
- * @since		Version 3.0
- * @filesource
- */
-
-/**
- * Interbase/Firebird Result Class
- *
- * This class extends the parent result class: CI_DB_result
- *
- * @category	Database
- * @author		EllisLab Dev Team
- * @link		http://codeigniter.com/user_guide/database/
- */
-class CI_DB_interbase_result extends CI_DB_result {
-
-	public $num_rows;
-
-	/**
-	 * Number of rows in the result set
-	 *
-	 * @return	int
-	 */
-	public function num_rows()
-	{
-		if (is_int($this->num_rows))
-		{
-			return $this->num_rows;
-		}
-
-		// Get the results so that you can get an accurate rowcount
-		$this->result();
-
-		return $this->num_rows;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Number of fields in the result set
-	 *
-	 * @return	int
-	 */
-	public function num_fields()
-	{
-		return @ibase_num_fields($this->result_id);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Fetch Field Names
-	 *
-	 * Generates an array of column names
-	 *
-	 * @return	array
-	 */
-	public function list_fields()
-	{
-		$field_names = array();
-		for ($i = 0, $num_fields = $this->num_fields(); $i < $num_fields; $i++)
-		{
-			$info = ibase_field_info($this->result_id, $i);
-			$field_names[] = $info['name'];
-		}
-
-		return $field_names;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Field data
-	 *
-	 * Generates an array of objects containing field meta-data
-	 *
-	 * @return	array
-	 */
-	public function field_data()
-	{
-		$retval = array();
-		for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
-		{
-			$info = ibase_field_info($this->result_id, $i);
-
-			$retval[$i]			= new stdClass();
-			$retval[$i]->name		= $info['name'];
-			$retval[$i]->type		= $info['type'];
-			$retval[$i]->max_length		= $info['length'];
-			$retval[$i]->primary_key	= 0;
-			$retval[$i]->default		= '';
-		}
-
-		return $retval;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Free the result
-	 *
-	 * @return	void
-	 */
-	public function free_result()
-	{
-		@ibase_free_result($this->result_id);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Result - associative array
-	 *
-	 * Returns the result set as an array
-	 *
-	 * @return	array
-	 */
-	protected function _fetch_assoc()
-	{
-		if (($row = @ibase_fetch_assoc($this->result_id, IBASE_FETCH_BLOBS)) !== FALSE)
-		{
-			//Increment row count
-			$this->num_rows++;
-		}
-
-		return $row;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Result - object
-	 *
-	 * Returns the result set as an object
-	 *
-	 * @return	object
-	 */
-	protected function _fetch_object()
-	{
-		if (($row = @ibase_fetch_object($this->result_id, IBASE_FETCH_BLOBS)) !== FALSE)
-		{
-			//Increment row count
-			$this->num_rows++;
-		}
-
-		return $row;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result.  "object" version.
-	 *
-	 * @return	object
-	 */
-	public function result_object()
-	{
-		if (count($this->result_object) === $this->num_rows)
-		{
-			return $this->result_object;
-		}
-
-		// Convert result array to object so that
-		// We don't have to get the result again
-		if (($c = count($this->result_array)) > 0)
-		{
-			for ($i = 0; $i < $c; $i++)
-			{
-				$this->result_object[$i] = (object) $this->result_array[$i];
-			}
-
-			return $this->result_object;
-		}
-
-		// In the event that query caching is on the result_id variable
-		// will return FALSE since there isn't a valid SQL resource so
-		// we'll simply return an empty array.
-		if ($this->result_id === FALSE)
-		{
-			return array();
-		}
-
-		$this->num_rows = 0;
-		while ($row = $this->_fetch_object())
-		{
-			$this->result_object[] = $row;
-		}
-
-		return $this->result_object;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result.  "array" version.
-	 *
-	 * @return	array
-	 */
-	public function result_array()
-	{
-		if (count($this->result_array) === $this->num_rows)
-		{
-			return $this->result_array;
-		}
-
-		// Since the object and array are really similar, just case
-		// the result object to an array  if need be
-		if (($c = count($this->result_object)) > 0)
-		{
-			for ($i = 0; $i < $c; $i++)
-			{
-				$this->result_array[$i] = (array) $this->result_object[$i];
-			}
-
-			return $this->result_array;
-		}
-
-		// In the event that query caching is on the result_id variable
-		// will return FALSE since there isn't a valid SQL resource so
-		// we'll simply return an empty array.
-		if ($this->result_id === FALSE)
-		{
-			return array();
-		}
-
-		$this->num_rows = 0;
-		while ($row = $this->_fetch_assoc())
-		{
-			$this->result_array[] = $row;
-		}
-
-		return $this->result_array;
-	}
-
-}
-
-/* End of file interbase_result.php */
-/* Location: ./system/database/drivers/interbase/interbase_result.php */
\ No newline at end of file
diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php
index 342ff26..1714704 100644
--- a/system/database/drivers/mssql/mssql_driver.php
+++ b/system/database/drivers/mssql/mssql_driver.php
@@ -43,33 +43,61 @@
 	public $dbdriver = 'mssql';
 
 	// The character used for escaping
-	protected $_escape_char = '';
+	protected $_escape_char = '"';
 
 	// clause and character used for LIKE escape sequences
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() methods.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' NEWID()';
 
+	// MSSQL-specific properties
+	protected $_quoted_identifier = TRUE;
+
+	/*
+	 * Constructor
+	 *
+	 * Appends the port number to the hostname, if needed.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if ( ! empty($this->port))
+		{
+			$this->hostname .= (DIRECTORY_SEPARATOR === '\\' ? ',' : ':').$this->port;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
 	/**
 	 * Non-persistent database connection
 	 *
+	 * @param	bool
 	 * @return	resource
 	 */
-	public function db_connect()
+	public function db_connect($persistent = FALSE)
 	{
-		if ($this->port != '')
+		$this->conn_id = ($persistent)
+				? @mssql_pconnect($this->hostname, $this->username, $this->password)
+				: @mssql_connect($this->hostname, $this->username, $this->password);
+
+		if ( ! $this->conn_id)
 		{
-			$this->hostname .= ','.$this->port;
+			return FALSE;
 		}
 
-		return @mssql_connect($this->hostname, $this->username, $this->password);
+		// Determine how identifiers are escaped
+		$query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');
+		$query = $query->row_array();
+		$this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];
+		$this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']');
+
+		return $this->conn_id;
 	}
 
 	// --------------------------------------------------------------------
@@ -81,12 +109,7 @@
 	 */
 	public function db_pconnect()
 	{
-		if ($this->port != '')
-		{
-			$this->hostname .= ','.$this->port;
-		}
-
-		return @mssql_pconnect($this->hostname, $this->username, $this->password);
+		return $this->db_connect(TRUE);
 	}
 
 	// --------------------------------------------------------------------
@@ -106,7 +129,7 @@
 
 		// Note: The brackets are required in the event that the DB name
 		// contains reserved characters
-		if (@mssql_select_db('['.$database.']', $this->conn_id))
+		if (@mssql_select_db($this->escape_identifiers($database), $this->conn_id))
 		{
 			$this->database = $database;
 			return TRUE;
@@ -121,7 +144,7 @@
 	 * Execute the query
 	 *
 	 * @param	string	an SQL query
-	 * @return	resource
+	 * @return	mixed	resource if rows are returned, bool otherwise
 	 */
 	protected function _execute($sql)
 	{
@@ -137,13 +160,8 @@
 	 */
 	public function trans_begin($test_mode = FALSE)
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
@@ -151,10 +169,9 @@
 		// Reset the transaction failure flag.
 		// If the $test_mode flag is set to TRUE transactions will be rolled back
 		// even if the queries produce a successful result.
-		$this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
+		$this->_trans_failure = ($test_mode === TRUE);
 
-		$this->simple_query('BEGIN TRAN');
-		return TRUE;
+		return $this->simple_query('BEGIN TRAN');
 	}
 
 	// --------------------------------------------------------------------
@@ -166,19 +183,13 @@
 	 */
 	public function trans_commit()
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
 
-		$this->simple_query('COMMIT TRAN');
-		return TRUE;
+		return $this->simple_query('COMMIT TRAN');
 	}
 
 	// --------------------------------------------------------------------
@@ -190,19 +201,13 @@
 	 */
 	public function trans_rollback()
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
 
-		$this->simple_query('ROLLBACK TRAN');
-		return TRUE;
+		return $this->simple_query('ROLLBACK TRAN');
 	}
 
 	// --------------------------------------------------------------------
@@ -232,7 +237,7 @@
 		// escape LIKE condition wildcards
 		if ($like === TRUE)
 		{
-			$str = str_replace(
+			return str_replace(
 				array($this->_like_escape_chr, '%', '_'),
 				array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
 				$str
@@ -265,28 +270,13 @@
 	 */
 	public function insert_id()
 	{
-		$ver = self::_parse_major_version($this->version());
-		$sql = ($ver >= 8 ? "SELECT SCOPE_IDENTITY() AS last_id" : "SELECT @@IDENTITY AS last_id");
-		$query = $this->query($sql);
-		$row = $query->row();
-		return $row->last_id;
-	}
+		$query = version_compare($this->version(), '8', '>=')
+			? 'SELECT SCOPE_IDENTITY() AS last_id'
+			: 'SELECT @@IDENTITY AS last_id';
 
-	// --------------------------------------------------------------------
-
-	/**
-	 * Parse major version
-	 *
-	 * Grabs the major version number from the
-	 * database server version string passed in.
-	 *
-	 * @param	string	$version
-	 * @return	int	major version number
-	 */
-	protected function _parse_major_version($version)
-	{
-		preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info);
-		return $ver_info[1]; // return the major version b/c that's all we're interested in.
+		$query = $this->query($query);
+		$query = $query->row();
+		return $query->last_id;
 	}
 
 	// --------------------------------------------------------------------
@@ -313,16 +303,17 @@
 	 */
 	protected function _list_tables($prefix_limit = FALSE)
 	{
-		$sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
+		$sql = 'SELECT '.$this->escape_identifiers('name')
+			.' FROM '.$this->escape_identifiers('sysobjects')
+			.' WHERE '.$this->escape_identifiers('type')." = 'U'";
 
-		// for future compatibility
-		if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+		if ($prefix_limit !== FALSE AND $this->dbprefix !== '')
 		{
-			//$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
-			return FALSE; // not currently supported
+			$sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
 		}
 
-		return $sql;
+		return $sql.' ORDER BY '.$this->escape_identifiers('name');
 	}
 
 	// --------------------------------------------------------------------
@@ -352,7 +343,7 @@
 	 */
 	protected function _field_data($table)
 	{
-		return "SELECT TOP 1 * FROM ".$table;
+		return 'SELECT TOP 1 * FROM '.$this->protect_identifiers($table);
 	}
 
 	// --------------------------------------------------------------------
@@ -385,12 +376,7 @@
 	 */
 	protected function _from_tables($tables)
 	{
-		if ( ! is_array($tables))
-		{
-			$tables = array($tables);
-		}
-
-		return implode(', ', $tables);
+		return is_array($tables) ? implode(', ', $tables) : $tables;
 	}
 
 	// --------------------------------------------------------------------
@@ -484,9 +470,30 @@
 	 */
 	protected function _limit($sql, $limit, $offset)
 	{
-		$i = $limit + $offset;
+		// As of SQL Server 2012 (11.0.*) OFFSET is supported
+		if (version_compare($this->version(), '11', '>='))
+		{
+			return $sql.' OFFSET '.(int) $offset.' ROWS FETCH NEXT '.(int) $limit.' ROWS ONLY';
+		}
 
-		return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql);
+		$limit = $offset + $limit;
+
+		// As of SQL Server 2005 (9.0.*) ROW_NUMBER() is supported,
+		// however an ORDER BY clause is required for it to work
+		if (version_compare($this->version(), '9', '>=') && $offset && ! empty($this->qb_orderby))
+		{
+			$orderby = 'ORDER BY '.implode(', ', $this->qb_orderby);
+
+			// We have to strip the ORDER BY clause
+			$sql = trim(substr($sql, 0, strrpos($sql, 'ORDER BY '.$orderby)));
+
+			return 'SELECT '.(count($this->qb_select) === 0 ? '*' : implode(', ', $this->qb_select))." FROM (\n"
+				.preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.$orderby.') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)
+				."\n) ".$this->escape_identifiers('CI_subquery')
+				."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.((int) $offset + 1).' AND '.$limit;
+		}
+
+		return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/mssql/mssql_forge.php b/system/database/drivers/mssql/mssql_forge.php
index bbf2d96..3a3528f 100644
--- a/system/database/drivers/mssql/mssql_forge.php
+++ b/system/database/drivers/mssql/mssql_forge.php
@@ -70,31 +70,25 @@
 
 				$sql .= "\n\t".$this->db->escape_identifiers($field).' '.$attributes['TYPE'];
 
-				if (array_key_exists('CONSTRAINT', $attributes))
+				if (stripos($attributes['TYPE'], 'INT') === FALSE && ! empty($attributes['CONSTRAINT']))
 				{
 					$sql .= '('.$attributes['CONSTRAINT'].')';
 				}
 
-				if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
+				if ( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE)
 				{
 					$sql .= ' UNSIGNED';
 				}
 
-				if (array_key_exists('DEFAULT', $attributes))
+				if (isset($attributes['DEFAULT']))
 				{
-					$sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
 				}
 
-				if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
-				{
-					$sql .= ' NULL';
-				}
-				else
-				{
-					$sql .= ' NOT NULL';
-				}
+				$sql .= ( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE)
+					? ' NULL' : ' NOT NULL';
 
-				if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
+				if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
 				{
 					$sql .= ' AUTO_INCREMENT';
 				}
@@ -109,22 +103,16 @@
 
 		if (count($primary_keys) > 0)
 		{
-			$primary_keys = $this->db->protect_identifiers($primary_keys);
-			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $primary_keys).')';
+			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $this->db->escape_identifiers($primary_keys)).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
 		{
 			foreach ($keys as $key)
 			{
-				if (is_array($key))
-				{
-					$key = $this->db->protect_identifiers($key);
-				}
-				else
-				{
-					$key = array($this->db->protect_identifiers($key));
-				}
+				$key = is_array($key)
+					? $this->db->escape_identifiers($key)
+					: array($this->db->escape_identifiers($key));
 
 				$sql .= ",\n\tFOREIGN KEY (".implode(', ', $key).')';
 			}
@@ -152,29 +140,18 @@
 	 */
 	protected function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
 	{
-		$sql = 'ALTER TABLE '.$this->db->protect_identifiers($table).' '.$alter_type.' '.$this->db->protect_identifiers($column_name);
+		$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' '.$this->db->escape_identifiers($column_name);
 
 		// DROP has everything it needs now.
-		if ($alter_type == 'DROP')
+		if ($alter_type === 'DROP')
 		{
 			return $sql;
 		}
 
-		$sql .= " ".$column_definition;
-
-		if ($default_value != '')
-		{
-			$sql .= " DEFAULT '".$default_value."'";
-		}
-
-		$sql .= ($null === NULL) ? ' NULL' : ' NOT NULL';
-
-		if ($after_field != '')
-		{
-			return $sql.' AFTER '.$this->db->protect_identifiers($after_field);
-		}
-
-		return $sql;
+		return $sql.' '.$column_definition
+			.($default_value != '' ? ' DEFAULT "'.$default_value.'"' : '')
+			.($null === NULL ? ' NULL' : ' NOT NULL')
+			.($after_field != '' ? ' AFTER '.$this->db->escape_identifiers($after_field) : '');
 	}
 
 }
diff --git a/system/database/drivers/mssql/mssql_result.php b/system/database/drivers/mssql/mssql_result.php
index 4cc87f4..aeede3f 100644
--- a/system/database/drivers/mssql/mssql_result.php
+++ b/system/database/drivers/mssql/mssql_result.php
@@ -26,13 +26,14 @@
  */
 
 /**
- * MS SQL Result Class
+ * MSSQL Result Class
  *
  * This class extends the parent result class: CI_DB_result
  *
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.3
  */
 class CI_DB_mssql_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return @mssql_num_rows($this->result_id);
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = @mssql_num_rows($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -92,12 +95,12 @@
 		$retval = array();
 		while ($field = mssql_fetch_field($this->result_id))
 		{
-			$F				= new stdClass();
-			$F->name		= $field->name;
-			$F->type		= $field->type;
+			$F		= new stdClass();
+			$F->name	= $field->name;
+			$F->type	= $field->type;
 			$F->max_length	= $field->max_length;
 			$F->primary_key = 0;
-			$F->default		= '';
+			$F->default	= '';
 
 			$retval[] = $F;
 		}
@@ -158,11 +161,25 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return mssql_fetch_object($this->result_id);
+		$row = @mssql_fetch_object($this->result_id);
+
+		if ($class_name === 'stdClass' OR ! $row)
+		{
+			return $row;
+		}
+
+		$class_name = new $class_name();
+		foreach ($row as $key => $value)
+		{
+			$class_name->$key = $value;
+		}
+
+		return $class_name;
 	}
 
 }
diff --git a/system/database/drivers/mssql/mssql_utility.php b/system/database/drivers/mssql/mssql_utility.php
index 6d47618..69fcec5 100644
--- a/system/database/drivers/mssql/mssql_utility.php
+++ b/system/database/drivers/mssql/mssql_utility.php
@@ -41,7 +41,7 @@
 	 * MSSQL Export
 	 *
 	 * @param	array	Preferences
-	 * @return	mixed
+	 * @return	bool
 	 */
 	protected function _backup($params = array())
 	{
diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php
index 7a1a7b9..29db904 100644
--- a/system/database/drivers/mysql/mysql_driver.php
+++ b/system/database/drivers/mysql/mysql_driver.php
@@ -49,12 +49,6 @@
 	protected $_like_escape_str = '';
 	protected $_like_escape_chr = '\\';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' RAND()'; // database specific random keyword
 
 	/**
@@ -64,16 +58,24 @@
 	 */
 	public $delete_hack = TRUE;
 
+	/**
+	 * Constructor
+	 *
+	 * @param	array
+	 * @return	void
+	 */
 	public function __construct($params)
 	{
 		parent::__construct($params);
 
-		if ($this->port != '')
+		if ( ! empty($this->port))
 		{
 			$this->hostname .= ':'.$this->port;
 		}
 	}
 
+	// --------------------------------------------------------------------
+
 	/**
 	 * Non-persistent database connection
 	 *
@@ -335,9 +337,9 @@
 	 */
 	protected function _list_tables($prefix_limit = FALSE)
 	{
-		$sql = 'SHOW TABLES FROM '.$this->_escape_char.$this->database.$this->_escape_char;
+		$sql = 'SHOW TABLES FROM '.$this->escape_identifiers($this->database);
 
-		if ($prefix_limit !== FALSE && $this->dbprefix != '')
+		if ($prefix_limit !== FALSE && $this->dbprefix !== '')
 		{
 			return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
 		}
@@ -355,7 +357,7 @@
 	 * @param	string	the table name
 	 * @return	string
 	 */
-	public function _list_columns($table = '')
+	protected function _list_columns($table = '')
 	{
 		return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
 	}
@@ -370,7 +372,7 @@
 	 */
 	public function field_data($table = '')
 	{
-		if ($table == '')
+		if ($table === '')
 		{
 			return ($this->db_debug) ? $this->display_error('db_field_param_missing') : FALSE;
 		}
@@ -412,27 +414,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	string	table name
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		if ( ! is_array($tables))
-		{
-			$tables = array($tables);
-		}
-
-		return '('.implode(', ', $tables).')';
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update_Batch statement
 	 *
 	 * Generates a platform-specific batch update string from the supplied data
@@ -451,7 +432,7 @@
 
 			foreach (array_keys($val) as $field)
 			{
-				if ($field != $index)
+				if ($field !== $index)
 				{
 					$final[$field][] =  'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
 				}
@@ -467,30 +448,13 @@
 		}
 
 		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2)
-			.' WHERE '.(($where != '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
+			.' WHERE '.(($where !== '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
 			.$index.' IN('.implode(',', $ids).')';
 	}
 
 	// --------------------------------------------------------------------
 
 	/**
-	 * Limit string
-	 *
-	 * Generates a platform-specific LIMIT clause
-	 *
-	 * @param	string	the sql query string
-	 * @param	int	the number of rows to limit the query to
-	 * @param	int	the offset value
-	 * @return	string
-	 */
-	protected function _limit($sql, $limit, $offset)
-	{
-		return $sql.' LIMIT '.($offset == 0 ? '' : $offset.', ').$limit;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Close DB Connection
 	 *
 	 * @return	void
diff --git a/system/database/drivers/mysql/mysql_forge.php b/system/database/drivers/mysql/mysql_forge.php
index 0e39aff..2ac75ba 100644
--- a/system/database/drivers/mysql/mysql_forge.php
+++ b/system/database/drivers/mysql/mysql_forge.php
@@ -60,8 +60,9 @@
 			{
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
 
-				$sql .= "\n\t".$this->db->protect_identifiers($field)
-					.( ! empty($attributes['NAME']) ? ' '.$this->db->protect_identifiers($attributes['NAME']).' ' : '');
+				$sql .= "\n\t".$this->db->escape_identifiers($field);
+
+				empty($attributes['NAME']) OR $sql .= ' '.$this->db->escape_identifiers($attributes['NAME']).' ';
 
 				if ( ! empty($attributes['TYPE']))
 				{
@@ -86,10 +87,23 @@
 					}
 				}
 
-				$sql .= (( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE) ? ' UNSIGNED' : '')
-					.(isset($attributes['DEFAULT']) ? " DEFAULT '".$attributes['DEFAULT']."'" : '')
-					.(( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE) ? ' NULL' : ' NOT NULL')
-					.(( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE) ? ' AUTO_INCREMENT' : '');
+				if ( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE)
+				{
+					$sql .= ' UNSIGNED';
+				}
+
+				if (isset($attributes['DEFAULT']))
+				{
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
+				}
+
+				$sql .= ( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE)
+					? ' NULL' : ' NOT NULL';
+
+				if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
+				{
+					$sql .= ' AUTO_INCREMENT';
+				}
 			}
 
 			// don't add a comma on the end of the last field
@@ -123,12 +137,12 @@
 			$sql .= 'IF NOT EXISTS ';
 		}
 
-		$sql .= $this->db->protect_identifiers($table).' ('.$this->_process_fields($fields);
+		$sql .= $this->db->escape_identifiers($table).' ('.$this->_process_fields($fields);
 
 		if (count($primary_keys) > 0)
 		{
-			$key_name = $this->db->protect_identifiers(implode('_', $primary_keys));
-			$sql .= ",\n\tPRIMARY KEY ".$key_name.' ('.implode(', ', $this->db->protect_identifiers($primary_keys)).')';
+			$key_name = $this->db->escape_identifiers(implode('_', $primary_keys));
+			$sql .= ",\n\tPRIMARY KEY ".$key_name.' ('.implode(', ', $this->db->escape_identifiers($primary_keys)).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
@@ -137,12 +151,12 @@
 			{
 				if (is_array($key))
 				{
-					$key_name = $this->db->protect_identifiers(implode('_', $key));
-					$key = $this->db->protect_identifiers($key);
+					$key_name = $this->db->escape_identifiers(implode('_', $key));
+					$key = $this->db->escape_identifiers($key);
 				}
 				else
 				{
-					$key_name = $this->db->protect_identifiers($key);
+					$key_name = $this->db->escape_identifiers($key);
 					$key = array($key_name);
 				}
 
@@ -169,16 +183,16 @@
 	 */
 	protected function _alter_table($alter_type, $table, $fields, $after_field = '')
 	{
-		$sql = 'ALTER TABLE '.$this->db->protect_identifiers($table).' '.$alter_type.' ';
+		$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' ';
 
 		// DROP has everything it needs now.
 		if ($alter_type === 'DROP')
 		{
-			return $sql.$this->db->protect_identifiers($fields);
+			return $sql.$this->db->escape_identifiers($fields);
 		}
 
 		return $sql.$this->_process_fields($fields)
-			.($after_field != '' ? ' AFTER '.$this->db->protect_identifiers($after_field) : '');
+			.($after_field !== '' ? ' AFTER '.$this->db->escape_identifiers($after_field) : '');
 	}
 
 }
diff --git a/system/database/drivers/mysql/mysql_result.php b/system/database/drivers/mysql/mysql_result.php
index 14d6d07..7fbb654 100644
--- a/system/database/drivers/mysql/mysql_result.php
+++ b/system/database/drivers/mysql/mysql_result.php
@@ -33,17 +33,35 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.0
  */
 class CI_DB_mysql_result extends CI_DB_result {
 
 	/**
+	 * Constructor
+	 *
+	 * @param	object
+	 * @return	void
+	 */
+	public function __construct(&$driver_object)
+	{
+		parent::__construct($driver_object);
+
+		// Required, due to mysql_data_seek() causing nightmares
+		// with empty result sets
+		$this->num_rows = @mysql_num_rows($this->result_id);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Number of rows in the result set
 	 *
 	 * @return	int
 	 */
 	public function num_rows()
 	{
-		return @mysql_num_rows($this->result_id);
+		return $this->num_rows;
 	}
 
 	// --------------------------------------------------------------------
@@ -132,7 +150,9 @@
 	 */
 	protected function _data_seek($n = 0)
 	{
-		return mysql_data_seek($this->result_id, $n);
+		return $this->num_rows
+			? @mysql_data_seek($this->result_id, $n)
+			: FALSE;
 	}
 
 	// --------------------------------------------------------------------
@@ -156,11 +176,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return mysql_fetch_object($this->result_id);
+		return mysql_fetch_object($this->result_id, $class_name);
 	}
 
 }
diff --git a/system/database/drivers/mysql/mysql_utility.php b/system/database/drivers/mysql/mysql_utility.php
index 642323d..f0bbc66 100644
--- a/system/database/drivers/mysql/mysql_utility.php
+++ b/system/database/drivers/mysql/mysql_utility.php
@@ -65,7 +65,7 @@
 			}
 
 			// Get the table schema
-			$query = $this->db->query('SHOW CREATE TABLE '.$this->db->protect_identifiers($this->db->database).'.'.$this->db->protect_identifiers($table));
+			$query = $this->db->query('SHOW CREATE TABLE '.$this->db->escape_identifiers($this->db->database.'.'.$table));
 
 			// No result means the table name was invalid
 			if ($query === FALSE)
@@ -76,7 +76,7 @@
 			// Write out the table schema
 			$output .= '#'.$newline.'# TABLE STRUCTURE FOR: '.$table.$newline.'#'.$newline.$newline;
 
-			if ($add_drop == TRUE)
+			if ($add_drop === TRUE)
 			{
 				$output .= 'DROP TABLE IF EXISTS '.$this->db->protect_identifiers($table).';'.$newline.$newline;
 			}
@@ -92,7 +92,7 @@
 			}
 
 			// If inserts are not needed we're done...
-			if ($add_insert == FALSE)
+			if ($add_insert === FALSE)
 			{
 				continue;
 			}
@@ -100,7 +100,7 @@
 			// Grab all the data from the current table
 			$query = $this->db->query('SELECT * FROM '.$this->db->protect_identifiers($table));
 
-			if ($query->num_rows() == 0)
+			if ($query->num_rows() === 0)
 			{
 				continue;
 			}
@@ -120,7 +120,7 @@
 							TRUE);
 
 				// Create a string of field names
-				$field_str .= $this->db->protect_identifiers($field->name).', ';
+				$field_str .= $this->db->escape_identifiers($field->name).', ';
 				$i++;
 			}
 
@@ -143,7 +143,7 @@
 					else
 					{
 						// Escape the data if it's not an integer
-						$val_str .= ($is_int[$i] == FALSE) ? $this->db->escape($v) : $v;
+						$val_str .= ($is_int[$i] === FALSE) ? $this->db->escape($v) : $v;
 					}
 
 					// Append a comma
diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php
index dd544f6..be61aab 100644
--- a/system/database/drivers/mysqli/mysqli_driver.php
+++ b/system/database/drivers/mysqli/mysqli_driver.php
@@ -49,12 +49,6 @@
 	protected $_like_escape_str = '';
 	protected $_like_escape_chr = '\\';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' RAND()'; // database specific random keyword
 
 	/**
@@ -71,9 +65,9 @@
 	 */
 	public function db_connect()
 	{
-		return ($this->port != '')
-			? @new mysqli($this->hostname, $this->username, $this->password, $this->database, $this->port)
-			: @new mysqli($this->hostname, $this->username, $this->password, $this->database);
+		return empty($this->port)
+			? @new mysqli($this->hostname, $this->username, $this->password, $this->database)
+			: @new mysqli($this->hostname, $this->username, $this->password, $this->database, $this->port);
 	}
 
 	// --------------------------------------------------------------------
@@ -91,9 +85,9 @@
 			return $this->db_connect();
 		}
 
-		return ($this->port != '')
-			? @new mysqli('p:'.$this->hostname, $this->username, $this->password, $this->database, $this->port)
-			: @new mysqli('p:'.$this->hostname, $this->username, $this->password, $this->database);
+		return empty($this->port)
+			? @new mysqli('p:'.$this->hostname, $this->username, $this->password, $this->database)
+			: @new mysqli('p:'.$this->hostname, $this->username, $this->password, $this->database, $this->port);
 	}
 
 	// --------------------------------------------------------------------
@@ -335,9 +329,9 @@
 	 */
 	protected function _list_tables($prefix_limit = FALSE)
 	{
-		$sql = 'SHOW TABLES FROM '.$this->_escape_char.$this->database.$this->_escape_char;
+		$sql = 'SHOW TABLES FROM '.$this->escape_identifiers($this->database);
 
-		if ($prefix_limit !== FALSE && $this->dbprefix != '')
+		if ($prefix_limit !== FALSE && $this->dbprefix !== '')
 		{
 			return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
 		}
@@ -370,7 +364,7 @@
 	 */
 	public function field_data($table = '')
 	{
-		if ($table == '')
+		if ($table === '')
 		{
 			return ($this->db_debug) ? $this->display_error('db_field_param_missing') : FALSE;
 		}
@@ -412,27 +406,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		if ( ! is_array($tables))
-		{
-			$tables = array($tables);
-		}
-
-		return '('.implode(', ', $tables).')';
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update_Batch statement
 	 *
 	 * Generates a platform-specific batch update string from the supplied data
@@ -451,7 +424,7 @@
 
 			foreach (array_keys($val) as $field)
 			{
-				if ($field != $index)
+				if ($field !== $index)
 				{
 					$final[$field][] =  'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
 				}
@@ -466,34 +439,16 @@
 				.'ELSE '.$k.' END, ';
 		}
 
-		$where = ($where != '' && count($where) > 0) ? implode(' ', $where).' AND ' : '';
+		$where = ($where !== '' && count($where) > 0) ? implode(' ', $where).' AND ' : '';
 
 		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2)
-			.' WHERE '.(($where != '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
+			.' WHERE '.(($where !== '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
 			.$index.' IN('.implode(',', $ids).')';
 	}
 
 	// --------------------------------------------------------------------
 
 	/**
-	 * Limit string
-	 *
-	 * Generates a platform-specific LIMIT clause
-	 *
-	 * @param	string	the sql query string
-	 * @param	int	the number of rows to limit the query to
-	 * @param	int	the offset value
-	 * @return	string
-	 */
-	protected function _limit($sql, $limit, $offset)
-	{
-		return $sql.' LIMIT '.$limit
-			.($offset > 0 ? ' OFFSET '.$offset : '');
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Close DB Connection
 	 *
 	 * @return	void
diff --git a/system/database/drivers/mysqli/mysqli_forge.php b/system/database/drivers/mysqli/mysqli_forge.php
index 503574d..b74c775 100644
--- a/system/database/drivers/mysqli/mysqli_forge.php
+++ b/system/database/drivers/mysqli/mysqli_forge.php
@@ -60,13 +60,13 @@
 			{
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
 
-				$sql .= "\n\t".$this->db->protect_identifiers($field)
-					.( ! empty($attributes['NAME']) ? ' '.$this->db->protect_identifiers($attributes['NAME']).' ' : '')
-				;
+				$sql .= "\n\t".$this->db->escape_identifiers($field);
+
+				empty($attributes['NAME']) OR $sql .= ' '.$this->db->escape_identifiers($attributes['NAME']).' ';
 
 				if ( ! empty($attributes['TYPE']))
 				{
-					$sql .=  ' '.$attributes['TYPE'];
+					$sql .= ' '.$attributes['TYPE'];
 
 					if ( ! empty($attributes['CONSTRAINT']))
 					{
@@ -87,10 +87,23 @@
 					}
 				}
 
-				$sql .= (( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE) ? ' UNSIGNED' : '')
-					.(isset($attributes['DEFAULT']) ? " DEFAULT '".$attributes['DEFAULT']."'" : '')
-					.(( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE) ? ' NULL' : ' NOT NULL')
-					.(( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE) ? ' AUTO_INCREMENT' : '');
+				if ( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE)
+				{
+					$sql .= ' UNSIGNED';
+				}
+
+				if (isset($attributes['DEFAULT']))
+				{
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
+				}
+
+				$sql .= ( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE)
+					? ' NULL' : ' NOT NULL';
+
+				if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
+				{
+					$sql .= ' AUTO_INCREMENT';
+				}
 			}
 
 			// don't add a comma on the end of the last field
@@ -128,8 +141,8 @@
 
 		if (count($primary_keys) > 0)
 		{
-			$key_name = $this->db->protect_identifiers(implode('_', $primary_keys));
-			$sql .= ",\n\tPRIMARY KEY ".$key_name.' ('.implode(', ', $this->db->protect_identifiers($primary_keys)).')';
+			$key_name = $this->db->escape_identifiers(implode('_', $primary_keys));
+			$sql .= ",\n\tPRIMARY KEY ".$key_name.' ('.implode(', ', $this->db->escape_identifiers($primary_keys)).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
@@ -138,12 +151,12 @@
 			{
 				if (is_array($key))
 				{
-					$key_name = $this->db->protect_identifiers(implode('_', $key));
-					$key = $this->db->protect_identifiers($key);
+					$key_name = $this->db->escape_identifiers(implode('_', $key));
+					$key = $this->db->escape_identifiers($key);
 				}
 				else
 				{
-					$key_name = $this->db->protect_identifiers($key);
+					$key_name = $this->db->escape_identifiers($key);
 					$key = array($key_name);
 				}
 
@@ -170,16 +183,16 @@
 	 */
 	protected function _alter_table($alter_type, $table, $fields, $after_field = '')
 	{
-		$sql = 'ALTER TABLE '.$this->db->protect_identifiers($table).' '.$alter_type.' ';
+		$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' ';
 
 		// DROP has everything it needs now.
 		if ($alter_type === 'DROP')
 		{
-			return $sql.$this->db->protect_identifiers($fields);
+			return $sql.$this->db->escape_identifiers($fields);
 		}
 
 		return $sql.$this->_process_fields($fields)
-			.($after_field != '' ? ' AFTER '.$this->db->protect_identifiers($after_field) : '');
+			.($after_field !== '' ? ' AFTER '.$this->db->escape_identifiers($after_field) : '');
 	}
 
 }
diff --git a/system/database/drivers/mysqli/mysqli_result.php b/system/database/drivers/mysqli/mysqli_result.php
index 9b4d494..c1ec4da 100644
--- a/system/database/drivers/mysqli/mysqli_result.php
+++ b/system/database/drivers/mysqli/mysqli_result.php
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.3
  */
 class CI_DB_mysqli_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return $this->result_id->num_rows;
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = $this->result_id->num_rows;
 	}
 
 	// --------------------------------------------------------------------
@@ -157,11 +160,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return $this->result_id->fetch_object();
+		return $this->result_id->fetch_object($class_name);
 	}
 
 }
diff --git a/system/database/drivers/mysqli/mysqli_utility.php b/system/database/drivers/mysqli/mysqli_utility.php
index 27d4ef8..5d2bdbc 100644
--- a/system/database/drivers/mysqli/mysqli_utility.php
+++ b/system/database/drivers/mysqli/mysqli_utility.php
@@ -46,9 +46,124 @@
 	 */
 	protected function _backup($params = array())
 	{
-		// Currently unsupported
-		return $this->db->display_error('db_unsuported_feature');
+		if (count($params) === 0)
+		{
+			return FALSE;
+		}
+
+		// Extract the prefs for simplicity
+		extract($params);
+
+		// Build the output
+		$output = '';
+		foreach ( (array) $tables as $table)
+		{
+			// Is the table in the "ignore" list?
+			if (in_array($table, (array) $ignore, TRUE))
+			{
+				continue;
+			}
+
+			// Get the table schema
+			$query = $this->db->query('SHOW CREATE TABLE '.$this->db->escape_identifiers($this->db->database.'.'.$table));
+
+			// No result means the table name was invalid
+			if ($query === FALSE)
+			{
+				continue;
+			}
+
+			// Write out the table schema
+			$output .= '#'.$newline.'# TABLE STRUCTURE FOR: '.$table.$newline.'#'.$newline.$newline;
+
+			if ($add_drop === TRUE)
+			{
+				$output .= 'DROP TABLE IF EXISTS '.$this->db->protect_identifiers($table).';'.$newline.$newline;
+			}
+
+			$i = 0;
+			$result = $query->result_array();
+			foreach ($result[0] as $val)
+			{
+				if ($i++ % 2)
+				{
+					$output .= $val.';'.$newline.$newline;
+				}
+			}
+
+			// If inserts are not needed we're done...
+			if ($add_insert === FALSE)
+			{
+				continue;
+			}
+
+			// Grab all the data from the current table
+			$query = $this->db->query('SELECT * FROM '.$this->db->protect_identifiers($table));
+
+			if ($query->num_rows() === 0)
+			{
+				continue;
+			}
+
+			// Fetch the field names and determine if the field is an
+			// integer type. We use this info to decide whether to
+			// surround the data with quotes or not
+
+			$i = 0;
+			$field_str = '';
+			$is_int = array();
+			while ($field = $query->result_id->fetch_field())
+			{
+				// Most versions of MySQL store timestamp as a string
+				$is_int[$i] = in_array(strtolower($field->type),
+							array('tinyint', 'smallint', 'mediumint', 'int', 'bigint'), //, 'timestamp'),
+							TRUE);
+
+				// Create a string of field names
+				$field_str .= $this->db->escape_identifiers($field->name).', ';
+				$i++;
+			}
+
+			// Trim off the end comma
+			$field_str = preg_replace('/, $/' , '', $field_str);
+
+			// Build the insert string
+			foreach ($query->result_array() as $row)
+			{
+				$val_str = '';
+
+				$i = 0;
+				foreach ($row as $v)
+				{
+					// Is the value NULL?
+					if ($v === NULL)
+					{
+						$val_str .= 'NULL';
+					}
+					else
+					{
+						// Escape the data if it's not an integer
+						$val_str .= ($is_int[$i] === FALSE) ? $this->db->escape($v) : $v;
+					}
+
+					// Append a comma
+					$val_str .= ', ';
+					$i++;
+				}
+
+				// Remove the comma at the end of the string
+				$val_str = preg_replace('/, $/' , '', $val_str);
+
+				// Build the INSERT string
+				$output .= 'INSERT INTO '.$this->db->protect_identifiers($table).' ('.$field_str.') VALUES ('.$val_str.');'.$newline;
+			}
+
+			$output .= $newline.$newline;
+		}
+
+		return $output;
 	}
+
 }
 
 /* End of file mysqli_utility.php */
diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php
index b979c8a..691247f 100644
--- a/system/database/drivers/oci8/oci8_driver.php
+++ b/system/database/drivers/oci8/oci8_driver.php
@@ -66,6 +66,8 @@
 	protected $_count_string = 'SELECT COUNT(1) AS ';
 	protected $_random_keyword = ' ASC'; // not currently supported
 
+	protected $_reserved_identifiers = array('*', 'rownum');
+
 	// Set "auto commit" by default
 	public $commit_mode = OCI_COMMIT_ON_SUCCESS;
 
@@ -156,6 +158,8 @@
 		$this->dsn = '';
 	}
 
+	// --------------------------------------------------------------------
+
 	/**
 	 * Non-persistent database connection
 	 *
@@ -177,9 +181,9 @@
 	 */
 	public function db_pconnect()
 	{
-		return ( ! empty($this->char_set))
-			? @oci_pconnect($this->username, $this->password, $this->dsn, $this->char_set)
-			: @oci_pconnect($this->username, $this->password, $this->dsn);
+		return empty($this->char_set)
+			? @oci_pconnect($this->username, $this->password, $this->dsn)
+			: @oci_pconnect($this->username, $this->password, $this->dsn, $this->char_set);
 	}
 
 	// --------------------------------------------------------------------
@@ -215,6 +219,8 @@
 		return @oci_execute($this->stmt_id, $this->commit_mode);
 	}
 
+	// --------------------------------------------------------------------
+
 	/**
 	 * Generate a statement ID
 	 *
@@ -234,7 +240,7 @@
 	/**
 	 * Get cursor. Returns a cursor from the database
 	 *
-	 * @return	cursor id
+	 * @return	resource
 	 */
 	public function get_cursor()
 	{
@@ -262,7 +268,7 @@
 	 */
 	public function stored_procedure($package, $procedure, $params)
 	{
-		if ($package == '' OR $procedure == '' OR ! is_array($params))
+		if ($package === '' OR $procedure === '' OR ! is_array($params))
 		{
 			if ($this->db_debug)
 			{
@@ -298,6 +304,7 @@
 	/**
 	 * Bind parameters
 	 *
+	 * @param	array
 	 * @return	void
 	 */
 	protected function _bind_params($params)
@@ -326,6 +333,7 @@
 	/**
 	 * Begin Transaction
 	 *
+	 * @param	bool
 	 * @return	bool
 	 */
 	public function trans_begin($test_mode = FALSE)
@@ -464,11 +472,12 @@
 	 */
 	protected function _list_tables($prefix_limit = FALSE)
 	{
-		$sql = 'SELECT TABLE_NAME FROM ALL_TABLES';
+		$sql = 'SELECT "TABLE_NAME" FROM "ALL_TABLES"';
 
-		if ($prefix_limit !== FALSE && $this->dbprefix != '')
+		if ($prefix_limit !== FALSE && $this->dbprefix !== '')
 		{
-			return $sql." WHERE TABLE_NAME LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
+			return $sql.' WHERE "TABLE_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
 		}
 
 		return $sql;
@@ -486,7 +495,7 @@
 	 */
 	protected function _list_columns($table = '')
 	{
-		return 'SELECT COLUMN_NAME FROM all_tab_columns WHERE table_name = \''.$table.'\'';
+		return 'SELECT "COLUMN_NAME" FROM "all_tab_columns" WHERE "TABLE_NAME" = '.$this->escape($table);
 	}
 
 	// --------------------------------------------------------------------
@@ -501,7 +510,7 @@
 	 */
 	protected function _field_data($table)
 	{
-		return 'SELECT * FROM '.$table.' WHERE rownum = 1';
+		return 'SELECT * FROM '.$this->protect_identifiers($table).' WHERE rownum = 1';
 	}
 
 	// --------------------------------------------------------------------
@@ -633,8 +642,8 @@
 	protected function _limit($sql, $limit, $offset)
 	{
 		$this->limit_used = TRUE;
-		return 'SELECT * FROM (SELECT inner_query.*, rownum rnum FROM ('.$sql.') inner_query WHERE rownum < '.($offset + $limit).')'
-			.($offset != 0 ? ' WHERE rnum >= '.$offset : '');
+		return 'SELECT * FROM (SELECT inner_query.*, rownum rnum FROM ('.$sql.') inner_query WHERE rownum < '.($offset + $limit + 1).')'
+			.($offset ? ' WHERE rnum >= '.($offset + 1): '');
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/oci8/oci8_forge.php b/system/database/drivers/oci8/oci8_forge.php
index bd265b6..92e8c02 100644
--- a/system/database/drivers/oci8/oci8_forge.php
+++ b/system/database/drivers/oci8/oci8_forge.php
@@ -73,11 +73,22 @@
 			{
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
 
-				$sql .= "\n\t".$this->db->protect_identifiers($field).' '.$attributes['TYPE']
-					.((isset($attributes['UNSINGED']) && $attributes['UNSIGNED'] === TRUE) ? ' UNSIGNED' : '')
-					.(isset($attributes['DEFAULT']) ? " DEFAULT '".$attributes['DEFAULT']."'" : '')
-					.((isset($attributes['NULL']) && $attributes['NULL'] === TRUE) ? '' : ' NOT NULL')
-					.(isset($attributes['CONSTRAINT']) ? ' CONSTRAINT '.$attributes['CONSTRAINT'] : '');
+				$sql .= "\n\t".$this->db->escape_identifiers($field).' '.$attributes['TYPE'];
+
+				if (isset($attributes['UNSINGED']) && $attributes['UNSIGNED'] === TRUE)
+				{
+					$sql .= ' UNSIGNED';
+				}
+
+				if (isset($attributes['DEFAULT']))
+				{
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
+				}
+
+				$sql .= (isset($attributes['NULL']) && $attributes['NULL'] === TRUE)
+					? '' : ' NOT NULL';
+
+				empty($attributes['CONSTRAINT']) OR ' CONSTRAINT '.$attributes['CONSTRAINT'];
 			}
 
 			// don't add a comma on the end of the last field
@@ -89,22 +100,16 @@
 
 		if (count($primary_keys) > 0)
 		{
-			$primary_keys = $this->db->protect_identifiers($primary_keys);
-			$sql .= ",\n\tCONSTRAINT ".$table.' PRIMARY KEY ('.implode(', ', $primary_keys).')';
+			$sql .= ",\n\tCONSTRAINT ".$table.' PRIMARY KEY ('.implode(', ', $this->db->escape_identifiers($primary_keys)).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
 		{
 			foreach ($keys as $key)
 			{
-				if (is_array($key))
-				{
-					$key = $this->db->protect_identifiers($key);
-				}
-				else
-				{
-					$key = array($this->db->protect_identifiers($key));
-				}
+				$key = is_array($key)
+					? $this->db->escape_identifiers($key)
+					: array($this->db->escape_identifiers($key));
 
 				$sql .= ",\n\tUNIQUE COLUMNS (".implode(', ', $key).')';
 			}
@@ -132,7 +137,7 @@
 	 */
 	protected function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
 	{
-		$sql = 'ALTER TABLE '.$this->db->protect_identifiers($table).' '.$alter_type.' '.$this->db->protect_identifiers($column_name);
+		$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' '.$this->db->escape_identifiers($column_name);
 
 		// DROP has everything it needs now.
 		if ($alter_type === 'DROP')
@@ -141,9 +146,9 @@
 		}
 
 		return $sql.' '.$column_definition
-			.($default_value != '' ? ' DEFAULT "'.$default_value.'"' : '')
+			.($default_value !== '' ? ' DEFAULT "'.$default_value.'"' : '')
 			.($null === NULL ? ' NULL' : ' NOT NULL')
-			.($after_field != '' ? ' AFTER '.$this->db->protect_identifiers($after_field) : '');
+			.($after_field !== '' ? ' AFTER '.$this->db->escape_identifiers($after_field) : '');
 
 	}
 
diff --git a/system/database/drivers/oci8/oci8_result.php b/system/database/drivers/oci8/oci8_result.php
index 6fb6c81..a2b600e 100644
--- a/system/database/drivers/oci8/oci8_result.php
+++ b/system/database/drivers/oci8/oci8_result.php
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.4.1
  */
 class CI_DB_oci8_result extends CI_DB_result {
 
@@ -41,14 +42,16 @@
 	public $limit_used;
 	public $commit_mode;
 
-	/* Overwriting the parent here, so we have a way to know if it's
-	 * already called or not:
+	/**
+	 * Constructor
+	 *
+	 * @param	object
+	 * @return	void
 	 */
-	public $num_rows;
-
 	public function __construct(&$driver_object)
 	{
 		parent::__construct($driver_object);
+
 		$this->stmt_id = $driver_object->stmt_id;
 		$this->curs_id = $driver_object->curs_id;
 		$this->limit_used = $driver_object->limit_used;
@@ -56,33 +59,6 @@
 		$driver_object->stmt_id = FALSE;
 	}
 
-	/**
-	 * Number of rows in the result set.
-	 *
-	 * Oracle doesn't have a graceful way to return the number of rows
-	 * so we have to use what amounts to a hack.
-	 *
-	 * @return	int
-	 */
-	public function num_rows()
-	{
-		if ( ! is_int($this->num_rows))
-		{
-			if (count($this->result_array) > 0)
-			{
-				return $this->num_rows = count($this->result_array);
-			}
-			elseif (count($this->result_object) > 0)
-			{
-				return $this->num_rows = count($this->result_object);
-			}
-
-			return $this->num_rows = count($this->result_array());
-		}
-
-		return $this->num_rows;
-	}
-
 	// --------------------------------------------------------------------
 
 	/**
@@ -191,450 +167,27 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		$id = ($this->curs_id) ? $this->curs_id : $this->stmt_id;
-		return oci_fetch_object($id);
-	}
+		$row = ($this->curs_id)
+			? oci_fetch_object($this->curs_id)
+			: oci_fetch_object($this->stmt_id);
 
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. Array version.
-	 *
-	 * @return	array
-	 */
-	public function result_array()
-	{
-		if (count($this->result_array) > 0)
+		if ($class_name === 'stdClass' OR ! $row)
 		{
-			return $this->result_array;
-		}
-		elseif (count($this->result_object) > 0)
-		{
-			for ($i = 0, $c = count($this->result_object); $i < $c; $i++)
-			{
-				$this->result_array[$i] = (array) $this->result_object[$i];
-			}
-
-			return $this->result_array;
-		}
-		elseif (is_array($this->row_data))
-		{
-			if (count($this->row_data) === 0)
-			{
-				return $this->result_array;
-			}
-			else
-			{
-				$row_index = count($this->row_data);
-			}
-		}
-		else
-		{
-			$row_index = 0;
-			$this->row_data = array();
+			return $row;
 		}
 
-		$row = NULL;
-		while ($row = $this->_fetch_assoc())
-		{
-			$this->row_data[$row_index++] = $row;
-		}
-
-		return $this->result_array = $this->row_data;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. "object" version.
-	 *
-	 * @return	array
-	 */
-	public function result_object()
-	{
-		if (count($this->result_object) > 0)
-		{
-			return $this->result_object;
-		}
-		elseif (count($this->result_array) > 0)
-		{
-			for ($i = 0, $c = count($this->result_array); $i < $c; $i++)
-			{
-				$this->result_object[] = (object) $this->result_array[$i];
-			}
-
-			return $this->result_object;
-		}
-		elseif (is_array($this->row_data))
-		{
-			if (count($this->row_data) === 0)
-			{
-				return $this->result_object;
-			}
-			else
-			{
-				$row_index = count($this->row_data);
-				for ($i = 0; $i < $row_index; $i++)
-				{
-					$this->result_object[$i] = (object) $this->row_data[$i];
-				}
-			}
-		}
-		else
-		{
-			$row_index = 0;
-			$this->row_data = array();
-		}
-
-		$row = NULL;
-		while ($row = $this->_fetch_object())
-		{
-			$this->row_data[$row_index] = (array) $row;
-			$this->result_object[$row_index++] = $row;
-		}
-
-		return $this->result_object;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. Custom object version.
-	 *
-	 * @param	string	class name used to instantiate rows to
-	 * @return	array
-	 */
-	public function custom_result_object($class_name)
-	{
-		if (isset($this->custom_result_object[$class_name]))
-		{
-			return $this->custom_result_object[$class_name];
-		}
-
-		if ( ! class_exists($class_name) OR $this->result_id === FALSE OR $this->num_rows() === 0)
-		{
-			return array();
-		}
-
-		/* Even if we didn't have result_array or result_object
-		 * set prior to custom_result_object() being called,
-		 * num_rows() has already done so.
-		 * Pass by reference, as we don't know how
-		 * large it might be and we don't want 1000 row
-		 * sets being copied.
-		 */
-		if (count($this->result_array) > 0)
-		{
-			$data = &$this->result_array;
-		}
-		elseif (count($this->result_object) > 0)
-		{
-			$data = &$this->result_object;
-		}
-
-		$this->custom_result_object[$class_name] = array();
-		for ($i = 0, $c = count($data); $i < $c; $i++)
-		{
-			$this->custom_result_object[$class_name][$i] = new $class_name();
-			foreach ($data[$i] as $key => $value)
-			{
-				$this->custom_result_object[$class_name][$i]->$key = $value;
-			}
-		}
-
-		return $this->custom_result_object[$class_name];
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result.
-	 *
-	 * Acts as a wrapper for row_object(), row_array()
-	 * and custom_row_object(). Also used by first_row(), next_row()
-	 * and previous_row().
-	 *
-	 * @param	int	row index
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function row($n = 0, $type = 'object')
-	{
-		if ($type === 'object')
-		{
-			return $this->row_object($n);
-		}
-		elseif ($type === 'array')
-		{
-			return $this->row_array($n);
-		}
-
-		return $this->custom_row_object($n, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Array version.
-	 *
-	 * @param	int	row index
-	 * @return	array
-	 */
-	public function row_array($n = 0)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
-		{
-			$n = (int) $n;
-		}
-
-		/* If row_data is initialized, it means that we've already tried
-		 * (at least) to fetch some data, so ... check if we already have
-		 * this row.
-		*/
-		if (is_array($this->row_data))
-		{
-			/* If we already have row_data[$n] - return it.
-			 *
-			 * If we enter the elseif, there's a number of reasons to
-			 * return an empty array:
-			 *
-			 *	- count($this->row_data) === 0 means there are no results
-			 *	- num_rows being set, result_array and/or result_object
-			 *	  having count() > 0 means that we've already fetched all
-			 *	  data and $n is greater than our highest row index available
-			 *	- $n < $this->current_row means that if such row existed,
-			 *	  we would've already returned it, therefore $n is an
-			 *	  invalid index
-			 */
-			if (isset($this->row_data[$n])) // We already have this row
-			{
-				$this->current_row = $n;
-				return $this->row_data[$n];
-			}
-			elseif (count($this->row_data) === 0 OR is_int($this->num_rows)
-				OR count($this->result_array) > 0 OR count($this->result_object) > 0
-				OR $n < $this->current_row)
-			{
-				// No such row exists
-				return NULL;
-			}
-
-			// Get the next row index that would actually need to be fetched
-			$current_row = ($this->current_row < count($this->row_data)) ? count($this->row_data) : $this->current_row + 1;
-		}
-		else
-		{
-			$current_row = $this->current_row = 0;
-			$this->row_data = array();
-		}
-
-		/* Fetch more data, if available
-		 *
-		 * NOTE: Operator precedence is important here, if you change
-		 *	 'AND' with '&&' - it WILL BREAK the results, as
-		 *	 $row will be assigned the scalar value of both
-		 *	 expressions!
-		 */
-		while ($row = $this->_fetch_assoc() AND $current_row <= $n)
-		{
-			$this->row_data[$current_row++] = $row;
-		}
-
-		// This would mean that there's no (more) data to fetch
-		if ( ! is_array($this->row_data) OR ! isset($this->row_data[$n]))
-		{
-			// Cache what we already have
-			if (is_array($this->row_data))
-			{
-				$this->num_rows = count($this->row_data);
-				/* Usually, row_data could have less elements than result_array,
-				 * but at this point - they should be exactly the same.
-				 */
-				$this->result_array = $this->row_data;
-			}
-			else
-			{
-				$this->num_rows = 0;
-			}
-
-			return NULL;
-		}
-
-		$this->current_row = $n;
-		return $this->row_data[$n];
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Object version.
-	 *
-	 * @param	int	row index
-	 * @return	mixed	object if row found; empty array if not
-	 */
-	public function row_object($n = 0)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
-		{
-			$n = (int) $n;
-		}
-		/* Logic here is exactly the same as in row_array,
-		 * except we have to cast row_data[$n] to an object.
-		 *
-		 * If we already have result_object though - we can
-		 * directly return from it.
-		 */
-		if (isset($this->result_object[$n]))
-		{
-			$this->current_row = $n;
-			// Set this, if not already done.
-			if ( ! is_int($this->num_rows))
-			{
-				$this->num_rows = count($this->result_object);
-			}
-
-			return $this->result_object[$n];
-		}
-
-		$row = $this->row_array($n);
-		// Cast only if the row exists
-		if (count($row) > 0)
-		{
-			$this->current_row = $n;
-			return (object) $row;
-		}
-
-		return NULL;
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Custom object version.
-	 *
-	 * @param	int	row index
-	 * @param	string	custom class name
-	 * @return	mixed	custom object if row found; empty array otherwise
-	 */
-	public function custom_row_object($n = 0, $class_name)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
-		{
-			$n = (int) $n;
-		}
-
-		if (array_key_exists($class_name, $this->custom_result_object))
-		{
-			/* We already have a the whole result set with this class_name,
-			 * return the specified row if it exists, and an empty array if
-			 * it doesn't.
-			 */
-			if (isset($this->custom_result_object[$class_name][$n]))
-			{
-				$this->current_row = $n;
-				return $this->custom_result_object[$class_name][$n];
-			}
-			else
-			{
-				return NULL;
-			}
-		}
-		elseif ( ! class_exists($class_name)) // No such class exists
-		{
-			return NULL;
-		}
-
-		$row = $this->row_array($n);
-		// A non-array would mean that the row doesn't exist
-		if ( ! is_array($row))
-		{
-			return NULL;
-		}
-
-		// Convert to the desired class and return
-		$row_object = new $class_name();
+		$class_name = new $class_name();
 		foreach ($row as $key => $value)
 		{
-			$row_object->$key = $value;
+			$class_name->$key = $value;
 		}
 
-		$this->current_row = $n;
-		return $row_object;
-	}
-
-	// --------------------------------------------------------------------
-
-	/* First row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function first_row($type = 'object')
-	{
-		return $this->row(0, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Last row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function last_row($type = 'object')
-	{
-		$result = &$this->result($type);
-		if ( ! isset($this->num_rows))
-		{
-			$this->num_rows = count($result);
-		}
-		$this->current_row = $this->num_rows - 1;
-		return $result[$this->current_row];
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Next row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function next_row($type = 'object')
-	{
-		if (is_array($this->row_data))
-		{
-			$count = count($this->row_data);
-			if ($this->current_row > $count OR ($this->current_row === 0 && $count === 0))
-			{
-				$n = $count;
-			}
-			else
-			{
-				$n = $this->current_row + 1;
-			}
-		}
-		else
-		{
-			$n = 0;
-		}
-
-		return $this->row($n, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Previous row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function previous_row($type = 'object')
-	{
-		$n = ($this->current_row !== 0) ? $this->current_row - 1 : 0;
-		return $this->row($n, $type);
+		return $class_name;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php
index 98fd806..8f0a474 100644
--- a/system/database/drivers/odbc/odbc_driver.php
+++ b/system/database/drivers/odbc/odbc_driver.php
@@ -49,12 +49,6 @@
 	protected $_like_escape_str = " {escape '%s'} ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword;
 
 	public function __construct($params)
@@ -64,7 +58,7 @@
 		$this->_random_keyword = ' RND('.time().')'; // database specific random keyword
 
 		// Legacy support for DSN in the hostname field
-		if ($this->dsn == '')
+		if (empty($this->dsn))
 		{
 			$this->dsn = $this->hostname;
 		}
@@ -114,13 +108,8 @@
 	 */
 	public function trans_begin($test_mode = FALSE)
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
@@ -128,7 +117,7 @@
 		// Reset the transaction failure flag.
 		// If the $test_mode flag is set to TRUE transactions will be rolled back
 		// even if the queries produce a successful result.
-		$this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
+		$this->_trans_failure = ($test_mode === TRUE);
 
 		return odbc_autocommit($this->conn_id, FALSE);
 	}
@@ -142,13 +131,8 @@
 	 */
 	public function trans_commit()
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
@@ -167,13 +151,8 @@
 	 */
 	public function trans_rollback()
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
@@ -204,7 +183,6 @@
 			return $str;
 		}
 
-		// ODBC doesn't require escaping
 		$str = remove_invisible_characters($str);
 
 		// escape LIKE condition wildcards
@@ -254,9 +232,9 @@
 	 */
 	protected function _list_tables($prefix_limit = FALSE)
 	{
-		$sql = "SHOW TABLES FROM `".$this->database."`";
+		$sql = 'SHOW TABLES FROM '.$this->database;
 
-		if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+		if ($prefix_limit !== FALSE && $this->dbprefix !== '')
 		{
 			//$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
 			return FALSE; // not currently supported
@@ -277,7 +255,7 @@
 	 */
 	protected function _list_columns($table = '')
 	{
-		return "SHOW COLUMNS FROM ".$table;
+		return 'SHOW COLUMNS FROM '.$table;
 	}
 
 	// --------------------------------------------------------------------
@@ -292,7 +270,7 @@
 	 */
 	protected function _field_data($table)
 	{
-		return "SELECT TOP 1 FROM ".$table;
+		return 'SELECT TOP 1 FROM '.$table;
 	}
 
 	// --------------------------------------------------------------------
@@ -323,12 +301,7 @@
 	 */
 	protected function _from_tables($tables)
 	{
-		if ( ! is_array($tables))
-		{
-			$tables = array($tables);
-		}
-
-		return '('.implode(', ', $tables).')';
+		return is_array($tables) ? implode(', ', $tables) : $tables;
 	}
 
 	// --------------------------------------------------------------------
@@ -352,24 +325,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Limit string
-	 *
-	 * Generates a platform-specific LIMIT clause
-	 *
-	 * @param	string	the sql query string
-	 * @param	int	the number of rows to limit the query to
-	 * @param	int	the offset value
-	 * @return	string
-	 */
-	protected function _limit($sql, $limit, $offset)
-	{
-		// Does ODBC doesn't use the LIMIT clause?
-		return $sql;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Close DB Connection
 	 *
 	 * @return	void
diff --git a/system/database/drivers/odbc/odbc_forge.php b/system/database/drivers/odbc/odbc_forge.php
index d59b8a9..d17b046 100644
--- a/system/database/drivers/odbc/odbc_forge.php
+++ b/system/database/drivers/odbc/odbc_forge.php
@@ -34,7 +34,6 @@
  */
 class CI_DB_odbc_forge extends CI_DB_forge {
 
-	protected $_drop_database	= 'DROP DATABASE %s';
 	protected $_drop_table		= 'DROP TABLE %s';
 
 	/**
@@ -66,41 +65,30 @@
 			// entered the field information, so we'll simply add it to the list
 			if (is_numeric($field))
 			{
-				$sql .= "\n\t$attributes";
+				$sql .= "\n\t".$attributes;
 			}
 			else
 			{
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
 
-				$sql .= "\n\t".$this->db->protect_identifiers($field);
+				$sql .= "\n\t".$this->db->escape_identifiers($field).' '.$attributes['TYPE'];
 
-				$sql .=  ' '.$attributes['TYPE'];
+				empty($attributes['CONSTRAINT']) OR $sql .= '('.$attributes['CONSTRAINT'].')';
 
-				if (array_key_exists('CONSTRAINT', $attributes))
-				{
-					$sql .= '('.$attributes['CONSTRAINT'].')';
-				}
-
-				if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
+				if ( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE)
 				{
 					$sql .= ' UNSIGNED';
 				}
 
-				if (array_key_exists('DEFAULT', $attributes))
+				if (isset($attributes['DEFAULT']))
 				{
-					$sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
 				}
 
-				if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
-				{
-					$sql .= ' NULL';
-				}
-				else
-				{
-					$sql .= ' NOT NULL';
-				}
+				$sql .= ( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE)
+					? ' NULL' : ' NOT NULL';
 
-				if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
+				if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
 				{
 					$sql .= ' AUTO_INCREMENT';
 				}
@@ -115,30 +103,22 @@
 
 		if (count($primary_keys) > 0)
 		{
-			$primary_keys = $this->db->protect_identifiers($primary_keys);
-			$sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
+			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $this->escape_identifiers($primary_keys)).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
 		{
 			foreach ($keys as $key)
 			{
-				if (is_array($key))
-				{
-					$key = $this->db->protect_identifiers($key);
-				}
-				else
-				{
-					$key = array($this->db->protect_identifiers($key));
-				}
+				$key = is_array($key)
+					? $this->db->escape_identifiers($key)
+					: array($this->db->escape_identifiers($key));
 
-				$sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")";
+				$sql .= ",\n\tFOREIGN KEY (".implode(', ', $key).')';
 			}
 		}
 
-		$sql .= "\n)";
-
-		return $sql;
+		return $sql."\n)";
 	}
 
 	// --------------------------------------------------------------------
@@ -160,37 +140,18 @@
 	 */
 	protected function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
 	{
-		$sql = 'ALTER TABLE '.$this->db->protect_identifiers($table).' '.$alter_type.' '.$this->db->protect_identifiers($column_name);
+		$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' '.$this->db->escape_identifiers($column_name);
 
 		// DROP has everything it needs now.
-		if ($alter_type == 'DROP')
+		if ($alter_type === 'DROP')
 		{
 			return $sql;
 		}
 
-		$sql .= " $column_definition";
-
-		if ($default_value != '')
-		{
-			$sql .= " DEFAULT \"$default_value\"";
-		}
-
-		if ($null === NULL)
-		{
-			$sql .= ' NULL';
-		}
-		else
-		{
-			$sql .= ' NOT NULL';
-		}
-
-		if ($after_field != '')
-		{
-			return $sql.' AFTER '.$this->db->protect_identifiers($after_field);
-		}
-
-		return $sql;
-
+		return $sql.' '.$column_definition
+			.($default_value != '' ? ' DEFAULT "'.$default_value.'"' : '')
+			.($null === NULL ? ' NULL' : ' NOT NULL')
+			.($after_field != '' ? ' AFTER '.$this->db->escape_identifiers($after_field) : '');
 	}
 
 }
diff --git a/system/database/drivers/odbc/odbc_result.php b/system/database/drivers/odbc/odbc_result.php
index ecba597..48dc48d 100644
--- a/system/database/drivers/odbc/odbc_result.php
+++ b/system/database/drivers/odbc/odbc_result.php
@@ -33,11 +33,10 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.3
  */
 class CI_DB_odbc_result extends CI_DB_result {
 
-	public $num_rows;
-
 	/**
 	 * Number of rows in the result set
 	 *
@@ -49,14 +48,22 @@
 		{
 			return $this->num_rows;
 		}
-
-		// Work-around for ODBC subdrivers that don't support num_rows()
-		if (($this->num_rows = @odbc_num_rows($this->result_id)) === -1)
+		elseif (($this->num_rows = @odbc_num_rows($this->result_id)) !== -1)
 		{
-			$this->num_rows = count($this->result_array());
+			return $this->num_rows;
 		}
 
-		return $this->num_rows;
+		// Work-around for ODBC subdrivers that don't support num_rows()
+		if (count($this->result_array) > 0)
+		{
+			return $this->num_rows = count($this->result_array);
+		}
+		elseif (count($this->result_object) > 0)
+		{
+			return $this->num_rows = count($this->result_object);
+		}
+
+		return $this->num_rows = count($this->result_array());
 	}
 
 	// --------------------------------------------------------------------
@@ -148,14 +155,7 @@
 	 */
 	protected function _fetch_assoc()
 	{
-		if (function_exists('odbc_fetch_array'))
-		{
-			return odbc_fetch_array($this->result_id);
-		}
-		else
-		{
-			return $this->_odbc_fetch_array($this->result_id);
-		}
+		return odbc_fetch_array($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -165,141 +165,93 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		if (function_exists('odbc_fetch_object'))
+		$row = odbc_fetch_object($this->result_id);
+
+		if ($class_name === 'stdClass' OR ! $row)
 		{
-			return odbc_fetch_object($this->result_id);
+			return $row;
 		}
-		else
+
+		$class_name = new $class_name();
+		foreach ($row as $key => $value)
 		{
-			return $this->_odbc_fetch_object($this->result_id);
+			$class_name->$key = $value;
 		}
+
+		return $class_name;
 	}
 
-	// --------------------------------------------------------------------
+}
 
+// --------------------------------------------------------------------
+
+if ( ! function_exists('odbc_fetch_array'))
+{
 	/**
-	 * Result - object
+	 * ODBC Fetch array
 	 *
-	 * subsititutes the odbc_fetch_object function when
-	 * not available (odbc_fetch_object requires unixODBC)
+	 * Emulates the native odbc_fetch_array() function when
+	 * it is not available (odbc_fetch_array() requires unixODBC)
 	 *
-	 * @return	object
-	 */
-	protected function _odbc_fetch_object(& $odbc_result)
-	{
-		$rs = array();
-		$rs_obj = FALSE;
-		if (odbc_fetch_into($odbc_result, $rs))
-		{
-			foreach ($rs as $k => $v)
-			{
-				$field_name = odbc_field_name($odbc_result, $k+1);
-				$rs_obj->$field_name = $v;
-			}
-		}
-		return $rs_obj;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Result - array
-	 *
-	 * subsititutes the odbc_fetch_array function when
-	 * not available (odbc_fetch_array requires unixODBC)
-	 *
+	 * @param	resource
+	 * @param	int
 	 * @return	array
 	 */
-	protected function _odbc_fetch_array(& $odbc_result)
+	function odbc_fetch_array(& $result, $rownumber = 1)
 	{
 		$rs = array();
-		$rs_assoc = FALSE;
-		if (odbc_fetch_into($odbc_result, $rs))
+		if ( ! odbc_fetch_into($result, $rs, $rownumber))
 		{
-			$rs_assoc = array();
-			foreach ($rs as $k => $v)
-			{
-				$field_name = odbc_field_name($odbc_result, $k+1);
-				$rs_assoc[$field_name] = $v;
-			}
+			return FALSE;
 		}
+
+		$rs_assoc = array();
+		foreach ($rs as $k => $v)
+		{
+			$field_name = odbc_field_name($result, $k+1);
+			$rs_assoc[$field_name] = $v;
+		}
+
 		return $rs_assoc;
 	}
+}
 
-	// --------------------------------------------------------------------
+// --------------------------------------------------------------------
 
+if ( ! function_exists('odbc_fetch_object'))
+{
 	/**
-	 * Query result. Array version.
+	 * ODBC Fetch object
 	 *
-	 * @return	array
-	 */
-	public function result_array()
-	{
-		if (count($this->result_array) > 0)
-		{
-			return $this->result_array;
-		}
-		elseif (($c = count($this->result_object)) > 0)
-		{
-			for ($i = 0; $i < $c; $i++)
-			{
-				$this->result_array[$i] = (array) $this->result_object[$i];
-			}
-		}
-		elseif ($this->result_id === FALSE)
-		{
-			return array();
-		}
-		else
-		{
-			while ($row = $this->_fetch_assoc())
-			{
-				$this->result_array[] = $row;
-			}
-		}
-
-		return $this->result_array;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. Object version.
+	 * Emulates the native odbc_fetch_object() function when
+	 * it is not available.
 	 *
-	 * @return	array
+	 * @param	resource
+	 * @param	int
+	 * @return	object
 	 */
-	public function result_object()
+	function odbc_fetch_object(& $result, $rownumber = 1)
 	{
-		if (count($this->result_object) > 0)
+		$rs = array();
+		if ( ! odbc_fetch_into($result, $rs, $rownumber))
 		{
-			return $this->result_object;
-		}
-		elseif (($c = count($this->result_array)) > 0)
-		{
-			for ($i = 0; $i < $c; $i++)
-			{
-				$this->result_object[$i] = (object) $this->result_array[$i];
-			}
-		}
-		elseif ($this->result_id === FALSE)
-		{
-			return array();
-		}
-		else
-		{
-			while ($row = $this->_fetch_object())
-			{
-				$this->result_object[] = $row;
-			}
+			return FALSE;
 		}
 
-		return $this->result_object;
+		$rs_object = new stdClass();
+		foreach ($rs as $k => $v)
+		{
+			$field_name = odbc_field_name($result, $k+1);
+			$rs_object->$field_name = $v;
+		}
+
+		return $rs_object;
 	}
-
 }
 
 /* End of file odbc_result.php */
diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php
index ec7f3e1..b36a3d9 100644
--- a/system/database/drivers/pdo/pdo_driver.php
+++ b/system/database/drivers/pdo/pdo_driver.php
@@ -42,145 +42,66 @@
 
 	public $dbdriver = 'pdo';
 
-	// the character used to excape - not necessary for PDO
-	protected $_escape_char = '';
+	// The character used to escaping
+	protected $_escape_char = '"';
 
 	// clause and character used for LIKE escape sequences
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword;
 
-	// need to track the pdo driver and options
-	public $pdodriver;
+	public $trans_enabled = FALSE;
+
+	// need to track the PDO options
 	public $options = array();
 
-	public function __construct($params)
-	{
-		parent::__construct($params);
-
-		if (preg_match('/([^;]+):/', $this->dsn, $match) && count($match) == 2)
-		{
-			// If there is a minimum valid dsn string pattern found, we're done
-			// This is for general PDO users, who tend to have a full DSN string.
-			$this->pdodriver = end($match);
-		}
-		else
-		{
-			// Try to build a complete DSN string from params
-			$this->_connect_string($params);
-		}
-
-		// clause and character used for LIKE escape sequences
-		// this one depends on the driver being used
-		if ($this->pdodriver === 'mysql')
-		{
-			$this->_escape_char = '`';
-			$this->_like_escape_str = '';
-			$this->_like_escape_chr = '\\';
-		}
-		elseif ($this->pdodriver === 'odbc')
-		{
-			$this->_like_escape_str = " {escape '%s'} ";
-		}
-		elseif ( ! in_array($this->pdodriver, array('sqlsrv', 'mssql', 'dblib', 'sybase')))
-		{
-			$this->_escape_char = '"';
-		}
-
-		$this->trans_enabled = FALSE;
-		$this->_random_keyword = ' RND('.time().')'; // database specific random keyword
-	}
-
 	/**
-	 * Connection String
+	 * Constructor
+	 *
+	 * Validates the DSN string and/or detects the subdriver
 	 *
 	 * @param	array
 	 * @return	void
 	 */
-	protected function _connect_string($params)
+	public function __construct($params)
 	{
-		if (strpos($this->hostname, ':'))
-		{
-			// hostname generally would have this prototype
-			// $db['hostname'] = 'pdodriver:host(/Server(/DSN))=hostname(/DSN);';
-			// We need to get the prefix (pdodriver used by PDO).
-			$dsnarray = explode(':', $this->hostname);
-			$this->pdodriver = $dsnarray[0];
+		parent::__construct($params);
 
-			// End dsn with a semicolon for extra backward compability
-			// if database property was not empty.
-			if ( ! empty($this->database))
-			{
-				$this->dsn .= rtrim($this->hostname, ';').';';
-			}
+		if (preg_match('/([^;]+):/', $this->dsn, $match) && count($match) === 2)
+		{
+			// If there is a minimum valid dsn string pattern found, we're done
+			// This is for general PDO users, who tend to have a full DSN string.
+			$this->subdriver = $match[1];
+			return;
 		}
-		else
+		// Legacy support for DSN specified in the hostname field
+		elseif (preg_match('/([^;]+):/', $this->hostname, $match) && count($match) === 2)
 		{
-			// Invalid DSN, display an error
-			if ( ! array_key_exists('pdodriver', $params))
-			{
-				show_error('Invalid DB Connection String for PDO');
-			}
+			$this->dsn = $this->hostname;
+			$this->hostname = NULL;
+			$this->subdriver = $match[1];
+			return;
+		}
+		elseif (in_array($this->subdriver, array('mssql', 'sybase'), TRUE))
+		{
+			$this->subdriver = 'dblib';
+		}
+		elseif ($this->subdriver === '4D')
+		{
+			$this->subdriver = '4d';
+		}
+		elseif ( ! in_array($this->subdriver, array('4d', 'cubrid', 'dblib', 'firebird', 'ibm', 'informix', 'mysql', 'oci', 'odbc', 'sqlite', 'sqlsrv'), TRUE))
+		{
+			log_message('error', 'PDO: Invalid or non-existent subdriver');
 
-			// Assuming that the following DSN string format is used:
-			// $dsn = 'pdo://username:password@hostname:port/database?pdodriver=pgsql';
-			$this->dsn = $this->pdodriver.':';
-
-			// Add hostname to the DSN for databases that need it
-			if ( ! empty($this->hostname)
-				&& strpos($this->hostname, ':') === FALSE
-				&& in_array($this->pdodriver, array('informix', 'mysql', 'pgsql', 'sybase', 'mssql', 'dblib', 'cubrid')))
+			if ($this->db_debug)
 			{
-			    $this->dsn .= 'host='.$this->hostname.';';
-			}
-
-			// Add a port to the DSN for databases that can use it
-			if ( ! empty($this->port) && in_array($this->pdodriver, array('informix', 'mysql', 'pgsql', 'ibm', 'cubrid')))
-			{
-			    $this->dsn .= 'port='.$this->port.';';
+				show_error('Invalid or non-existent PDO subdriver');
 			}
 		}
 
-		// Add the database name to the DSN, if needed
-	    if (stripos($this->dsn, 'dbname') === FALSE
-	       && in_array($this->pdodriver, array('4D', 'pgsql', 'mysql', 'firebird', 'sybase', 'mssql', 'dblib', 'cubrid')))
-	    {
-	        $this->dsn .= 'dbname='.$this->database.';';
-	    }
-	    elseif (stripos($this->dsn, 'database') === FALSE && in_array($this->pdodriver, array('ibm', 'sqlsrv')))
-	    {
-	    	if (stripos($this->dsn, 'dsn') === FALSE)
-	    	{
-		        $this->dsn .= 'database='.$this->database.';';
-	    	}
-	    }
-	    elseif ($this->pdodriver === 'sqlite' && $this->dsn === 'sqlite:')
-	    {
-	        if ($this->database !== ':memory')
-	        {
-	            if ( ! file_exists($this->database))
-	            {
-	                show_error('Invalid DB Connection string for PDO SQLite');
-	            }
-
-	            $this->dsn .= (strpos($this->database, DIRECTORY_SEPARATOR) !== 0) ? DIRECTORY_SEPARATOR : '';
-	        }
-
-	        $this->dsn .= $this->database;
-	    }
-
-	    // Add charset to the DSN, if needed
-	    if ( ! empty($this->char_set) && in_array($this->pdodriver, array('4D', 'mysql', 'sybase', 'mssql', 'dblib', 'oci')))
-	    {
-	        $this->dsn .= 'charset='.$this->char_set.';';
-	    }
+		$this->dsn = NULL;
 	}
 
 	// --------------------------------------------------------------------
@@ -188,53 +109,17 @@
 	/**
 	 * Non-persistent database connection
 	 *
-	 * @return	object
-	 */
-	public function db_connect()
-	{
-		return $this->_pdo_connect();
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Persistent database connection
-	 *
-	 * @return	object
-	 */
-	public function db_pconnect()
-	{
-		return $this->_pdo_connect(TRUE);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * PDO connection
-	 *
 	 * @param	bool
 	 * @return	object
 	 */
-	protected function _pdo_connect($persistent = FALSE)
+	public function db_connect($persistent = FALSE)
 	{
-		$this->options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_SILENT;
-		$persistent === FALSE OR $this->options[PDO::ATTR_PERSISTENT] = TRUE;
-
-		/* Prior to PHP 5.3.6, even if the charset was supplied in the DSN
-		 * on connect - it was ignored. This is a work-around for the issue.
-		 *
-		 * Reference: http://www.php.net/manual/en/ref.pdo-mysql.connection.php
-		 */
-		if ($this->pdodriver === 'mysql' && ! is_php('5.3.6') && ! empty($this->char_set))
-		{
-			$this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$this->char_set
-					.( ! empty($this->db_collat) ? " COLLATE '".$this->dbcollat."'" : '');
-		}
+		$this->options[PDO::ATTR_PERSISTENT] = $persistent;
 
 		// Connecting...
 		try
 		{
-			return new PDO($this->dsn, $this->username, $this->password, $this->options);
+			return @new PDO($this->dsn, $this->username, $this->password, $this->options);
 		}
 		catch (PDOException $e)
 		{
@@ -250,15 +135,38 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Persistent database connection
+	 *
+	 * @return	object
+	 */
+	public function db_pconnect()
+	{
+		return $this->db_connect(TRUE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Database version number
 	 *
 	 * @return	string
 	 */
 	public function version()
 	{
-		return isset($this->data_cache['version'])
-			? $this->data_cache['version']
-			: $this->data_cache['version'] = $this->conn_id->getAttribute(PDO::ATTR_SERVER_VERSION);
+		if (isset($this->data_cache['version']))
+		{
+			return $this->data_cache['version'];
+		}
+
+		// Not all subdrivers support the getAttribute() method
+		try
+		{
+			return $this->data_cache['version'] = $this->conn_id->getAttribute(PDO::ATTR_SERVER_VERSION);
+		}
+		catch (PDOException $e)
+		{
+			return parent::version();
+		}
 	}
 
 	// --------------------------------------------------------------------
@@ -358,7 +266,7 @@
 		$str = $this->conn_id->quote($str);
 
 		// If there are duplicated quotes, trim them away
-		if (strpos($str, "'") === 0)
+		if ($str[0] === "'")
 		{
 			$str = substr($str, 1, -1);
 		}
@@ -396,69 +304,12 @@
 	 */
 	public function insert_id($name = NULL)
 	{
-		if ($this->pdodriver === 'pgsql' && $name === NULL && $this->version() >= '8.1')
-		{
-			$query = $this->query('SELECT LASTVAL() AS ins_id');
-			$query = $query->row();
-			return $query->ins_id;
-		}
-
 		return $this->conn_id->lastInsertId($name);
 	}
 
 	// --------------------------------------------------------------------
 
 	/**
-	 * Show table query
-	 *
-	 * Generates a platform-specific query string so that the table names can be fetched
-	 *
-	 * @param	bool
-	 * @return	string
-	 */
-	protected function _list_tables($prefix_limit = FALSE)
-	{
-		if ($this->pdodriver == 'pgsql')
-		{
-			// Analog function to show all tables in postgre
-			$sql = "SELECT * FROM information_schema.tables WHERE table_schema = 'public'";
-		}
-		elseif ($this->pdodriver == 'sqlite')
-		{
-			// Analog function to show all tables in sqlite
-			$sql = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'";
-		}
-		else
-		{
-			$sql = 'SHOW TABLES FROM '.$this->escape_identifiers($this->database);
-		}
-
-		if ($prefix_limit !== FALSE AND $this->dbprefix != '')
-		{
-			return FALSE;
-		}
-
-		return $sql;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Show column query
-	 *
-	 * Generates a platform-specific query string so that the column names can be fetched
-	 *
-	 * @param	string	the table name
-	 * @return	string
-	 */
-	protected function _list_columns($table = '')
-	{
-		return 'SHOW COLUMNS FROM '.$this->escape_identifiers($table);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Field data query
 	 *
 	 * Generates a platform-specific query so that the column data can be retrieved
@@ -468,23 +319,7 @@
 	 */
 	protected function _field_data($table)
 	{
-		if ($this->pdodriver == 'mysql' or $this->pdodriver == 'pgsql')
-		{
-			// Analog function for mysql and postgre
-			return 'SELECT * FROM '.$this->escape_identifiers($table).' LIMIT 1';
-		}
-		elseif ($this->pdodriver == 'oci')
-		{
-			// Analog function for oci
-			return 'SELECT * FROM '.$this->escape_identifiers($table).' WHERE ROWNUM <= 1';
-		}
-		elseif ($this->pdodriver == 'sqlite')
-		{
-			// Analog function for sqlite
-			return 'PRAGMA table_info('.$this->escape_identifiers($table).')';
-		}
-
-		return 'SELECT TOP 1 FROM '.$this->escape_identifiers($table);
+		return 'SELECT TOP 1 * FROM '.$this->protect_identifiers($table);
 	}
 
 	// --------------------------------------------------------------------
@@ -519,27 +354,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		if ( ! is_array($tables))
-		{
-			$tables = array($tables);
-		}
-
-		return (count($tables) === 1) ? $tables[0] : '('.implode(', ', $tables).')';
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Update_Batch statement
 	 *
 	 * Generates a platform-specific batch update string from the supplied data
@@ -551,8 +365,8 @@
 	 */
 	protected function _update_batch($table, $values, $index, $where = NULL)
 	{
-		$ids   = array();
-		$where = ($where != '' && count($where) >=1) ? implode(" ", $where).' AND ' : '';
+		$ids = array();
+		$where = ($where !== '' && count($where) >=1) ? implode(" ", $where).' AND ' : '';
 
 		foreach ($values as $key => $val)
 		{
@@ -560,7 +374,7 @@
 
 			foreach (array_keys($val) as $field)
 			{
-				if ($field != $index)
+				if ($field !== $index)
 				{
 					$final[$field][] =  'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
 				}
@@ -603,39 +417,10 @@
 	 */
 	protected function _truncate($table)
 	{
-		return 'DELETE FROM '.$table;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Limit string
-	 *
-	 * Generates a platform-specific LIMIT clause
-	 *
-	 * @param	string	the sql query string
-	 * @param	int	the number of rows to limit the query to
-	 * @param	int	the offset value
-	 * @return	string
-	 */
-	protected function _limit($sql, $limit, $offset)
-	{
-		if ($this->pdodriver == 'cubrid' OR $this->pdodriver == 'sqlite')
-		{
-			$offset = ($offset == 0) ? '' : $offset.', ';
-
-			return $sql.'LIMIT '.$offset.$limit;
-		}
-		else
-		{
-			$sql .= 'LIMIT '.$limit;
-			$sql .= ($offset > 0) ? ' OFFSET '.$offset : '';
-
-			return $sql;
-		}
+		return 'TRUNCATE TABLE '.$table;
 	}
 
 }
 
 /* End of file pdo_driver.php */
-/* Location: ./system/database/drivers/pdo/pdo_driver.php */
\ No newline at end of file
+/* Location: ./system/database/drivers/pdo/pdo_driver.php */
diff --git a/system/database/drivers/pdo/pdo_forge.php b/system/database/drivers/pdo/pdo_forge.php
index ca8657a..34a6ee4 100644
--- a/system/database/drivers/pdo/pdo_forge.php
+++ b/system/database/drivers/pdo/pdo_forge.php
@@ -34,7 +34,6 @@
  */
 class CI_DB_pdo_forge extends CI_DB_forge {
 
-	protected $_drop_database	= 'DROP DATABASE %s';
 	protected $_drop_table		= 'DROP TABLE %s';
 
 	/**
@@ -66,46 +65,38 @@
 			// entered the field information, so we'll simply add it to the list
 			if (is_numeric($field))
 			{
-				$sql .= "\n\t$attributes";
+				$sql .= "\n\t".$attributes;
 			}
 			else
 			{
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
-				$numeric    = array('SERIAL', 'INTEGER');
+				$numeric = array('SERIAL', 'INTEGER');
 
-				$sql .= "\n\t".$this->db->protect_identifiers($field);
+				$sql .= "\n\t".$this->db->escape_identifiers($field).' '.$attributes['TYPE'];
 
-				$sql .=  ' '.$attributes['TYPE'];
-
-				if (array_key_exists('CONSTRAINT', $attributes))
+				if ( ! empty($attributes['CONSTRAINT']))
 				{
 					// Exception for Postgre numeric which not too happy with constraint within those type
-					if ( ! ($this->db->pdodriver == 'pgsql' && in_array($attributes['TYPE'], $numeric)))
+					if ( ! ($this->db->subdriver === 'pgsql' && in_array($attributes['TYPE'], $numeric)))
 					{
 						$sql .= '('.$attributes['CONSTRAINT'].')';
 					}
 				}
 
-				if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
+				if ( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE)
 				{
 					$sql .= ' UNSIGNED';
 				}
 
-				if (array_key_exists('DEFAULT', $attributes))
+				if (isset($attributes['DEFAULT']))
 				{
-					$sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
 				}
 
-				if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
-				{
-					$sql .= ' NULL';
-				}
-				else
-				{
-					$sql .= ' NOT NULL';
-				}
+				$sql .= ( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE)
+					? ' NULL' : ' NOT NULL';
 
-				if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
+				if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
 				{
 					$sql .= ' AUTO_INCREMENT';
 				}
@@ -120,30 +111,22 @@
 
 		if (count($primary_keys) > 0)
 		{
-			$primary_keys = $this->db->protect_identifiers($primary_keys);
-			$sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
+			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $this->db->escape_identifiers($primary_keys)).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
 		{
 			foreach ($keys as $key)
 			{
-				if (is_array($key))
-				{
-					$key = $this->db->protect_identifiers($key);
-				}
-				else
-				{
-					$key = array($this->db->protect_identifiers($key));
-				}
+				$key = is_array($key)
+					? $this->db->escape_identifiers($key)
+					: array($this->db->escape_identifiers($key));
 
-				$sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")";
+				$sql .= ",\n\tFOREIGN KEY (".implode(', ', $key).')';
 			}
 		}
 
-		$sql .= "\n)";
-
-		return $sql;
+		return $sql."\n)";
 	}
 
 	// --------------------------------------------------------------------
@@ -165,37 +148,18 @@
 	 */
 	protected function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
 	{
-		$sql = 'ALTER TABLE `'.$this->db->protect_identifiers($table).'` '.$alter_type.' '.$this->db->protect_identifiers($column_name);
+		$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' '.$this->db->escape_identifiers($column_name);
 
 		// DROP has everything it needs now.
-		if ($alter_type == 'DROP')
+		if ($alter_type === 'DROP')
 		{
 			return $sql;
 		}
 
-		$sql .= " $column_definition";
-
-		if ($default_value != '')
-		{
-			$sql .= " DEFAULT \"$default_value\"";
-		}
-
-		if ($null === NULL)
-		{
-			$sql .= ' NULL';
-		}
-		else
-		{
-			$sql .= ' NOT NULL';
-		}
-
-		if ($after_field != '')
-		{
-			return $sql.' AFTER '.$this->db->protect_identifiers($after_field);
-		}
-
-		return $sql;
-
+		return $sql .' '.$column_definition
+			.($default_value !== '' ? " DEFAULT '".$default_value."'" : '')
+			.($null === NULL ? ' NULL' : ' NOT NULL')
+			.($after_field !== '' ? ' AFTER '.$this->db->escape_identifiers($after_field) : '');
 	}
 
 }
diff --git a/system/database/drivers/pdo/pdo_result.php b/system/database/drivers/pdo/pdo_result.php
index 0b8937c..4444069 100644
--- a/system/database/drivers/pdo/pdo_result.php
+++ b/system/database/drivers/pdo/pdo_result.php
@@ -21,7 +21,7 @@
  * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
  * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
  * @link		http://codeigniter.com
- * @since		Version 2.1.0
+ * @since		Version 1.0
  * @filesource
  */
 
@@ -33,72 +33,35 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	2.1
  */
 class CI_DB_pdo_result extends CI_DB_result {
 
 	/**
-	 * @var bool  Hold the flag whether a result handler already fetched before
-	 */
-	protected $is_fetched = FALSE;
-
-	/**
-	 * @var mixed Hold the fetched assoc array of a result handler
-	 */
-	protected $result_assoc;
-
-	/**
 	 * Number of rows in the result set
 	 *
 	 * @return	int
 	 */
 	public function num_rows()
 	{
-		if (empty($this->result_id) OR ! is_object($this->result_id))
+		if (is_int($this->num_rows))
 		{
-			// invalid result handler
-			return 0;
+			return $this->num_rows;
 		}
-		elseif (($num_rows = $this->result_id->rowCount()) && $num_rows > 0)
+		elseif (count($this->result_array) > 0)
 		{
-			// If rowCount return something, we're done.
-			return $num_rows;
+			return $this->num_rows = count($this->result_array);
+		}
+		elseif (count($this->result_object) > 0)
+		{
+			return $this->num_rows = count($this->result_object);
+		}
+		elseif (($num_rows = $this->result_id->rowCount()) > 0)
+		{
+			return $this->num_rows = $num_rows;
 		}
 
-		// Fetch the result, instead perform another extra query
-		return ($this->is_fetched && is_array($this->result_assoc)) ? count($this->result_assoc) : count($this->result_assoc());
-	}
-
-	/**
-	 * Fetch the result handler
-	 *
-	 * @return	mixed
-	 */
-	public function result_assoc()
-	{
-		// If the result already fetched before, use that one
-		if (count($this->result_array) > 0 OR $this->is_fetched)
-		{
-			return $this->result_array();
-		}
-
-		// Define the output
-		$output = array('assoc', 'object');
-
-		// Initial value
-		$this->result_assoc = array() and $this->result_object = array();
-
-		// Fetch the result
-		while ($row = $this->_fetch_assoc())
-		{
-			$this->result_assoc[] = $row;
-			$this->result_object[] = (object) $row;
-		}
-
-		// Save this as buffer and marked the fetch flag
-		$this->result_array = $this->result_assoc;
-		$this->is_fetched = TRUE;
-
-		return $this->result_assoc;
+		return $this->num_rows = count($this->result_array());
 	}
 
 	// --------------------------------------------------------------------
@@ -124,12 +87,14 @@
 	 */
 	public function list_fields()
 	{
-		if ($this->db->db_debug)
+		$field_names = array();
+		for ($i = 0, $c = $this->num_fields(); $i < $c; $i++)
 		{
-			return $this->db->display_error('db_unsuported_feature');
+			$field_names[$i] = @$this->result_id->getColumnMeta();
+			$field_names[$i] = $field_names[$i]['name'];
 		}
 
-		return FALSE;
+		return $field_names;
 	}
 
 	// --------------------------------------------------------------------
@@ -240,11 +205,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return $this->result_id->fetch(PDO::FETCH_OBJ);
+		return $this->result_id->fetchObject($class_name);
 	}
 
 }
diff --git a/system/database/drivers/interbase/index.html b/system/database/drivers/pdo/subdrivers/index.html
similarity index 100%
copy from system/database/drivers/interbase/index.html
copy to system/database/drivers/pdo/subdrivers/index.html
diff --git a/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php
new file mode 100644
index 0000000..e287f5c
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php
@@ -0,0 +1,223 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO 4D Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_4d_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = '4d';
+
+	// The character used for escaping
+	protected $_escape_char = array('[', ']');
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = '4D:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			empty($this->port) OR $this->dsn .= ';port='.$this->port;
+			empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+		}
+		elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 3) === FALSE)
+		{
+			$this->dsn .= ';charset='.$this->char_set;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT '.$this->escape_identifiers('TABLE_NAME').' FROM '.$this->escape_identifiers('_USER_TABLES');
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			$sql .= ' WHERE '.$this->escape_identifiers('TABLE_NAME')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT '.$this->escape_identifiers('COLUMN_NAME').' FROM '.$this->escape_identifiers('_USER_COLUMNS')
+			.' WHERE '.$this->escape_identifiers('TABLE_NAME').' = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE).' LIMIT 1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause (ignored)
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		return $sql.' LIMIT '.$limit.($offset ? ' OFFSET '.$offset : '');
+	}
+
+}
+
+/* End of file pdo_4d_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_4d_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php b/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
new file mode 100644
index 0000000..05eeacf
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php
@@ -0,0 +1,189 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO CUBRID Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_cubrid_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'cubrid';
+
+	protected $_escape_char = '`';
+
+	// clause and character used for LIKE escape sequences - not used in CUBRID
+	protected $_like_escape_str = '';
+	protected $_like_escape_chr = '\\';
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'cubrid:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			empty($this->port) OR $this->dsn .= ';port='.$this->port;
+			empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SHOW TABLES';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table).' LIMIT 1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update_Batch statement
+	 *
+	 * Generates a platform-specific batch update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @return	string
+	 */
+	protected function _update_batch($table, $values, $index, $where = NULL)
+	{
+		$ids = array();
+		foreach ($values as $key => $val)
+		{
+			$ids[] = $val[$index];
+
+			foreach (array_keys($val) as $field)
+			{
+				if ($field !== $index)
+				{
+					$final[$field][] =  'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
+				}
+			}
+		}
+
+		$cases = '';
+		foreach ($final as $k => $v)
+		{
+			$cases .= $k." = CASE \n"
+				.implode("\n", $v)."\n"
+				.'ELSE '.$k.' END), ';
+		}
+
+		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2)
+			.' WHERE '.(($where !== '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
+			.$index.' IN('.implode(',', $ids).')';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _truncate($table)
+	{
+		return 'TRUNCATE '.$table;
+	}
+
+}
+
+/* End of file pdo_cubrid_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
new file mode 100644
index 0000000..7060c9e
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php
@@ -0,0 +1,265 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO DBLIB Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_dblib_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'dblib';
+
+	protected $_random_keyword = ' NEWID()';
+
+	protected $_quoted_identifier;
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = $params['subdriver'].':host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			if ( ! empty($this->port))
+			{
+				$this->dsn .= (DIRECTORY_SEPARATOR === '\\' ? ',' : ':').$this->port;
+			}
+
+			empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+			empty($this->appname) OR $this->dsn .= ';appname='.$this->appname;
+		}
+		else
+		{
+			if ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 6) === FALSE)
+			{
+				$this->dsn .= ';charset='.$this->char_set;
+			}
+
+			$this->subdriver = 'dblib';
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Non-persistent database connection
+	 *
+	 * @param	bool
+	 * @return	object
+	 */
+	public function db_connect($persistent = FALSE)
+	{
+		$this->conn_id = parent::db_connect($persistent);
+
+		if ( ! is_object($this->conn_id))
+		{
+			return $this->conn_id;
+		}
+
+		// Determine how identifiers are escaped
+		$query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');
+		$query = $query->row_array();
+		$this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];
+		$this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']');
+
+		return $this->conn_id;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		return 'SELECT '.$this->escape_identifiers('name')
+			.' FROM '.$this->escape_identifiers('sysobjects')
+			.' WHERE '.$this->escape_identifiers('type')." = 'U'";
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			$sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql.' ORDER BY '.$this->escape_identifiers('name');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT '.$this->escape_identifiers('column_name')
+				.' FROM '.$this->escape_identifiers('information_schema.columns')
+				.' WHERE '.$this->escape_identifiers('table_name').' = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return ($limit)
+			? 'WITH ci_delete AS (SELECT TOP '.$limit.' * FROM '.$table.$conditions.') DELETE FROM ci_delete'
+			: 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		$limit = $offset + $limit;
+
+		// As of SQL Server 2005 (9.0.*) ROW_NUMBER() is supported,
+		// however an ORDER BY clause is required for it to work
+		if (version_compare($this->version(), '9', '>=') && $offset && ! empty($this->qb_orderby))
+		{
+			$orderby = 'ORDER BY '.implode(', ', $this->qb_orderby);
+
+			// We have to strip the ORDER BY clause
+			$sql = trim(substr($sql, 0, strrpos($sql, 'ORDER BY '.$orderby)));
+
+			return 'SELECT '.(count($this->qb_select) === 0 ? '*' : implode(', ', $this->qb_select))." FROM (\n"
+				.preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.$orderby.') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)
+				."\n) ".$this->escape_identifiers('CI_subquery')
+				."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.((int) $offset + 1).' AND '.$limit;
+		}
+
+		return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
+	}
+
+}
+
+/* End of file pdo_dblib_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php */
diff --git a/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
new file mode 100644
index 0000000..c074a9a
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php
@@ -0,0 +1,262 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO Firebird Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_firebird_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'firebird';
+
+	/**
+	 * The syntax to count rows is slightly different across different
+	 * database engines, so this string appears in each driver and is
+	 * used for the count_all() and count_all_results() functions.
+	 */
+	protected $_random_keyword = ' RANDOM()'; // Currently not supported
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'firebird:';
+
+			if ( ! empty($this->database))
+			{
+				$this->dsn .= 'dbname='.$this->database;
+			}
+			elseif ( ! empty($this->hostname))
+			{
+				$this->dsn .= 'dbname='.$this->hostname;
+			}
+
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+			empty($this->role) OR $this->dsn .= ';role='.$this->role;
+		}
+		elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 9) === FALSE)
+		{
+			$this->dsn .= ';charset='.$this->char_set;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "RDB$RELATION_NAME" FROM "RDB$RELATIONS" WHERE "RDB$RELATION_NAME" NOT LIKE \'RDB$%\' AND "RDB$RELATION_NAME" NOT LIKE \'MON$%\'';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql.' AND "RDB$RELATION_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT "RDB$FIELD_NAME" FROM "RDB$RELATION_FIELDS" WHERE "RDB$RELATION_NAME" = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT FIRST 1 * FROM '.$this->protect_identifiers($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return 	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+	 */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr)
+			.$where
+			.(count($orderby) > 0 ? ' ORDER BY '.implode(', ', $orderby) : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+         */
+	protected function _truncate($table)
+	{
+		return 'DELETE FROM '.$table;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause (ignored)
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		return 'DELETE FROM '.$table.(count($conditions) > 0 ? ' WHERE '.implode(' AND ', $conditions) : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		// Limit clause depends on if Interbase or Firebird
+		if (stripos($this->version(), 'firebird') !== FALSE)
+		{
+			$select = 'FIRST '. (int) $limit
+				.($offset > 0 ? ' SKIP '. (int) $offset : '');
+		}
+		else
+		{
+			$select = 'ROWS '
+				.($offset > 0 ? (int) $offset.' TO '.($limit + $offset) : (int) $limit);
+		}
+
+		return preg_replace('`SELECT`i', 'SELECT '.$select, $sql);
+	}
+
+}
+
+/* End of file pdo_firebird_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php b/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php
new file mode 100644
index 0000000..832c03c
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php
@@ -0,0 +1,262 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO IBM DB2 Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_ibm_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'ibm';
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'ibm:';
+
+			// Pre-defined DSN
+			if (empty($this->hostname) && empty($this->HOSTNAME) && empty($this->port) && empty($this->PORT))
+			{
+				if (isset($this->DSN))
+				{
+					$this->dsn .= 'DSN='.$this->DSN;
+				}
+				elseif ( ! empty($this->database))
+				{
+					$this->dsn .= 'DSN='.$this->database;
+				}
+
+				return;
+			}
+
+			$this->dsn .= 'DRIVER='.(isset($this->DRIVER) ? '{'.$this->DRIVER.'}' : '{IBM DB2 ODBC DRIVER}').';';
+
+			if (isset($this->DATABASE))
+			{
+				$this->dsn .= 'DATABASE='.$this->DATABASE.';';
+			}
+			elseif ( ! empty($this->database))
+			{
+				$this->dsn .= 'DATABASE='.$this->database.';';
+			}
+
+			if (isset($this->HOSTNAME))
+			{
+				$this->dsn .= 'HOSTNAME='.$this->HOSTNAME.';';
+			}
+			else
+			{
+				$this->dsn .= 'HOSTNAME='.(empty($this->hostname) ? '127.0.0.1;' : $this->hostname.';');
+			}
+
+			if (isset($this->PORT))
+			{
+				$this->dsn .= 'PORT='.$this->port.';';
+			}
+			elseif ( ! empty($this->port))
+			{
+				$this->dsn .= ';PORT='.$this->port.';';
+			}
+
+			$this->dsn .= 'PROTOCOL='.(isset($this->PROTOCOL) ? $this->PROTOCOL.';' : 'TCPIP;');
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "tabname" FROM "syscat"."tables" WHERE "type" = \'T\'';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			$sql .= ' AND "tabname" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT "colname" FROM "syscat"."tables"
+			WHERE "syscat"."tabtype" = \'T\' AND "syscat"."tabname" = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE).' FETCH FIRST 1 ROWS ONLY';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause (ignored)
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		$sql .= ' FETCH FIRST '.($limit + $offset).' ROWS ONLY';
+
+		return ($offset)
+			? 'SELECT * FROM ('.$sql.') WHERE rownum > '.$offset
+			: $sql;
+	}
+
+}
+
+/* End of file pdo_ibm_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php b/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
new file mode 100644
index 0000000..a3efc63
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php
@@ -0,0 +1,271 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO Informix Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_informix_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'informix';
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'informix:';
+
+			// Pre-defined DSN
+			if (empty($this->hostname) && empty($this->host) && empty($this->port) && empty($this->service))
+			{
+				if (isset($this->DSN))
+				{
+					$this->dsn .= 'DSN='.$this->DSN;
+				}
+				elseif ( ! empty($this->database))
+				{
+					$this->dsn .= 'DSN='.$this->database;
+				}
+
+				return;
+			}
+
+			if (isset($this->host))
+			{
+				$this->dsn .= 'host='.$this->host;
+			}
+			else
+			{
+				$this->dsn .= 'host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+			}
+
+			if (isset($this->service))
+			{
+				$this->dsn .= '; service='.$this->service;
+			}
+			elseif ( ! empty($this->port))
+			{
+				$this->dsn .= '; service='.$this->port;
+			}
+
+			empty($this->database) OR $this->dsn .= '; database='.$this->database;
+			empty($this->server) OR $this->dsn .= '; server='.$this->server;
+
+			$this->dsn .= '; protocol='.(isset($this->protocol) ? $this->protocol : 'onsoctcp')
+				.'; EnableScrollableCursors=1';
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "tabname" FROM "systables" WHERE "tabid" > 99 AND "tabtype" = \'T\'';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			$sql .= ' AND "tabname" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT "colname" FROM "systables", "syscolumns"
+			WHERE "systables"."tabid" = "syscolumns"."tabid" AND "systables"."tabtype" = \'T\' AND "systables"."tabname" = '
+			.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT FIRST 1 * FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _truncate($table)
+	{
+		return 'TRUNCATE TABLE ONLY '.$table;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause (ignored)
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		$select = 'SELECT '.($offset ? 'SKIP '.$offset : '').'FIRST '.$limit.' ';
+		return preg_replace('/^(SELECT\s)/i', $select, $sql, 1);
+	}
+
+}
+
+/* End of file pdo_informix_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_informix_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
new file mode 100644
index 0000000..78afe24
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php
@@ -0,0 +1,217 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO MySQL Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_mysql_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'mysql';
+
+	protected $_escape_char = '`';
+
+	// clause and character used for LIKE escape sequences - not used in MySQL
+	protected $_like_escape_str = '';
+	protected $_like_escape_chr = '\\';
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'mysql:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			empty($this->port) OR $this->dsn .= ';port='.$this->port;
+			empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+		}
+		elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 6) === FALSE && is_php('5.3.6'))
+		{
+			$this->dsn .= ';charset='.$this->char_set;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Non-persistent database connection
+	 *
+	 * @param	bool
+	 * @return	object
+	 */
+	public function db_connect($persistent = FALSE)
+	{
+		/* Prior to PHP 5.3.6, even if the charset was supplied in the DSN
+		 * on connect - it was ignored. This is a work-around for the issue.
+		 *
+		 * Reference: http://www.php.net/manual/en/ref.pdo-mysql.connection.php
+		 */
+		if ( ! is_php('5.3.6') && ! empty($this->char_set))
+		{
+			$this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$this->char_set
+				.(empty($this->dbcollat) ? '' : ' COLLATE '.$this->dbcollat);
+		}
+
+		return parent::db_connect($persistent);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SHOW TABLES';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table).' LIMIT 1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update_Batch statement
+	 *
+	 * Generates a platform-specific batch update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @return	string
+	 */
+	protected function _update_batch($table, $values, $index, $where = NULL)
+	{
+		$ids = array();
+		foreach ($values as $key => $val)
+		{
+			$ids[] = $val[$index];
+
+			foreach (array_keys($val) as $field)
+			{
+				if ($field !== $index)
+				{
+					$final[$field][] =  'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
+				}
+			}
+		}
+
+		$cases = '';
+		foreach ($final as $k => $v)
+		{
+			$cases .= $k." = CASE \n"
+				.implode("\n", $v)."\n"
+				.'ELSE '.$k.' END), ';
+		}
+
+		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2)
+			.' WHERE '.(($where !== '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
+			.$index.' IN('.implode(',', $ids).')';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _truncate($table)
+	{
+		return 'TRUNCATE '.$table;
+	}
+
+}
+
+/* End of file pdo_mysql_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php b/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
new file mode 100644
index 0000000..56ec1bc
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php
@@ -0,0 +1,230 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO Oracle Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_oci_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'oci';
+
+	/**
+	 * The syntax to count rows is slightly different across different
+	 * database engines, so this string appears in each driver and is
+	 * used for the count_all() and count_all_results() functions.
+	 */
+	protected $_count_string = 'SELECT COUNT(1) AS ';
+	protected $_random_keyword = ' ASC'; // Currently not supported
+
+	protected $_reserved_identifiers = array('*', 'rownum');
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'oci:dbname=';
+
+			// Oracle has a slightly different PDO DSN format (Easy Connect),
+			// which also supports pre-defined DSNs.
+			if (empty($this->hostname) && empty($this->port))
+			{
+				$this->dsn .= $this->database;
+			}
+			else
+			{
+				$this->dsn .= '//'.(empty($this->hostname) ? '127.0.0.1' : $this->hostname)
+					.(empty($this->port) ? '' : ':'.$this->port).'/';
+
+				empty($this->database) OR $this->dsn .= $this->database;
+			}
+
+			empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
+		}
+		elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 4) === FALSE)
+		{
+			$this->dsn .= ';charset='.$this->char_set;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "TABLE_NAME" FROM "ALL_TABLES"';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql.' WHERE "TABLE_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT "COLUMN_NAME" FROM "all_tab_columns" WHERE "TABLE_NAME" = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table).' WHERE rownum = 1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Insert_batch statement
+	 *
+	 * @param	string	the table name
+	 * @param	array	the insert keys
+	 * @param	array	the insert values
+	 * @return 	string
+	 */
+	protected function _insert_batch($table, $keys, $values)
+	{
+		$keys = implode(', ', $keys);
+		$sql = "INSERT ALL\n";
+
+		for ($i = 0, $c = count($values); $i < $c; $i++)
+		{
+			$sql .= '	INTO '.$table.' ('.$keys.') VALUES '.$values[$i]."\n";
+		}
+
+		return $sql.'SELECT * FROM dual';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+		empty($limit) OR $conditions[] = 'rownum <= '.$limit;
+
+		return 'DELETE FROM '.$table.(count($conditions) > 0 ? ' WHERE '.implode(' AND ', $conditions) : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		return 'SELECT * FROM (SELECT inner_query.*, rownum rnum FROM ('.$sql.') inner_query WHERE rownum < '.($offset + $limit + 1).')'
+			.($offset ? ' WHERE rnum >= '.($offset + 1): '');
+	}
+
+}
+
+/* End of file pdo_oci_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_oci_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
new file mode 100644
index 0000000..392754f
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php
@@ -0,0 +1,268 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO ODBC Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_odbc_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'odbc';
+
+	// The character used for escaping - not used in ODBC
+	protected $_escape_char = '';
+
+	// clause and character used for LIKE escape sequences
+	protected $_like_escape_chr = '!';
+	protected $_like_escape_str = " {escape '%s'} ";
+
+	protected $_random_keyword = ' RAND()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'odbc:';
+
+			// Pre-defined DSN
+			if (empty($this->hostname) && empty($this->HOSTNAME) && empty($this->port) && empty($this->PORT))
+			{
+				if (isset($this->DSN))
+				{
+					$this->dsn .= 'DSN='.$this->DSN;
+				}
+				elseif ( ! empty($this->database))
+				{
+					$this->dsn .= 'DSN='.$this->database;
+				}
+
+				return;
+			}
+
+			// If the DSN is not pre-configured - try to build an IBM DB2 connection string
+			$this->dsn .= 'DRIVER='.(isset($this->DRIVER) ? '{'.$this->DRIVER.'}' : '{IBM DB2 ODBC DRIVER}').';';
+
+			if (isset($this->DATABASE))
+			{
+				$this->dsn .= 'DATABASE='.$this->DATABASE.';';
+			}
+			elseif ( ! empty($this->database))
+			{
+				$this->dsn .= 'DATABASE='.$this->database.';';
+			}
+
+			if (isset($this->HOSTNAME))
+			{
+				$this->dsn .= 'HOSTNAME='.$this->HOSTNAME.';';
+			}
+			else
+			{
+				$this->dsn .= 'HOSTNAME='.(empty($this->hostname) ? '127.0.0.1;' : $this->hostname.';');
+			}
+
+			if (isset($this->PORT))
+			{
+				$this->dsn .= 'PORT='.$this->port.';';
+			}
+			elseif ( ! empty($this->port))
+			{
+				$this->dsn .= ';PORT='.$this->port.';';
+			}
+
+			$this->dsn .= 'PROTOCOL='.(isset($this->PROTOCOL) ? $this->PROTOCOL.';' : 'TCPIP;');
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'";
+
+		if ($prefix_limit !== FALSE && $this->dbprefix !== '')
+		{
+			return $sql." AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+                return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT column_name FROM information_schema.columns WHERE table_name = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _truncate($table)
+	{
+		return 'DELETE FROM '.$table;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
+	}
+
+}
+
+/* End of file pdo_odbc_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
new file mode 100644
index 0000000..9a476f1
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php
@@ -0,0 +1,341 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO PostgreSQL Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_pgsql_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'pgsql';
+
+	protected $_random_keyword = ' RANDOM()';
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'pgsql:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			empty($this->port) OR $this->dsn .= ';port='.$this->port;
+			empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Insert ID
+	 *
+	 * @param	string
+	 * @return	int
+	 */
+	public function insert_id($name = NULL)
+	{
+		if ($name === NULL && version_compare($this->version(), '8.1', '>='))
+		{
+			$query = $this->query('SELECT LASTVAL() AS ins_id');
+			$query = $query->row();
+			return $query->ins_id;
+		}
+
+		return $this->conn_id->lastInsertId($name);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \'public\'';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql.' AND "table_name" LIKE \''
+				.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT "column_name" FROM "information_schema"."columns" WHERE "table_name" = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table).' LIMIT 1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update_Batch statement
+	 *
+	 * Generates a platform-specific batch update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @return	string
+	 */
+	protected function _update_batch($table, $values, $index, $where = NULL)
+	{
+		$ids = array();
+		foreach ($values as $key => $val)
+		{
+			$ids[] = $val[$index];
+
+			foreach (array_keys($val) as $field)
+			{
+				if ($field !== $index)
+				{
+					$final[$field][] =  'WHEN '.$val[$index].' THEN '.$val[$field];
+				}
+			}
+		}
+
+		$cases = '';
+		foreach ($final as $k => $v)
+		{
+			$cases .= $k.' = (CASE '.$k."\n"
+				.implode("\n", $v)."\n"
+				.'ELSE '.$k.' END), ';
+		}
+
+		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2)
+			.' WHERE '.(($where !== '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
+			.$index.' IN('.implode(',', $ids).')';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause (ignored)
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		return 'DELETE FROM '.$table.(count($conditions) > 0 ? ' WHERE '.implode(' AND ', $conditions) : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		return $sql.' LIMIT '.$limit.($offset ? ' OFFSET '.$offset : '');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Where
+	 *
+	 * Called by where() or or_where()
+	 *
+	 * @param	mixed
+	 * @param	mixed
+	 * @param	string
+	 * @return	object
+	 */
+	protected function _where($key, $value = NULL, $type = 'AND ', $escape = NULL)
+	{
+		if ( ! is_array($key))
+		{
+			$key = array($key => $value);
+		}
+
+		// If the escape value was not set will will base it on the global setting
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
+
+		foreach ($key as $k => $v)
+		{
+			$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)
+				? $this->_group_get_type('')
+				: $this->_group_get_type($type);
+
+			$k = (($op = $this->_get_operator($k)) !== FALSE)
+				? $this->protect_identifiers(substr($k, 0, strpos($k, $op)), FALSE, $escape).strstr($k, $op)
+				: $this->protect_identifiers($k, FALSE, $escape);
+
+			if (is_null($v) && ! $this->_has_operator($k))
+			{
+				// value appears not to have been set, assign the test to IS NULL
+				$k .= ' IS NULL';
+			}
+
+			if ( ! is_null($v))
+			{
+				if ($escape === TRUE)
+				{
+					$v = ' '.$this->escape($v);
+				}
+				elseif (is_bool($v))
+				{
+					$v = ($v ? ' TRUE' : ' FALSE');
+				}
+
+				if ( ! $this->_has_operator($k))
+				{
+					$k .= ' = ';
+				}
+			}
+
+			$this->qb_where[] = $prefix.$k.$v;
+			if ($this->qb_caching === TRUE)
+			{
+				$this->qb_cache_where[] = $prefix.$k.$v;
+				$this->qb_cache_exists[] = 'where';
+			}
+
+		}
+
+		return $this;
+	}
+
+}
+
+/* End of file pdo_pgsql_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
new file mode 100644
index 0000000..bf0363f
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php
@@ -0,0 +1,167 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO SQLite Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_sqlite_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'sqlite';
+
+	/**
+	 * The syntax to count rows is slightly different across different
+	 * database engines, so this string appears in each driver and is
+	 * used for the count_all() and count_all_results() functions.
+	 */
+	protected $_random_keyword = ' RANDOM()'; // Currently not supported
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'sqlite:';
+
+			if (empty($this->database) && empty($this->hostname))
+			{
+				$this->database = ':memory:';
+			}
+
+			$this->database = empty($this->database) ? $this->hostname : $this->database;
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		$sql = 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\'';
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			return $sql.' AND "NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		// Not supported
+		return FALSE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Field data query
+	 *
+	 * Generates a platform-specific query so that the column data can be retrieved
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _field_data($table)
+	{
+		return 'SELECT * FROM '.$this->protect_identifiers($table).' LIMIT 0,1';
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Replace statement
+	 *
+	 * @param	string	the table name
+	 * @param	array	the insert keys
+	 * @param	array	the insert values
+	 * @return 	string
+	 */
+	protected function _replace($table, $keys, $values)
+	{
+		return 'INSERT OR '.parent::_replace($table, $keys, $values);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Truncate statement
+	 *
+	 * Generates a platform-specific truncate string from the supplied data
+	 *
+	 * If the database does not support the truncate() command,
+	 * then this method maps to 'DELETE FROM table'
+	 *
+	 * @param	string	the table name
+	 * @return	string
+         */
+	protected function _truncate($table)
+	{
+		return 'DELETE FROM '.$table;
+	}
+
+}
+
+/* End of file pdo_sqlite_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
new file mode 100644
index 0000000..f125b8f
--- /dev/null
+++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php
@@ -0,0 +1,299 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0.0
+ * @filesource
+ */
+
+/**
+ * PDO SQLSRV Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the query builder
+ * class is being used or not.
+ *
+ * @package		CodeIgniter
+ * @subpackage	Drivers
+ * @category	Database
+ * @author		EllisLab Dev Team
+ * @link		http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_pdo_sqlsrv_driver extends CI_DB_pdo_driver {
+
+	public $subdriver = 'sqlsrv';
+
+	protected $_random_keyword = ' NEWID()';
+
+	protected $_quoted_identifier;
+
+	/**
+	 * Constructor
+	 *
+	 * Builds the DSN if not already set.
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	public function __construct($params)
+	{
+		parent::__construct($params);
+
+		if (empty($this->dsn))
+		{
+			$this->dsn = 'sqlsrv:Server='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
+
+			empty($this->port) OR $this->dsn .= ','.$this->port;
+			empty($this->database) OR $this->dsn .= ';Database='.$this->database;
+
+			// Some custom options
+
+			if (isset($this->QuotedId))
+			{
+				$this->dsn .= ';QuotedId='.$this->QuotedId;
+				$this->_quoted_identifier = (bool) $this->QuotedId;
+			}
+
+			if (isset($this->ConnectionPooling))
+			{
+				$this->dsn .= ';ConnectionPooling='.$this->ConnectionPooling;
+			}
+
+			if (isset($this->Encrypt))
+			{
+				$this->dsn .= ';Encrypt='.$this->Encrypt;
+			}
+
+			if (isset($this->TraceOn))
+			{
+				$this->dsn .= ';TraceOn='.$this->TraceOn;
+			}
+
+			if (isset($this->TrustServerCertificate))
+			{
+				$this->dsn .= ';TrustServerCertificate='.$this->TrustServerCertificate;
+			}
+
+			empty($this->APP) OR $this->dsn .= ';APP='.$this->APP;
+			empty($this->Failover_Partner) OR $this->dsn .= ';Failover_Partner='.$this->Failover_Partner;
+			empty($this->LoginTimeout) OR $this->dsn .= ';LoginTimeout='.$this->LoginTimeout;
+			empty($this->MultipleActiveResultSets) OR $this->dsn .= ';MultipleActiveResultSets='.$this->MultipleActiveResultSets;
+			empty($this->TraceFile) OR $this->dsn .= ';TraceFile='.$this->TraceFile;
+			empty($this->WSID) OR $this->dsn .= ';WSID='.$this->WSID;
+		}
+		elseif (preg_match('/QuotedId=(0|1)/', $this->dsn, $match))
+		{
+			$this->_quoted_identifier = (bool) $match[1];
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Non-persistent database connection
+	 *
+	 * @param	bool
+	 * @return	object
+	 */
+	public function db_connect($persistent = FALSE)
+	{
+		if ( ! empty($this->char_set) && preg_match('/utf[^8]*8/i', $this->char_set))
+		{
+			$this->options[PDO::SQLSRV_ENCODING_UTF8] = 1;
+		}
+
+		$this->conn_id = parent::db_connect($persistent);
+
+		if ( ! is_object($this->conn_id) OR is_bool($this->_quoted_identifier))
+		{
+			return $this->conn_id;
+		}
+
+		// Determine how identifiers are escaped
+		$query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');
+		$query = $query->row_array();
+		$this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];
+		$this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']');
+
+		return $this->conn_id;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show table query
+	 *
+	 * Generates a platform-specific query string so that the table names can be fetched
+	 *
+	 * @param	bool
+	 * @return	string
+	 */
+	protected function _list_tables($prefix_limit = FALSE)
+	{
+		return 'SELECT '.$this->escape_identifiers('name')
+			.' FROM '.$this->escape_identifiers('sysobjects')
+			.' WHERE '.$this->escape_identifiers('type')." = 'U'";
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			$sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
+		}
+
+		return $sql.' ORDER BY '.$this->escape_identifiers('name');
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Show column query
+	 *
+	 * Generates a platform-specific query string so that the column names can be fetched
+	 *
+	 * @param	string	the table name
+	 * @return	string
+	 */
+	protected function _list_columns($table = '')
+	{
+		return 'SELECT '.$this->escape_identifiers('column_name')
+			.' FROM '.$this->escape_identifiers('information_schema.columns')
+			.' WHERE '.$this->escape_identifiers('table_name').' = '.$this->escape($table);
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * From Tables
+	 *
+	 * This function implicitly groups FROM tables so there is no confusion
+	 * about operator precedence in harmony with SQL standards
+	 *
+	 * @param	array
+	 * @return	string
+	 */
+	protected function _from_tables($tables)
+	{
+		return is_array($tables) ? implode(', ', $tables) : $tables;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Update statement
+	 *
+	 * Generates a platform-specific update string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the update data
+	 * @param	array	the where clause
+	 * @param	array	the orderby clause (ignored)
+	 * @param	array	the limit clause (ignored)
+	 * @param	array	the like clause
+	 * @return	string
+         */
+	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
+	{
+		foreach ($values as $key => $val)
+		{
+			$valstr[] = $key.' = '.$val;
+		}
+
+		$where = empty($where) ? '' : ' WHERE '.implode(' ', $where);
+
+		if ( ! empty($like))
+		{
+			$where .= ($where === '' ? ' WHERE ' : ' AND ').implode(' ', $like);
+		}
+
+		return 'UPDATE '.$table.' SET '.implode(', ', $valstr).$where;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Delete statement
+	 *
+	 * Generates a platform-specific delete string from the supplied data
+	 *
+	 * @param	string	the table name
+	 * @param	array	the where clause
+	 * @param	array	the like clause
+	 * @param	string	the limit clause
+	 * @return	string
+	 */
+	protected function _delete($table, $where = array(), $like = array(), $limit = FALSE)
+	{
+		$conditions = array();
+
+		empty($where) OR $conditions[] = implode(' ', $where);
+		empty($like) OR $conditions[] = implode(' ', $like);
+
+		$conditions = (count($conditions) > 0) ? ' WHERE '.implode(' AND ', $conditions) : '';
+
+		return ($limit)
+			? 'WITH ci_delete AS (SELECT TOP '.$limit.' * FROM '.$table.$conditions.') DELETE FROM ci_delete'
+			: 'DELETE FROM '.$table.$conditions;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Limit string
+	 *
+	 * Generates a platform-specific LIMIT clause
+	 *
+	 * @param	string	the sql query string
+	 * @param	int	the number of rows to limit the query to
+	 * @param	int	the offset value
+	 * @return	string
+	 */
+	protected function _limit($sql, $limit, $offset)
+	{
+		// As of SQL Server 2012 (11.0.*) OFFSET is supported
+		if (version_compare($this->version(), '11', '>='))
+		{
+			return $sql.' OFFSET '.(int) $offset.' ROWS FETCH NEXT '.(int) $limit.' ROWS ONLY';
+		}
+
+		$limit = $offset + $limit;
+
+		// An ORDER BY clause is required for ROW_NUMBER() to work
+		if ($offset && ! empty($this->qb_orderby))
+		{
+			$orderby = 'ORDER BY '.implode(', ', $this->qb_orderby);
+
+			// We have to strip the ORDER BY clause
+			$sql = trim(substr($sql, 0, strrpos($sql, 'ORDER BY '.$orderby)));
+
+			return 'SELECT '.(count($this->qb_select) === 0 ? '*' : implode(', ', $this->qb_select))." FROM (\n"
+				.preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.$orderby.') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)
+				."\n) ".$this->escape_identifiers('CI_subquery')
+				."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.((int) $offset + 1).' AND '.$limit;
+		}
+
+		return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
+	}
+
+}
+
+/* End of file pdo_sqlsrv_driver.php */
+/* Location: ./system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php */
diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php
index c2a1884..20b7867 100644
--- a/system/database/drivers/postgre/postgre_driver.php
+++ b/system/database/drivers/postgre/postgre_driver.php
@@ -48,12 +48,6 @@
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' RANDOM()'; // database specific random keyword
 
 	/**
@@ -356,13 +350,13 @@
 		$table	= (func_num_args() > 0) ? func_get_arg(0) : NULL;
 		$column	= (func_num_args() > 1) ? func_get_arg(1) : NULL;
 
-		if ($table == NULL && $v >= '8.1')
+		if ($table === NULL && $v >= '8.1')
 		{
 			$sql = 'SELECT LASTVAL() AS ins_id';
 		}
-		elseif ($table != NULL)
+		elseif ($table !== NULL)
 		{
-			if ($column != NULL && $v >= '8.0')
+			if ($column !== NULL && $v >= '8.0')
 			{
 				$sql = 'SELECT pg_get_serial_sequence(\''.$table."', '".$column."') AS seq";
 				$query = $this->query($sql);
@@ -399,11 +393,13 @@
 	 */
 	protected function _list_tables($prefix_limit = FALSE)
 	{
-		$sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'";
+		$sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \'public\'';
 
-		if ($prefix_limit !== FALSE && $this->dbprefix != '')
+		if ($prefix_limit !== FALSE && $this->dbprefix !== '')
 		{
-			return $sql." AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
+			return $sql.' AND "table_name" LIKE \''
+				.$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
 		}
 
 		return $sql;
@@ -421,7 +417,7 @@
 	 */
 	protected function _list_columns($table = '')
 	{
-		return "SELECT column_name FROM information_schema.columns WHERE table_name = '".$table."'";
+		return 'SELECT "column_name" FROM "information_schema"."columns" WHERE "table_name" = '.$this->escape($table);
 	}
 
 	// --------------------------------------------------------------------
@@ -467,12 +463,7 @@
 	 */
 	protected function _from_tables($tables)
 	{
-		if ( ! is_array($tables))
-		{
-			$tables = array($tables);
-		}
-
-		return implode(', ', $tables);
+		return is_array($tables) ? implode(', ', $tables) : $tables;
 	}
 
 	// --------------------------------------------------------------------
@@ -528,7 +519,7 @@
 
 			foreach (array_keys($val) as $field)
 			{
-				if ($field != $index)
+				if ($field !== $index)
 				{
 					$final[$field][] =  'WHEN '.$val[$index].' THEN '.$val[$field];
 				}
@@ -544,7 +535,7 @@
 		}
 
 		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2)
-			.' WHERE '.(($where != '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
+			.' WHERE '.(($where !== '' && count($where) > 0) ? implode(' ', $where).' AND ' : '')
 			.$index.' IN('.implode(',', $ids).')';
 	}
 
@@ -585,7 +576,7 @@
 	 */
 	protected function _limit($sql, $limit, $offset)
 	{
-		return $sql.' LIMIT '.$limit.($offset == 0 ? '' : ' OFFSET '.$offset);
+		return $sql.' LIMIT '.$limit.($offset ? ' OFFSET '.$offset : '');
 	}
 
 	// --------------------------------------------------------------------
@@ -598,27 +589,31 @@
 	 * @param	mixed
 	 * @param	mixed
 	 * @param	string
+	 * @param	mixed
 	 * @return	object
-	 *
 	 */
 	protected function _where($key, $value = NULL, $type = 'AND ', $escape = NULL)
 	{
-		$type = $this->_group_get_type($type);
-
 		if ( ! is_array($key))
 		{
 			$key = array($key => $value);
 		}
 
 		// If the escape value was not set will will base it on the global setting
-		if ( ! is_bool($escape))
-		{
-			$escape = $this->_protect_identifiers;
-		}
+		is_bool($escape) OR $escape = $this->_protect_identifiers;
 
 		foreach ($key as $k => $v)
 		{
-			$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) ? '' : $type;
+			$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)
+				? $this->_group_get_type('')
+				: $this->_group_get_type($type);
+
+			if ($escape === TRUE)
+			{
+				$k = (($op = $this->_get_operator($k)) !== FALSE)
+					? $this->escape_identifiers(trim(substr($k, 0, strpos($k, $op)))).' '.strstr($k, $op)
+					: $this->escape_identifiers(trim($k));
+			}
 
 			if (is_null($v) && ! $this->_has_operator($k))
 			{
@@ -630,7 +625,6 @@
 			{
 				if ($escape === TRUE)
 				{
-					$k = $this->protect_identifiers($k, FALSE, $escape);
 					$v = ' '.$this->escape($v);
 				}
 				elseif (is_bool($v))
@@ -643,10 +637,6 @@
 					$k .= ' = ';
 				}
 			}
-			else
-			{
-				$k = $this->protect_identifiers($k, FALSE, $escape);
-			}
 
 			$this->qb_where[] = $prefix.$k.$v;
 			if ($this->qb_caching === TRUE)
diff --git a/system/database/drivers/postgre/postgre_forge.php b/system/database/drivers/postgre/postgre_forge.php
index 94c97af..c434e95 100644
--- a/system/database/drivers/postgre/postgre_forge.php
+++ b/system/database/drivers/postgre/postgre_forge.php
@@ -58,7 +58,7 @@
 			}
 			else
 			{
-				$sql .= "\n\t".$this->db->protect_identifiers($field);
+				$sql .= "\n\t".$this->db->escape_identifiers($field);
 
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
 				$is_unsigned = ( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE);
@@ -107,10 +107,19 @@
 					$sql .= '('.$attributes['CONSTRAINT'].')';
 				}
 
-				$sql .= (isset($attributes['DEFAULT']) ? " DEFAULT '".$attributes['DEFAULT']."'" : '')
-					.(( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE) ? ' NULL' : ' NOT NULL')
-					// Added new attribute to create unqite fields. Also works with MySQL
-					.(( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE) ? ' UNIQUE' : '');
+				if (isset($attributes['DEFAULT']))
+				{
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
+				}
+
+				$sql .= ( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE)
+					? ' NULL' : ' NOT NULL';
+
+				// Added new attribute to create unique fields. Also works with MySQL
+				if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
+				{
+					$sql .= ' UNIQUE';
+				}
 			}
 
 			// don't add a comma on the end of the last field
@@ -139,26 +148,17 @@
 	{
 		$sql = 'CREATE TABLE ';
 
-		if ($if_not_exists === TRUE)
+		// PostgreSQL doesn't support IF NOT EXISTS syntax so we check if table exists manually
+		if ($if_not_exists === TRUE && $this->db->table_exists($table))
 		{
-			// PostgreSQL doesn't support IF NOT EXISTS syntax so we check if table exists manually
-			if ($this->db->table_exists($table))
-			{
-				return TRUE;
-			}
+			return TRUE;
 		}
 
 		$sql .= $this->db->escape_identifiers($table).' ('.$this->_process_fields($fields, $primary_keys);
 
 		if (count($primary_keys) > 0)
 		{
-			// Something seems to break when passing an array to protect_identifiers()
-			foreach ($primary_keys as $index => $key)
-			{
-				$primary_keys[$index] = $this->db->protect_identifiers($key);
-			}
-
-			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $primary_keys).')';
+			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $this->db->escape_identifiers($primary_keys)).')';
 		}
 
 		$sql .= "\n);";
@@ -167,18 +167,14 @@
 		{
 			foreach ($keys as $key)
 			{
-				if (is_array($key))
-				{
-					$key = $this->db->protect_identifiers($key);
-				}
-				else
-				{
-					$key = array($this->db->protect_identifiers($key));
-				}
+				$key = is_array($key)
+					? $this->db->escape_identifiers($key)
+					: array($this->db->escape_identifiers($key));
 
 				foreach ($key as $field)
 				{
-					$sql .= 'CREATE INDEX '.$table.'_'.str_replace(array('"', "'"), '', $field).'_index ON '.$table.' ('.$field.'); ';
+					$sql .= "\nCREATE INDEX ".$this->db->escape_identifiers($table.'_'.str_replace(array('"', "'"), '', $field).'_index')
+						.' ON '.$this->db->escape_identifiers($table).' ('.$this->db->escape_identifiers($field).');';
 				}
 			}
 		}
@@ -205,16 +201,16 @@
 	 */
 	protected function _alter_table($alter_type, $table, $fields, $after_field = '')
  	{
- 		$sql = 'ALTER TABLE '.$this->db->protect_identifiers($table).' '.$alter_type.' ';
+ 		$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' ';
 
  		// DROP has everything it needs now.
  		if ($alter_type === 'DROP')
  		{
- 			return $sql.$this->db->protect_identifiers($fields);
+ 			return $sql.$this->db->escape_identifiers($fields);
  		}
 
  		return $sql.$this->_process_fields($fields)
-			.($after_field != '' ? ' AFTER '.$this->db->protect_identifiers($after_field) : '');
+			.($after_field !== '' ? ' AFTER '.$this->db->escape_identifiers($after_field) : '');
  	}
 
 }
diff --git a/system/database/drivers/postgre/postgre_result.php b/system/database/drivers/postgre/postgre_result.php
index f913bc9..eb9d647 100644
--- a/system/database/drivers/postgre/postgre_result.php
+++ b/system/database/drivers/postgre/postgre_result.php
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.3
  */
 class CI_DB_postgre_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return @pg_num_rows($this->result_id);
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = @pg_num_rows($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -156,11 +159,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return pg_fetch_object($this->result_id);
+		return pg_fetch_object($this->result_id, NULL, $class_name);
 	}
 
 }
diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php
index d8b869c..19824db 100644
--- a/system/database/drivers/sqlite/sqlite_driver.php
+++ b/system/database/drivers/sqlite/sqlite_driver.php
@@ -43,18 +43,12 @@
 	public $dbdriver = 'sqlite';
 
 	// The character used to escape with - not needed for SQLite
-	protected $_escape_char = '';
+	protected $_escape_char = '"';
 
 	// clause and character used for LIKE escape sequences
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' Random()'; // database specific random keyword
 
 	/**
@@ -127,7 +121,9 @@
 	 */
 	protected function _execute($sql)
 	{
-		return @sqlite_query($this->conn_id, $sql);
+		return $this->is_write_type($sql)
+			? @sqlite_exec($this->conn_id, $sql)
+			: @sqlite_query($this->conn_id, $sql);
 	}
 
 	// --------------------------------------------------------------------
@@ -139,13 +135,8 @@
 	 */
 	public function trans_begin($test_mode = FALSE)
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
@@ -153,7 +144,7 @@
 		// Reset the transaction failure flag.
 		// If the $test_mode flag is set to TRUE transactions will be rolled back
 		// even if the queries produce a successful result.
-		$this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
+		$this->_trans_failure = ($test_mode === TRUE);
 
 		$this->simple_query('BEGIN TRANSACTION');
 		return TRUE;
@@ -168,13 +159,8 @@
 	 */
 	public function trans_commit()
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
@@ -192,13 +178,8 @@
 	 */
 	public function trans_rollback()
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
@@ -277,12 +258,13 @@
 	 */
 	protected function _list_tables($prefix_limit = FALSE)
 	{
-		$sql = "SELECT name from sqlite_master WHERE type='table'";
+		$sql = "SELECT name FROM sqlite_master WHERE type='table'";
 
-		if ($prefix_limit !== FALSE AND $this->dbprefix != '')
+		if ($prefix_limit !== FALSE && $this->dbprefix != '')
 		{
-			$sql .= " AND 'name' LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
+			return $sql." AND 'name' LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
 		}
+
 		return $sql;
 	}
 
@@ -314,7 +296,7 @@
 	 */
 	protected function _field_data($table)
 	{
-		return "SELECT * FROM ".$table." LIMIT 1";
+		return 'SELECT * FROM '.$this->escape_identifiers($table).' LIMIT 1';
 	}
 
 	// --------------------------------------------------------------------
@@ -337,27 +319,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	array
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		if ( ! is_array($tables))
-		{
-			$tables = array($tables);
-		}
-
-		return '('.implode(', ', $tables).')';
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Replace statement
 	 *
 	 * Generates a platform-specific replace string from the supplied data
@@ -393,32 +354,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Limit string
-	 *
-	 * Generates a platform-specific LIMIT clause
-	 *
-	 * @param	string	the sql query string
-	 * @param	int	the number of rows to limit the query to
-	 * @param	int	the offset value
-	 * @return	string
-	 */
-	protected function _limit($sql, $limit, $offset)
-	{
-		if ($offset == 0)
-		{
-			$offset = '';
-		}
-		else
-		{
-			$offset .= ", ";
-		}
-
-		return $sql."LIMIT ".$offset.$limit;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Close DB Connection
 	 *
 	 * @return	void
diff --git a/system/database/drivers/sqlite/sqlite_forge.php b/system/database/drivers/sqlite/sqlite_forge.php
index dd6f0f7..e02e327 100644
--- a/system/database/drivers/sqlite/sqlite_forge.php
+++ b/system/database/drivers/sqlite/sqlite_forge.php
@@ -61,6 +61,14 @@
 		{
 			return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
 		}
+		elseif ( ! empty($this->db->data_cache['db_names']))
+		{
+			$key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
+			if ($key !== FALSE)
+			{
+				unset($this->db->data_cache['db_names'][$key]);
+			}
+		}
 
 		return TRUE;
 	}
@@ -97,41 +105,31 @@
 			// entered the field information, so we'll simply add it to the list
 			if (is_numeric($field))
 			{
-				$sql .= "\n\t$attributes";
+				$sql .= "\n\t".$attributes;
 			}
 			else
 			{
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
 
-				$sql .= "\n\t".$this->db->protect_identifiers($field);
+				$sql .= "\n\t".$this->db->escape_identifiers($field).' '.$attributes['TYPE'];
 
-				$sql .=  ' '.$attributes['TYPE'];
+				empty($attributes['CONSTRAINT']) OR $sql .= '('.$attributes['CONSTRAINT'].')';
 
-				if (array_key_exists('CONSTRAINT', $attributes))
-				{
-					$sql .= '('.$attributes['CONSTRAINT'].')';
-				}
-
-				if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
+				if ( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE)
 				{
 					$sql .= ' UNSIGNED';
 				}
 
-				if (array_key_exists('DEFAULT', $attributes))
+				if (isset($attributes['DEFAULT']))
 				{
-					$sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
 				}
 
-				if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
-				{
-					$sql .= ' NULL';
-				}
-				else
-				{
-					$sql .= ' NOT NULL';
-				}
 
-				if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
+				$sql .= ( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE)
+					? ' NULL' : ' NOT NULL';
+
+				if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
 				{
 					$sql .= ' AUTO_INCREMENT';
 				}
@@ -146,30 +144,22 @@
 
 		if (count($primary_keys) > 0)
 		{
-			$primary_keys = $this->db->protect_identifiers($primary_keys);
-			$sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
+			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $this->db->escape_identifiers($primary_keys)).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
 		{
 			foreach ($keys as $key)
 			{
-				if (is_array($key))
-				{
-					$key = $this->db->protect_identifiers($key);
-				}
-				else
-				{
-					$key = array($this->db->protect_identifiers($key));
-				}
+				$key = is_array($key)
+					? $this->db->escape_identifiers($key)
+					: array($this->db->escape_identifiers($key));
 
-				$sql .= ",\n\tUNIQUE (" . implode(', ', $key) . ")";
+				$sql .= ",\n\tUNIQUE (".implode(', ', $key).')';
 			}
 		}
 
-		$sql .= "\n)";
-
-		return $sql;
+		return $sql."\n)";
 	}
 
 	// --------------------------------------------------------------------
@@ -191,40 +181,21 @@
 	 */
 	protected function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
 	{
-		$sql = 'ALTER TABLE '.$this->db->protect_identifiers($table).' '.$alter_type.' '.$this->db->protect_identifiers($column_name);
-
-		// DROP has everything it needs now.
-		if ($alter_type == 'DROP')
+		/* SQLite only supports adding new columns and it does
+		 * NOT support the AFTER statement. Each new column will
+		 * be added as the last one in the table.
+		 */
+		if ($alter_type !== 'ADD COLUMN')
 		{
-			// SQLite does not support dropping columns
-			// http://www.sqlite.org/omitted.html
-			// http://www.sqlite.org/faq.html#q11
+			// Not supported
 			return FALSE;
 		}
 
-		$sql .= " $column_definition";
-
-		if ($default_value != '')
-		{
-			$sql .= " DEFAULT \"$default_value\"";
-		}
-
-		if ($null === NULL)
-		{
-			$sql .= ' NULL';
-		}
-		else
-		{
-			$sql .= ' NOT NULL';
-		}
-
-		if ($after_field != '')
-		{
-			return $sql.' AFTER '.$this->db->protect_identifiers($after_field);
-		}
-
-		return $sql;
-
+		return 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' '.$this->db->escape_identifiers($column_name)
+			.' '.$column_definition
+			.($default_value != '' ? " DEFAULT '".$default_value."'" : '')
+			// If NOT NULL is specified, the field must have a DEFAULT value other than NULL
+			.(($null !== NULL && $default_value !== 'NULL') ? ' NOT NULL' : ' NULL');
 	}
 
 }
diff --git a/system/database/drivers/sqlite/sqlite_result.php b/system/database/drivers/sqlite/sqlite_result.php
index 741dc9d..eef9787 100644
--- a/system/database/drivers/sqlite/sqlite_result.php
+++ b/system/database/drivers/sqlite/sqlite_result.php
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	1.3
  */
 class CI_DB_sqlite_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return @sqlite_num_rows($this->result_id);
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = @sqlite_num_rows($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -140,17 +143,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		if (function_exists('sqlite_fetch_object'))
-		{
-			return sqlite_fetch_object($this->result_id);
-		}
-
-		$arr = sqlite_fetch_array($this->result_id, SQLITE_ASSOC);
-		return is_array($arr) ? (object) $arr : FALSE;
+		return sqlite_fetch_object($this->result_id, $class_name);
 	}
 
 }
diff --git a/system/database/drivers/sqlite3/sqlite3_driver.php b/system/database/drivers/sqlite3/sqlite3_driver.php
index ea4cf2d..cc35d31 100644
--- a/system/database/drivers/sqlite3/sqlite3_driver.php
+++ b/system/database/drivers/sqlite3/sqlite3_driver.php
@@ -50,12 +50,6 @@
 	protected $_like_escape_str = ' ESCAPE \'%s\' ';
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
 	protected $_random_keyword = ' RANDOM()';
 
 	/**
@@ -87,7 +81,7 @@
 	public function db_pconnect()
 	{
 		log_message('debug', 'SQLite3 doesn\'t support persistent connections');
-		return $this->db_pconnect();
+		return $this->db_connect();
 	}
 
 	// --------------------------------------------------------------------
@@ -104,7 +98,7 @@
 			return $this->data_cache['version'];
 		}
 
-		$version = $this->conn_id->version();
+		$version = SQLite3::version();
 		return $this->data_cache['version'] = $version['versionString'];
 	}
 
@@ -318,27 +312,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * From Tables
-	 *
-	 * This function implicitly groups FROM tables so there is no confusion
-	 * about operator precedence in harmony with SQL standards
-	 *
-	 * @param	string
-	 * @return	string
-	 */
-	protected function _from_tables($tables)
-	{
-		if ( ! is_array($tables))
-		{
-			$tables = array($tables);
-		}
-
-		return '('.implode(', ', $tables).')';
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Replace statement
 	 *
 	 * Generates a platform-specific replace string from the supplied data
@@ -374,23 +347,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Limit string
-	 *
-	 * Generates a platform-specific LIMIT clause
-	 *
-	 * @param	string	the sql query string
-	 * @param	int	the number of rows to limit the query to
-	 * @param	int	the offset value
-	 * @return	string
-	 */
-	protected function _limit($sql, $limit, $offset)
-	{
-		return $sql.' LIMIT '.($offset ? $offset.',' : '').$limit;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Close DB Connection
 	 *
 	 * @return	void
diff --git a/system/database/drivers/sqlite3/sqlite3_forge.php b/system/database/drivers/sqlite3/sqlite3_forge.php
index 20f1e6f..6a76ba9 100644
--- a/system/database/drivers/sqlite3/sqlite3_forge.php
+++ b/system/database/drivers/sqlite3/sqlite3_forge.php
@@ -66,6 +66,14 @@
 			{
 				return $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE;
 			}
+			elseif ( ! empty($this->db->data_cache['db_names']))
+			{
+				$key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
+				if ($key !== FALSE)
+				{
+					unset($this->db->data_cache['db_names'][$key]);
+				}
+			}
 
 			return TRUE;
 		}
@@ -111,13 +119,27 @@
 			{
 				$attributes = array_change_key_case($attributes, CASE_UPPER);
 
-				$sql .= "\n\t".$this->db->protect_identifiers($field)
-					.' '.$attributes['TYPE']
-					.( ! empty($attributes['CONSTRAINT']) ? '('.$attributes['CONSTRAINT'].')' : '')
-					.(( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE) ? ' UNSIGNED' : '')
-					.(isset($attributes['DEFAULT']) ? ' DEFAULT \''.$attributes['DEFAULT'].'\'' : '')
-					.(( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE) ? ' NULL' : ' NOT NULL')
-					.(( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE) ? ' AUTO_INCREMENT' : '');
+				$sql .= "\n\t".$this->db->escape_identifiers($field).' '.$attributes['TYPE'];
+
+				empty($attributes['CONSTRAINT']) OR $sql .= '('.$attributes['CONSTRAINT'].')';
+
+				if ( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE)
+				{
+					$sql .= ' UNSIGNED';
+				}
+
+				if (isset($attributes['DEFAULT']))
+				{
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
+				}
+
+				$sql .= ( ! empty($attributes['NULL']) && $attributes['NULL'] === TRUE)
+					? ' NULL' : ' NOT NULL';
+
+				if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
+				{
+					$sql .= ' AUTO_INCREMENT';
+				}
 			}
 
 			// don't add a comma on the end of the last field
@@ -129,22 +151,16 @@
 
 		if (count($primary_keys) > 0)
 		{
-			$primary_keys = $this->db->protect_identifiers($primary_keys);
-			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $primary_keys).')';
+			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $this->db->escape_identifiers($primary_keys)).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
 		{
 			foreach ($keys as $key)
 			{
-				if (is_array($key))
-				{
-					$key = $this->db->protect_identifiers($key);
-				}
-				else
-				{
-					$key = array($this->db->protect_identifiers($key));
-				}
+				$key = is_array($key)
+					? $this->db->escape_identifiers($key)
+					: array($this->db->escape_identifiers($key));
 
 				$sql .= ",\n\tUNIQUE (".implode(', ', $key).')';
 			}
@@ -182,9 +198,9 @@
 			return FALSE;
 		}
 
-		return 'ALTER TABLE '.$this->db->protect_identifiers($table).' '.$alter_type.' '.$this->db->protect_identifiers($column_name)
+		return 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' '.$this->db->escape_identifiers($column_name)
 			.' '.$column_definition
-			.($default_value != '' ? ' DEFAULT '.$default_value : '')
+			.($default_value !== '' ? ' DEFAULT '.$default_value : '')
 			// If NOT NULL is specified, the field must have a DEFAULT value other than NULL
 			.(($null !== NULL && $default_value !== 'NULL') ? ' NOT NULL' : ' NULL');
 	}
diff --git a/system/database/drivers/sqlite3/sqlite3_result.php b/system/database/drivers/sqlite3/sqlite3_result.php
index 946b365..8e9b9c1 100644
--- a/system/database/drivers/sqlite3/sqlite3_result.php
+++ b/system/database/drivers/sqlite3/sqlite3_result.php
@@ -26,40 +26,21 @@
  */
 
 /**
- * SQLite Result Class
+ * SQLite3 Result Class
  *
  * This class extends the parent result class: CI_DB_result
  *
  * @category	Database
- * @author	Andrey Andreev
- * @link	http://codeigniter.com/user_guide/database/
+ * @author		Andrey Andreev
+ * @link		http://codeigniter.com/user_guide/database/
+ * @since	3.0
  */
 class CI_DB_sqlite3_result extends CI_DB_result {
 
-	// Overwriting the parent here, so we have a way to know if it's already set
-	public $num_rows;
-
 	// num_fields() might be called multiple times, so we'll use this one to cache it's result
 	protected $_num_fields;
 
 	/**
-	 * Number of rows in the result set
-	 *
-	 * @return	int
-	 */
-	public function num_rows()
-	{
-		/* The SQLite3 driver doesn't have a graceful way to do this,
-		 * so we'll have to do it on our own.
-		 */
-		return is_int($this->num_rows)
-			? $this->num_rows
-			: $this->num_rows = count($this->result_array());
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Number of fields in the result set
 	 *
 	 * @return	int
@@ -153,443 +134,28 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		// No native support for fetching as an object
-		$row = $this->_fetch_assoc();
-		return ($row !== FALSE) ? (object) $row : FALSE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. "array" version.
-	 *
-	 * return	array
-	 */
-	public function result_array()
-	{
-		if (count($this->result_array) > 0)
+		// No native support for fetching rows as objects
+		if (($row = $this->result_id->fetchArray(SQLITE3_ASSOC)) === FALSE)
 		{
-			return $this->result_array;
+			return FALSE;
 		}
-		elseif (is_array($this->row_data))
+		elseif ($class_name === 'stdClass')
 		{
-			if (count($this->row_data) === 0)
-			{
-				return $this->result_array;
-			}
-			else
-			{
-				$row_index = count($this->row_data);
-			}
-		}
-		else
-		{
-			$row_index = 0;
-			$this->row_data = array();
-		}
-
-		$row = NULL;
-		while ($row = $this->_fetch_assoc())
-		{
-			$this->row_data[$row_index++] = $row;
-		}
-
-		return $this->result_array = $this->row_data;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. "object" version.
-	 *
-	 * @return	array
-	 */
-	public function result_object()
-	{
-		if (count($this->result_object) > 0)
-		{
-			return $this->result_object;
-		}
-		elseif (count($this->result_array) > 0)
-		{
-			for ($i = 0, $c = count($this->result_array); $i < $c; $i++)
-			{
-				$this->result_object[] = (object) $this->result_array[$i];
-			}
-
-			return $this->result_object;
-		}
-		elseif (is_array($this->row_data))
-		{
-			if (count($this->row_data) === 0)
-			{
-				return $this->result_object;
-			}
-			else
-			{
-				$row_index = count($this->row_data);
-				for ($i = 0; $i < $row_index; $i++)
-				{
-					$this->result_object[$i] = (object) $this->row_data[$i];
-				}
-			}
-		}
-		else
-		{
-			$row_index = 0;
-			$this->row_data = array();
-		}
-
-		$row = NULL;
-		while ($row = $this->_fetch_assoc())
-		{
-			$this->row_data[$row_index] = $row;
-			$this->result_object[$row_index++] = (object) $row;
-		}
-
-		$this->result_array = $this->row_data;
-
-		/* As described for the num_rows() method - there's no easy
-		 * way to get the number of rows selected. Our work-around
-		 * solution (as in here as well) first checks if result_array
-		 * exists and returns its count. It doesn't however check for
-		 * custom_object_result, so - do it here.
-		 */
-		if ( ! is_int($this->num_rows))
-		{
-			$this->num_rows = count($this->result_object);
-		}
-
-		return $this->result_object;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Query result. Custom object version.
-	 *
-	 * @param	string	class name used to instantiate rows to
-	 * @return	array
-	 */
-	public function custom_result_object($class_name)
-	{
-		if (array_key_exists($class_name, $this->custom_result_object))
-		{
-			return $this->custom_result_object[$class_name];
-		}
-
-		if ( ! class_exists($class_name) OR ! is_object($this->result_id) OR $this->num_rows() === 0)
-		{
-			return array();
-		}
-
-		/* Even if result_array hasn't been set prior to custom_result_object being called,
-		 * num_rows() has done it.
-		*/
-		$data = &$this->result_array;
-
-		$result_object = array();
-		for ($i = 0, $c = count($data); $i < $c; $i++)
-		{
-			$result_object[$i] = new $class_name();
-			foreach ($data[$i] as $key => $value)
-			{
-				$result_object[$i]->$key = $value;
-			}
-		}
-
-		/* As described for the num_rows() method - there's no easy
-		 * way to get the number of rows selected. Our work-around
-		 * solution (as in here as well) first checks if result_array
-		 * exists and returns its count. It doesn't however check for
-		 * custom_object_result, so - do it here.
-		 */
-		if ( ! is_int($this->num_rows))
-		{
-			$this->num_rows = count($result_object);
-		}
-
-		// Cache and return the array
-		return $this->custom_result_object[$class_name] = $result_object;
-        }
-
-	// --------------------------------------------------------------------
-
-	/* Single row result.
-	 *
-	 * Acts as a wrapper for row_object(), row_array()
-	 * and custom_row_object(). Also used by first_row(), next_row()
-	 * and previous_row().
-	 *
-	 * @param	int	row index
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function row($n = 0, $type = 'object')
-	{
-		if ($type === 'object')
-		{
-			return $this->row_object($n);
-		}
-		elseif ($type === 'array')
-		{
-			return $this->row_array($n);
-		}
-
-		return $this->custom_row_object($n, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Array version.
-	 *
-	 * @param	int	row index
-	 * @return	array
-	 */
-	public function row_array($n = 0)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
-		{
-			$n = (int) $n;
-		}
-
-		/* If row_data is initialized, it means that we've already tried
-		 * (at least) to fetch some data, so ... check if we already have
-		 * this row.
-		*/
-		if (is_array($this->row_data))
-		{
-			/* If we already have row_data[$n] - return it.
-			 *
-			 * If we enter the elseif, there's a number of reasons to
-			 * return an empty array:
-			 *
-			 *	- count($this->row_data) === 0 means there are no results
-			 *	- num_rows being set or result_array having count() > 0 means
-			 *	  that we've already fetched all data and $n is greater than
-			 *	  our highest row index available
-			 *	- $n < $this->current_row means that if such row existed,
-			 *	  we would've already returned it, therefore $n is an
-			 *	  invalid index
-			 */
-			if (isset($this->row_data[$n])) // We already have this row
-			{
-				$this->current_row = $n;
-				return $this->row_data[$n];
-			}
-			elseif (count($this->row_data) === 0 OR is_int($this->num_rows)
-				OR count($this->result_array) > 0 OR $n < $this->current_row)
-			{
-				// No such row exists
-				return NULL;
-			}
-
-			// Get the next row index that would actually need to be fetched
-			$current_row = ($this->current_row < count($this->row_data)) ? count($this->row_data) : $this->current_row + 1;
-		}
-		else
-		{
-			$current_row = $this->current_row = 0;
-			$this->row_data = array();
-		}
-
-		/* Fetch more data, if available
-		 *
-		 * NOTE: Operator precedence is important here, if you change
-		 *	 'AND' with '&&' - it WILL BREAK the results, as
-		 *	 $row will be assigned the scalar value of both
-		 *	 expressions!
-		 */
-		while ($row = $this->_fetch_assoc() AND $current_row <= $n)
-		{
-			$this->row_data[$current_row++] = $row;
-		}
-
-		// This would mean that there's no (more) data to fetch
-		if ( ! is_array($this->row_data) OR ! isset($this->row_data[$n]))
-		{
-			// Cache what we already have
-			if (is_array($this->row_data))
-			{
-				$this->num_rows = count($this->row_data);
-				/* Usually, row_data could have less elements than result_array,
-				 * but at this point - they should be exactly the same.
-				 */
-				$this->result_array = $this->row_data;
-			}
-			else
-			{
-				$this->num_rows = 0;
-			}
-
-			return NULL;
-		}
-
-		$this->current_row = $n;
-		return $this->row_data[$n];
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Object version.
-	 *
-	 * @param	int	row index
-	 * @return	mixed	object if row found; empty array if not
-	 */
-	public function row_object($n = 0)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
-		{
-			$n = (int) $n;
-		}
-
-		/* Logic here is exactly the same as in row_array,
-		 * except we have to cast row_data[$n] to an object.
-		 *
-		 * If we already have result_object though - we can
-		 * directly return from it.
-		 */
-		if (isset($this->result_object[$n]))
-		{
-			$this->current_row = $n;
-			return $this->result_object[$n];
-		}
-
-		$row = $this->row_array($n);
-		// Cast only if the row exists
-		if (count($row) > 0)
-		{
-			$this->current_row = $n;
 			return (object) $row;
 		}
 
-		return NULL;
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Single row result. Custom object version.
-	 *
-	 * @param	int	row index
-	 * @param	string	custom class name
-	 * @return	mixed	custom object if row found; empty array otherwise
-	 */
-	public function custom_row_object($n = 0, $class_name)
-	{
-		// Make sure $n is not a string
-		if ( ! is_int($n))
+		$class_name = new $class_name();
+		foreach (array_keys($row) as $key)
 		{
-			$n = (int) $n;
+			$class_name->$key = $row[$key];
 		}
 
-		if (array_key_exists($class_name, $this->custom_result_object))
-		{
-			/* We already have a the whole result set with this class_name,
-			 * return the specified row if it exists, and an empty array if
-			 * it doesn't.
-			 */
-			if (isset($this->custom_result_object[$class_name][$n]))
-			{
-				$this->current_row = $n;
-				return $this->custom_result_object[$class_name][$n];
-			}
-			else
-			{
-				return NULL;
-			}
-		}
-		elseif ( ! class_exists($class_name)) // No such class exists
-		{
-			return NULL;
-		}
-
-		$row = $this->row_array($n);
-		// A non-array would mean that the row doesn't exist
-		if ( ! is_array($row))
-		{
-			return NULL;
-		}
-
-		// Convert to the desired class and return
-		$row_object = new $class_name();
-		foreach ($row as $key => $value)
-		{
-			$row_object->$key = $value;
-		}
-
-		$this->current_row = $n;
-		return $row_object;
-	}
-
-	// --------------------------------------------------------------------
-
-	/* First row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function first_row($type = 'object')
-	{
-		return $this->row(0, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Last row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function last_row($type = 'object')
-	{
-		$result = &$this->result($type);
-		if ( ! isset($this->num_rows))
-		{
-			$this->num_rows = count($result);
-		}
-		$this->current_row = $this->num_rows - 1;
-		return $result[$this->current_row];
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Next row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function next_row($type = 'object')
-	{
-		if (is_array($this->row_data))
-		{
-			$count = count($this->row_data);
-			$n = ($this->current_row > $count OR ($this->current_row === 0 && $count === 0)) ? $count : $this->current_row + 1;
-		}
-		else
-		{
-			$n = 0;
-		}
-
-		return $this->row($n, $type);
-	}
-
-	// --------------------------------------------------------------------
-
-	/* Previous row result.
-	 *
-	 * @param	string	('object', 'array' or a custom class name)
-	 * @return	mixed	whatever was passed to the second parameter
-	 */
-	public function previous_row($type = 'object')
-	{
-		$n = ($this->current_row !== 0) ? $this->current_row - 1 : 0;
-		return $this->row($n, $type);
+		return $class_name;
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php
index 961066d..8bd18bd 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_driver.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php
@@ -21,7 +21,7 @@
  * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
  * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
  * @link		http://codeigniter.com
- * @since		Version 1.0
+ * @since		Version 2.0.3
  * @filesource
  */
 
@@ -43,19 +43,16 @@
 	public $dbdriver = 'sqlsrv';
 
 	// The character used for escaping
-	protected $_escape_char = '';
+	protected $_escape_char = '"';
 
 	// clause and character used for LIKE escape sequences
 	protected $_like_escape_str = " ESCAPE '%s' ";
 	protected $_like_escape_chr = '!';
 
-	/**
-	 * The syntax to count rows is slightly different across different
-	 * database engines, so this string appears in each driver and is
-	 * used for the count_all() and count_all_results() functions.
-	 */
-	protected $_count_string = 'SELECT COUNT(*) AS ';
-	protected $_random_keyword = ' NEWID()'; // not currently supported
+	protected $_random_keyword = ' NEWID()';
+
+	// SQLSRV-specific properties
+	protected $_quoted_identifier = TRUE;
 
 	/**
 	 * Non-persistent database connection
@@ -83,7 +80,15 @@
 			unset($connection['UID'], $connection['PWD']);
 		}
 
-		return sqlsrv_connect($this->hostname, $connection);
+		$this->conn_id = sqlsrv_connect($this->hostname, $connection);
+
+		// Determine how identifiers are escaped
+		$query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi');
+		$query = $query->row_array();
+		$this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi'];
+		$this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']');
+
+		return $this->conn_id;
 	}
 
 	// --------------------------------------------------------------------
@@ -132,7 +137,7 @@
 	 */
 	protected function _execute($sql)
 	{
-		return (is_write_type($sql) && stripos($sql, 'INSERT') === FALSE)
+		return ($this->is_write_type($sql) && stripos($sql, 'INSERT') === FALSE)
 			? sqlsrv_query($this->conn_id, $sql)
 			: sqlsrv_query($this->conn_id, $sql, NULL, array('Scrollable' => SQLSRV_CURSOR_STATIC));
 	}
@@ -146,13 +151,8 @@
 	 */
 	public function trans_begin($test_mode = FALSE)
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
@@ -160,7 +160,7 @@
 		// Reset the transaction failure flag.
 		// If the $test_mode flag is set to TRUE transactions will be rolled back
 		// even if the queries produce a successful result.
-		$this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
+		$this->_trans_failure = ($test_mode === TRUE);
 
 		return sqlsrv_begin_transaction($this->conn_id);
 	}
@@ -174,13 +174,8 @@
 	 */
 	public function trans_commit()
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
@@ -197,13 +192,8 @@
 	 */
 	public function trans_rollback()
 	{
-		if ( ! $this->trans_enabled)
-		{
-			return TRUE;
-		}
-
 		// When transactions are nested we only begin/commit/rollback the outermost ones
-		if ($this->_trans_depth > 0)
+		if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
 		{
 			return TRUE;
 		}
@@ -288,7 +278,17 @@
 	 */
 	protected function _list_tables($prefix_limit = FALSE)
 	{
-		return "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
+		$sql = 'SELECT '.$this->escape_identifiers('name')
+			.' FROM '.$this->escape_identifiers('sysobjects')
+			.' WHERE '.$this->escape_identifiers('type')." = 'U'";
+
+		if ($prefix_limit === TRUE && $this->dbprefix !== '')
+		{
+			$sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' "
+				.sprintf($this->_escape_like_str, $this->_escape_like_chr);
+		}
+
+		return $sql.' ORDER BY '.$this->escape_identifiers('name');
 	}
 
 	// --------------------------------------------------------------------
@@ -318,7 +318,7 @@
 	 */
 	protected function _field_data($table)
 	{
-		return 'SELECT TOP 1 * FROM '.$table;
+		return 'SELECT TOP 1 * FROM '.$this->protect_identifiers($table);
 	}
 
 	// --------------------------------------------------------------------
@@ -372,12 +372,7 @@
 	 */
 	protected function _from_tables($tables)
 	{
-		if ( ! is_array($tables))
-		{
-			$tables = array($tables);
-		}
-
-		return implode(', ', $tables);
+		return is_array($tables) ? implode(', ', $tables) : $tables;
 	}
 
 	// --------------------------------------------------------------------
@@ -397,7 +392,7 @@
 	 */
 	protected function _update($table, $values, $where, $orderby = array(), $limit = FALSE, $like = array())
 	{
-		foreach($values as $key => $val)
+		foreach ($values as $key => $val)
 		{
 			$valstr[] = $key.' = '.$val;
 		}
@@ -471,7 +466,29 @@
 	 */
 	protected function _limit($sql, $limit, $offset)
 	{
-		return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.($limit + $offset).' ', $sql);
+		// As of SQL Server 2012 (11.0.*) OFFSET is supported
+		if (version_compare($this->version(), '11', '>='))
+		{
+			return $sql.' OFFSET '.(int) $offset.' ROWS FETCH NEXT '.(int) $limit.' ROWS ONLY';
+		}
+
+		$limit = $offset + $limit;
+
+		// An ORDER BY clause is required for ROW_NUMBER() to work
+		if ($offset && ! empty($this->qb_orderby))
+		{
+			$orderby = 'ORDER BY '.implode(', ', $this->qb_orderby);
+
+			// We have to strip the ORDER BY clause
+			$sql = trim(substr($sql, 0, strrpos($sql, 'ORDER BY '.$orderby)));
+
+			return 'SELECT '.(count($this->qb_select) === 0 ? '*' : implode(', ', $this->qb_select))." FROM (\n"
+				.preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.$orderby.') AS '.$this->escape_identifiers('CI_rownum').', ', $sql)
+				."\n) ".$this->escape_identifiers('CI_subquery')
+				."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.((int) $offset + 1).' AND '.$limit;
+		}
+
+		return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql);
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/database/drivers/sqlsrv/sqlsrv_forge.php b/system/database/drivers/sqlsrv/sqlsrv_forge.php
index c817c2c..ccdb369 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_forge.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_forge.php
@@ -21,7 +21,7 @@
  * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
  * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
  * @link		http://codeigniter.com
- * @since		Version 1.0
+ * @since		Version 2.0.3
  * @filesource
  */
 
@@ -44,7 +44,7 @@
 	 * @param	mixed	primary key(s)
 	 * @param	mixed	key(s)
 	 * @param	bool	should 'IF NOT EXISTS' be added to the SQL
-	 * @return	bool
+	 * @return	string
 	 */
 	protected function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
 	{
@@ -70,31 +70,25 @@
 
 				$sql .= "\n\t".$this->db->escape_identifiers($field).' '.$attributes['TYPE'];
 
-				if (array_key_exists('CONSTRAINT', $attributes))
+				if (stripos($attributes['TYPE'], 'INT') === FALSE && ! empty($attributes['CONSTRAINT']))
 				{
 					$sql .= '('.$attributes['CONSTRAINT'].')';
 				}
 
-				if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
+				if ( ! empty($attributes['UNSIGNED']) && $attributes['UNSIGNED'] === TRUE)
 				{
 					$sql .= ' UNSIGNED';
 				}
 
-				if (array_key_exists('DEFAULT', $attributes))
+				if (isset($attributes['DEFAULT']))
 				{
-					$sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
+					$sql .= " DEFAULT '".$attributes['DEFAULT']."'";
 				}
 
-				if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
-				{
-					$sql .= ' NULL';
-				}
-				else
-				{
-					$sql .= ' NOT NULL';
-				}
+				$sql .= ( ! empty($attributes['NULL']) && $attribues['NULL'] === TRUE)
+					? ' NULL' : ' NOT NULL';
 
-				if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
+				if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE)
 				{
 					$sql .= ' AUTO_INCREMENT';
 				}
@@ -109,22 +103,16 @@
 
 		if (count($primary_keys) > 0)
 		{
-			$primary_keys = $this->db->protect_identifiers($primary_keys);
-			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $primary_keys).')';
+			$sql .= ",\n\tPRIMARY KEY (".implode(', ', $this->db->escape_identifiers($primary_keys)).')';
 		}
 
 		if (is_array($keys) && count($keys) > 0)
 		{
 			foreach ($keys as $key)
 			{
-				if (is_array($key))
-				{
-					$key = $this->db->protect_identifiers($key);
-				}
-				else
-				{
-					$key = array($this->db->protect_identifiers($key));
-				}
+				$key = is_array($key)
+					? $this->db->escape_identifiers($key)
+					: array($this->escape_identifiers($key));
 
 				$sql .= ",\n\tFOREIGN KEY (".implode(', ', $key).')';
 			}
@@ -152,29 +140,18 @@
 	 */
 	protected function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
 	{
-		$sql = 'ALTER TABLE '.$this->db->protect_identifiers($table).' '.$alter_type.' '.$this->db->protect_identifiers($column_name);
+		$sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' '.$alter_type.' '.$this->db->escape_identifiers($column_name);
 
 		// DROP has everything it needs now.
-		if ($alter_type == 'DROP')
+		if ($alter_type === 'DROP')
 		{
 			return $sql;
 		}
 
-		$sql .= ' '.$column_definition;
-
-		if ($default_value != '')
-		{
-			$sql .= " DEFAULT '".$default_value."'";
-		}
-
-		$sql .= ($null === NULL) ? ' NULL' : ' NOT NULL';
-
-		if ($after_field != '')
-		{
-			return $sql.' AFTER '.$this->db->protect_identifiers($after_field);
-		}
-
-		return $sql;
+		return $sql.' '.$column_definition
+			.($default_value != '' ? ' DEFAULT "'.$default_value.'"' : '')
+			.($null === NULL ? ' NULL' : ' NOT NULL')
+			.($after_field != '' ? ' AFTER '.$this->db->escape_identifiers($after_field) : '');
 	}
 
 }
diff --git a/system/database/drivers/sqlsrv/sqlsrv_result.php b/system/database/drivers/sqlsrv/sqlsrv_result.php
index 0802677..fb7a686 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_result.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_result.php
@@ -33,6 +33,7 @@
  * @category	Database
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/database/
+ * @since	2.0.3
  */
 class CI_DB_sqlsrv_result extends CI_DB_result {
 
@@ -43,7 +44,9 @@
 	 */
 	public function num_rows()
 	{
-		return @sqlsrv_num_rows($this->result_id);
+		return is_int($this->num_rows)
+			? $this->num_rows
+			: $this->num_rows = @sqlsrv_num_rows($this->result_id);
 	}
 
 	// --------------------------------------------------------------------
@@ -92,12 +95,12 @@
 		$retval = array();
 		foreach (sqlsrv_field_metadata($this->result_id) as $offset => $field)
 		{
-			$F 				= new stdClass();
-			$F->name 		= $field['Name'];
-			$F->type 		= $field['Type'];
+			$F 		= new stdClass();
+			$F->name 	= $field['Name'];
+			$F->type 	= $field['Type'];
 			$F->max_length	= $field['Size'];
 			$F->primary_key = 0;
-			$F->default		= '';
+			$F->default	= '';
 
 			$retval[] = $F;
 		}
@@ -142,11 +145,12 @@
 	 *
 	 * Returns the result set as an object
 	 *
+	 * @param	string
 	 * @return	object
 	 */
-	protected function _fetch_object()
+	protected function _fetch_object($class_name = 'stdClass')
 	{
-		return sqlsrv_fetch_object($this->result_id);
+		return sqlsrv_fetch_object($this->result_id, $class_name);
 	}
 
 }
diff --git a/system/database/drivers/sqlsrv/sqlsrv_utility.php b/system/database/drivers/sqlsrv/sqlsrv_utility.php
index 394964b..d518cc1 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_utility.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_utility.php
@@ -21,7 +21,7 @@
  * @copyright	Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
  * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
  * @link		http://codeigniter.com
- * @since		Version 1.0
+ * @since		Version 2.0.3
  * @filesource
  */
 
@@ -41,7 +41,7 @@
 	 * SQLSRV Export
 	 *
 	 * @param	array	Preferences
-	 * @return	mixed
+	 * @return	bool
 	 */
 	protected function _backup($params = array())
 	{
diff --git a/system/helpers/array_helper.php b/system/helpers/array_helper.php
index 6a7c8e3..ed2fe3c 100644
--- a/system/helpers/array_helper.php
+++ b/system/helpers/array_helper.php
@@ -43,16 +43,16 @@
 	 * Element
 	 *
 	 * Lets you determine whether an array index is set and whether it has a value.
-	 * If the element is empty it returns FALSE (or whatever you specify as the default value.)
+	 * If the element is empty it returns NULL (or whatever you specify as the default value.)
 	 *
 	 * @param	string
 	 * @param	array
 	 * @param	mixed
 	 * @return	mixed	depends on what the array contains
 	 */
-	function element($item, $array, $default = FALSE)
+	function element($item, $array, $default = NULL)
 	{
-		return empty($array[$item]) ? $default : $array[$item];
+		return array_key_exists($item, $array) ? $array[$item] : $default;
 	}
 }
 
@@ -87,7 +87,7 @@
 	 * @param	mixed
 	 * @return	mixed	depends on what the array contains
 	 */
-	function elements($items, $array, $default = FALSE)
+	function elements($items, $array, $default = NULL)
 	{
 		$return = array();
 
@@ -95,7 +95,7 @@
 
 		foreach ($items as $item)
 		{
-			$return[$item] = isset($array[$item]) ? $array[$item] : $default;
+			$return[$item] = array_key_exists($item, $array) ? $array[$item] : $default;
 		}
 
 		return $return;
diff --git a/system/helpers/captcha_helper.php b/system/helpers/captcha_helper.php
index b116706..4676b2a 100644
--- a/system/helpers/captcha_helper.php
+++ b/system/helpers/captcha_helper.php
@@ -64,7 +64,7 @@
 			}
 		}
 
-		if ($img_path == '' OR $img_url == ''
+		if ($img_path === '' OR $img_url === ''
 			OR ! @is_dir($img_path) OR ! is_writeable($img_path)
 			OR ! extension_loaded('gd'))
 		{
@@ -93,7 +93,7 @@
 		// Do we have a "word" yet?
 		// -----------------------------------
 
-		if ($word == '')
+		if ($word === '')
 		{
 			$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
 			$word = '';
@@ -156,7 +156,7 @@
 		//  Write the text
 		// -----------------------------------
 
-		$use_font = ($font_path != '' && file_exists($font_path) && function_exists('imagettftext'));
+		$use_font = ($font_path !== '' && file_exists($font_path) && function_exists('imagettftext'));
 		if ($use_font === FALSE)
 		{
 			$font_size = 5;
diff --git a/system/helpers/date_helper.php b/system/helpers/date_helper.php
index 5f0427f..a45b3d7 100644
--- a/system/helpers/date_helper.php
+++ b/system/helpers/date_helper.php
@@ -42,29 +42,28 @@
 	/**
 	 * Get "now" time
 	 *
-	 * Returns time() or its GMT equivalent based on the config file preference
+	 * Returns time() based on the timezone parameter or on the
+	 * "time_reference" setting
 	 *
+	 * @param	string
 	 * @return	int
 	 */
-	function now()
+	function now($timezone = NULL)
 	{
-		$CI =& get_instance();
-
-		if (strtolower($CI->config->item('time_reference')) == 'gmt')
+		if (empty($timezone))
 		{
-			$now = time();
-			$system_time = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now));
-
-			if (strlen($system_time) < 10)
-			{
-				$system_time = time();
-				log_message('error', 'The Date class could not set a proper GMT timestamp so the local time() value was used.');
-			}
-
-			return $system_time;
+			$timezone = config_item('time_reference');
 		}
 
-		return time();
+		if ($timezone === 'local' OR $timezone === date_default_timezone_get())
+		{
+			return time();
+		}
+
+		$datetime = new DateTime('now', new DateTimeZone($timezone));
+		sscanf($datetime->format('j-n-Y G:i:s'), '%d-%d-%d %d:%d:%d', $day, $month, $year, $hour, $minute, $second);
+
+		return mktime($hour, $minute, $second, $month, $day, $year);
 	}
 }
 
@@ -90,12 +89,14 @@
 	 */
 	function mdate($datestr = '', $time = '')
 	{
-		if ($datestr == '')
+		if ($datestr === '')
 		{
 			return '';
 		}
-
-		$time = ($time == '') ? now() : $time;
+		elseif (empty($time))
+		{
+			$time = now();
+		}
 
 		$datestr = str_replace(
 			'%\\',
@@ -116,31 +117,37 @@
 	 *
 	 * Returns a date formatted according to the submitted standard.
 	 *
+	 * As of PHP 5.2, the DateTime extension provides constants that
+	 * serve for the exact same purpose and are used with date().
+	 * Due to that, this function is DEPRECATED and should be removed
+	 * in CodeIgniter 3.1+.
+	 *
+	 * Here are two examples of how you should replace it:
+	 *
+	 *	date(DATE_RFC822, now()); // default
+	 *	date(DATE_W3C, $time); // a different format and time
+	 *
+	 * Reference: http://www.php.net/manual/en/class.datetime.php#datetime.constants.types
+	 *
+	 * @deprecated
 	 * @param	string	the chosen format
 	 * @param	int	Unix timestamp
 	 * @return	string
 	 */
-	function standard_date($fmt = 'DATE_RFC822', $time = '')
+	function standard_date($fmt = 'DATE_RFC822', $time = NULL)
 	{
-		$formats = array(
-						'DATE_ATOM'		=>	'%Y-%m-%dT%H:%i:%s%O',
-						'DATE_COOKIE'	=>	'%l, %d-%M-%y %H:%i:%s UTC',
-						'DATE_ISO8601'	=>	'%Y-%m-%dT%H:%i:%s%O',
-						'DATE_RFC822'	=>	'%D, %d %M %y %H:%i:%s %O',
-						'DATE_RFC850'	=>	'%l, %d-%M-%y %H:%i:%s UTC',
-						'DATE_RFC1036'	=>	'%D, %d %M %y %H:%i:%s %O',
-						'DATE_RFC1123'	=>	'%D, %d %M %Y %H:%i:%s %O',
-						'DATE_RFC2822'	=>	'%D, %d %M %Y %H:%i:%s %O',
-						'DATE_RSS'		=>	'%D, %d %M %Y %H:%i:%s %O',
-						'DATE_W3C'		=>	'%Y-%m-%dT%H:%i:%s%O'
-						);
+		if (empty($time))
+		{
+			$time = now();
+		}
 
-		if ( ! isset($formats[$fmt]))
+		// Procedural style pre-defined constants from the DateTime extension
+		if (strpos($fmt, 'DATE_') !== 0 OR defined($fmt) === FALSE)
 		{
 			return FALSE;
 		}
 
-		return mdate($formats[$fmt], $time);
+		return date(constant($fmt), $time);
 	}
 }
 
@@ -164,20 +171,9 @@
 		$CI =& get_instance();
 		$CI->lang->load('date');
 
-		if ( ! is_numeric($seconds))
-		{
-			$seconds = 1;
-		}
-
-		if ( ! is_numeric($time))
-		{
-			$time = time();
-		}
-
-		if ( ! is_numeric($units))
-		{
-			$units = 7;
-		}
+		is_numeric($seconds) OR $seconds = 1;
+		is_numeric($time) OR $time = time();
+		is_numeric($units) OR $units = 7;
 
 		$seconds = ($time <= $seconds) ? 1 : $time - $seconds;
 
@@ -186,7 +182,7 @@
 
 		if ($years > 0)
 		{
-			$str[] = $years.' '.$CI->lang->line((($years	> 1) ? 'date_years' : 'date_year'));
+			$str[] = $years.' '.$CI->lang->line($years > 1 ? 'date_years' : 'date_year');
 		}
 
 		$seconds -= $years * 31557600;
@@ -196,7 +192,7 @@
 		{
 			if ($months > 0)
 			{
-				$str[] = $months.' '.$CI->lang->line((($months	> 1) ? 'date_months' : 'date_month'));
+				$str[] = $months.' '.$CI->lang->line($months > 1 ? 'date_months' : 'date_month');
 			}
 
 			$seconds -= $months * 2629743;
@@ -208,7 +204,7 @@
 		{
 			if ($weeks > 0)
 			{
-				$str[] = $weeks.' '.$CI->lang->line((($weeks	> 1) ? 'date_weeks' : 'date_week'));
+				$str[] = $weeks.' '.$CI->lang->line($weeks > 1 ? 'date_weeks' : 'date_week');
 			}
 
 			$seconds -= $weeks * 604800;
@@ -220,7 +216,7 @@
 		{
 			if ($days > 0)
 			{
-				$str[] = $days.' '.$CI->lang->line((($days	> 1) ? 'date_days' : 'date_day'));
+				$str[] = $days.' '.$CI->lang->line($days > 1 ? 'date_days' : 'date_day');
 			}
 
 			$seconds -= $days * 86400;
@@ -232,7 +228,7 @@
 		{
 			if ($hours > 0)
 			{
-				$str[] = $hours.' '.$CI->lang->line((($hours	> 1) ? 'date_hours' : 'date_hour'));
+				$str[] = $hours.' '.$CI->lang->line($hours > 1 ? 'date_hours' : 'date_hour');
 			}
 
 			$seconds -= $hours * 3600;
@@ -244,7 +240,7 @@
 		{
 			if ($minutes > 0)
 			{
-				$str[] = $minutes.' '.$CI->lang->line((($minutes	> 1) ? 'date_minutes' : 'date_minute'));
+				$str[] = $minutes.' '.$CI->lang->line($minutes > 1 ? 'date_minutes' : 'date_minute');
 			}
 
 			$seconds -= $minutes * 60;
@@ -252,7 +248,7 @@
 
 		if (count($str) === 0)
 		{
-			$str[] = $seconds.' '.$CI->lang->line((($seconds	> 1) ? 'date_seconds' : 'date_second'));
+			$str[] = $seconds.' '.$CI->lang->line($seconds > 1 ? 'date_seconds' : 'date_second');
 		}
 
 		return implode(', ', $str);
@@ -279,15 +275,19 @@
 		{
 			return 0;
 		}
-
-		if ( ! is_numeric($year) OR strlen($year) != 4)
+		elseif ( ! is_numeric($year) OR strlen($year) !== 4)
 		{
 			$year = date('Y');
 		}
 
+		if ($year >= 1970)
+		{
+			return (int) date('t', mktime(12, 0, 0, $month, 1, $year));
+		}
+
 		if ($month == 2)
 		{
-			if ($year % 400 == 0 OR ($year % 4 == 0 && $year % 100 != 0))
+			if ($year % 400 === 0 OR ($year % 4 === 0 && $year % 100 !== 0))
 			{
 				return 29;
 			}
@@ -310,17 +310,17 @@
 	 */
 	function local_to_gmt($time = '')
 	{
-		if ($time == '')
+		if ($time === '')
 		{
 			$time = time();
 		}
 
 		return mktime(
-			gmdate('H', $time),
+			gmdate('G', $time),
 			gmdate('i', $time),
 			gmdate('s', $time),
-			gmdate('m', $time),
-			gmdate('d', $time),
+			gmdate('n', $time),
+			gmdate('j', $time),
 			gmdate('Y', $time)
 		);
 	}
@@ -344,19 +344,14 @@
 	 */
 	function gmt_to_local($time = '', $timezone = 'UTC', $dst = FALSE)
 	{
-		if ($time == '')
+		if ($time === '')
 		{
 			return now();
 		}
 
 		$time += timezones($timezone) * 3600;
 
-		if ($dst == TRUE)
-		{
-			$time += 3600;
-		}
-
-		return $time;
+		return ($dst === TRUE) ? $time + 3600 : $time;
 	}
 }
 
@@ -376,9 +371,7 @@
 		// since the formatting changed with MySQL 4.1
 		// YYYY-MM-DD HH:MM:SS
 
-		$time = str_replace('-', '', $time);
-		$time = str_replace(':', '', $time);
-		$time = str_replace(' ', '', $time);
+		$time = str_replace(array('-', ':', ' '), '', $time);
 
 		// YYYYMMDDHHMMSS
 		return mktime(
@@ -408,9 +401,9 @@
 	 */
 	function unix_to_human($time = '', $seconds = FALSE, $fmt = 'us')
 	{
-		$r  = date('Y', $time).'-'.date('m', $time).'-'.date('d', $time).' ';
+		$r = date('Y', $time).'-'.date('m', $time).'-'.date('d', $time).' ';
 
-		if ($fmt == 'us')
+		if ($fmt === 'us')
 		{
 			$r .= date('h', $time).':'.date('i', $time);
 		}
@@ -424,9 +417,9 @@
 			$r .= ':'.date('s', $time);
 		}
 
-		if ($fmt == 'us')
+		if ($fmt === 'us')
 		{
-			$r .= ' '.date('A', $time);
+			return $r.' '.date('A', $time);
 		}
 
 		return $r;
@@ -447,58 +440,40 @@
 	 */
 	function human_to_unix($datestr = '')
 	{
-		if ($datestr == '')
+		if ($datestr === '')
 		{
 			return FALSE;
 		}
 
 		$datestr = preg_replace('/\040+/', ' ', trim($datestr));
 
-		if ( ! preg_match('/^[0-9]{2,4}\-[0-9]{1,2}\-[0-9]{1,2}\s[0-9]{1,2}:[0-9]{1,2}(?::[0-9]{1,2})?(?:\s[AP]M)?$/i', $datestr))
+		if ( ! preg_match('/^(\d{2}|\d{4})\-[0-9]{1,2}\-[0-9]{1,2}\s[0-9]{1,2}:[0-9]{1,2}(?::[0-9]{1,2})?(?:\s[AP]M)?$/i', $datestr))
 		{
 			return FALSE;
 		}
 
 		$split = explode(' ', $datestr);
 
-		$ex = explode('-', $split['0']);
-
-		$year  = (strlen($ex[0]) === 2) ? '20'.$ex[0] : $ex[0];
-		$month = (strlen($ex[1]) === 1) ? '0'.$ex[1]  : $ex[1];
-		$day   = (strlen($ex[2]) === 1) ? '0'.$ex[2]  : $ex[2];
+		list($year, $month, $day) = explode('-', $split[0]);
 
 		$ex = explode(':', $split['1']);
 
-		$hour = (strlen($ex[0]) === 1) ? '0'.$ex[0] : $ex[0];
-		$min  = (strlen($ex[1]) === 1) ? '0'.$ex[1] : $ex[1];
+		$hour	= (int) $ex[0];
+		$min	= (int) $ex[1];
+		$sec	= ( ! empty($ex[2]) && preg_match('/[0-9]{1,2}/', $ex[2]))
+				? (int) $ex[2] : 0;
 
-		if (isset($ex['2']) && preg_match('/[0-9]{1,2}/', $ex[2]))
-		{
-			$sec  = (strlen($ex[2]) === 1) ? '0'.$ex[2] : $ex[2];
-		}
-		else
-		{
-			// Unless specified, seconds get set to zero.
-			$sec = '00';
-		}
-
-		if (isset($split['2']))
+		if (isset($split[2]))
 		{
 			$ampm = strtolower($split[2]);
 
-			if (substr($ampm, 0, 1) === 'p' && $hour < 12)
+			if ($ampm[0] === 'p' && $hour < 12)
 			{
 				$hour += 12;
 			}
-
-			if (substr($ampm, 0, 1) === 'a' && $hour == 12)
+			elseif ($ampm[0] === 'a' && $hour === 12)
 			{
-				$hour =  '00';
-			}
-
-			if (strlen($hour) === 1)
-			{
-				$hour = '0'.$hour;
+				$hour = 0;
 			}
 		}
 
@@ -524,9 +499,13 @@
 		{
 			return 'Unknown';
 		}
+		elseif (empty($format))
+		{
+			$format = 'U';
+		}
 
 		// Date like: YYYYMM
-		if (preg_match('/^\d{6}$/', $bad_date))
+		if (preg_match('/^\d{6}$/i', $bad_date))
 		{
 			if (in_array(substr($bad_date, 0, 2), array('19', '20')))
 			{
@@ -543,26 +522,21 @@
 		}
 
 		// Date Like: YYYYMMDD
-		if (preg_match('/^\d{8}$/', $bad_date))
+		if (preg_match('/^(\d{2})\d{2}(\d{4})$/i', $bad_date, $matches))
 		{
-			$month = substr($bad_date, 0, 2);
-			$day   = substr($bad_date, 2, 2);
-			$year  = substr($bad_date, 4, 4);
-
-			return date($format, strtotime($month.'/01/'.$year));
+			return date($format, strtotime($matches[1].'/01/'.$matches[2]));
 		}
 
 		// Date Like: MM-DD-YYYY __or__ M-D-YYYY (or anything in between)
-		if (preg_match('/^\d{1,2}-\d{1,2}-\d{4}$/', $bad_date))
+		if (preg_match('/^(\d{1,2})-(\d{1,2})-(\d{4})$/i', $bad_date, $matches))
 		{
-			list($m, $d, $y) = explode('-', $bad_date);
-			return date($format, strtotime($y.'-'.$m.'-'.$d));
+			return date($format, strtotime($matches[3].'-'.$matches[1].'-'.$matches[2]));
 		}
 
 		// Any other kind of string, when converted into UNIX time,
 		// produces "0 seconds after epoc..." is probably bad...
 		// return "Invalid Date".
-		if (date('U', strtotime($bad_date)) == '0')
+		if (date('U', strtotime($bad_date)) === '0')
 		{
 			return 'Invalid Date';
 		}
@@ -584,27 +558,43 @@
 	 * @param	string	timezone
 	 * @param	string	classname
 	 * @param	string	menu name
+	 * @param	mixed	attributes
 	 * @return	string
 	 */
-	function timezone_menu($default = 'UTC', $class = '', $name = 'timezones')
+	function timezone_menu($default = 'UTC', $class = '', $name = 'timezones', $attributes = '')
 	{
 		$CI =& get_instance();
 		$CI->lang->load('date');
 
-		$default = ($default == 'GMT') ? 'UTC' : $default;
+		$default = ($default === 'GMT') ? 'UTC' : $default;
 
 		$menu = '<select name="'.$name.'"';
 
-		if ($class != '')
+		if ($class !== '')
 		{
 			$menu .= ' class="'.$class.'"';
 		}
 
-		$menu .= ">\n";
+		// Generate a string from the attributes submitted, if any
+		if (is_array($attributes))
+		{
+			$atts = '';
+			foreach ($attributes as $key => $val)
+			{
+				$atts .= ' '.$key.'="'.$val.'"';
+			}
+			$attributes = $atts;
+		}
+		elseif (is_string($attributes) && strlen($attributes) > 0)
+		{
+			$attributes = ' '.$attributes;
+		}
+
+		$menu .= $attributes.">\n";
 
 		foreach (timezones() as $key => $val)
 		{
-			$selected = ($default == $key) ? ' selected="selected"' : '';
+			$selected = ($default === $key) ? ' selected="selected"' : '';
 			$menu .= '<option value="'.$key.'"'.$selected.'>'.$CI->lang->line($key)."</option>\n";
 		}
 
@@ -673,13 +663,11 @@
 			'UP14'		=> +14
 		);
 
-		if ($tz == '')
+		if ($tz === '')
 		{
 			return $zones;
 		}
 
-		$tz = ($tz == 'GMT') ? 'UTC' : $tz;
-
 		return isset($zones[$tz]) ? $zones[$tz] : 0;
 	}
 }
diff --git a/system/helpers/directory_helper.php b/system/helpers/directory_helper.php
index bda8fe8..e7d3b5e 100644
--- a/system/helpers/directory_helper.php
+++ b/system/helpers/directory_helper.php
@@ -62,7 +62,7 @@
 			while (FALSE !== ($file = readdir($fp)))
 			{
 				// Remove '.', '..', and hidden files [optional]
-				if ( ! trim($file, '.') OR ($hidden == FALSE && $file[0] === '.'))
+				if ( ! trim($file, '.') OR ($hidden === FALSE && $file[0] === '.'))
 				{
 					continue;
 				}
diff --git a/system/helpers/download_helper.php b/system/helpers/download_helper.php
index 470b61e..09c4de5 100644
--- a/system/helpers/download_helper.php
+++ b/system/helpers/download_helper.php
@@ -46,12 +46,12 @@
 	 *
 	 * @param	string	filename
 	 * @param	mixed	the data to be downloaded
-	 * @param	bool	wether to try and send the actual file MIME type
+	 * @param	bool	whether to try and send the actual file MIME type
 	 * @return	void
 	 */
 	function force_download($filename = '', $data = '', $set_mime = FALSE)
 	{
-		if ($filename == '' OR $data == '')
+		if ($filename === '' OR $data === '')
 		{
 			return FALSE;
 		}
@@ -73,14 +73,7 @@
 			}
 
 			// Load the mime types
-			if (defined('ENVIRONMENT') && is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
-			{
-				include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
-			}
-			elseif (is_file(APPPATH.'config/mimes.php'))
-			{
-				include(APPPATH.'config/mimes.php');
-			}
+			$mimes =& get_mimes();
 
 			// Only change the default MIME if we can find one
 			if (isset($mimes[$extension]))
diff --git a/system/helpers/email_helper.php b/system/helpers/email_helper.php
index 0516e93..2a63b36 100644
--- a/system/helpers/email_helper.php
+++ b/system/helpers/email_helper.php
@@ -45,9 +45,9 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	function valid_email($address)
+	function valid_email($email)
 	{
-		return (bool) preg_match('/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix', $address);
+		return (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
 	}
 }
 
diff --git a/system/helpers/file_helper.php b/system/helpers/file_helper.php
index fb45034..7270ee3 100644
--- a/system/helpers/file_helper.php
+++ b/system/helpers/file_helper.php
@@ -44,38 +44,16 @@
 	 *
 	 * Opens the file specfied in the path and returns it as a string.
 	 *
+	 * This function is DEPRECATED and should be removed in
+	 * CodeIgniter 3.1+. Use file_get_contents() instead.
+	 *
+	 * @deprecated
 	 * @param	string	path to file
 	 * @return	string
 	 */
 	function read_file($file)
 	{
-		if ( ! file_exists($file))
-		{
-			return FALSE;
-		}
-
-		if (function_exists('file_get_contents'))
-		{
-			return file_get_contents($file);
-		}
-
-		if ( ! $fp = @fopen($file, FOPEN_READ))
-		{
-			return FALSE;
-		}
-
-		flock($fp, LOCK_SH);
-
-		$data = '';
-		if (filesize($file) > 0)
-		{
-			$data =& fread($fp, filesize($file));
-		}
-
-		flock($fp, LOCK_UN);
-		fclose($fp);
-
-		return $data;
+		return @file_get_contents($file);
 	}
 }
 
@@ -154,7 +132,7 @@
 		}
 		@closedir($current_dir);
 
-		if ($del_dir == TRUE && $level > 0)
+		if ($del_dir === TRUE && $level > 0)
 		{
 			return @rmdir($path);
 		}
@@ -199,7 +177,7 @@
 				}
 				elseif ($file[0] !== '.')
 				{
-					$_filedata[] = ($include_path == TRUE) ? $source_dir.$file : $file;
+					$_filedata[] = ($include_path === TRUE) ? $source_dir.$file : $file;
 				}
 			}
 			closedir($fp);
@@ -349,36 +327,23 @@
 	{
 		$extension = strtolower(substr(strrchr($file, '.'), 1));
 
-		global $mimes;
+		static $mimes;
 
 		if ( ! is_array($mimes))
 		{
-			if (defined('ENVIRONMENT') && is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
-			{
-				include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
-			}
-			elseif (is_file(APPPATH.'config/mimes.php'))
-			{
-				include(APPPATH.'config/mimes.php');
-			}
+			$mimes =& get_mimes();
 
-			if ( ! is_array($mimes))
+			if (empty($mimes))
 			{
 				return FALSE;
 			}
 		}
 
-		if (array_key_exists($extension, $mimes))
+		if (isset($mimes[$extension]))
 		{
-			if (is_array($mimes[$extension]))
-			{
-				// Multiple mime types, just give the first one
-				return current($mimes[$extension]);
-			}
-			else
-			{
-				return $mimes[$extension];
-			}
+			return is_array($mimes[$extension])
+				? current($mimes[$extension]) // Multiple mime types, just give the first one
+				: $mimes[$extension];
 		}
 
 		return FALSE;
diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php
index eca6c5f..0c5d550 100644
--- a/system/helpers/form_helper.php
+++ b/system/helpers/form_helper.php
@@ -52,7 +52,7 @@
 	{
 		$CI =& get_instance();
 
-		if ($attributes == '')
+		if ($attributes === '')
 		{
 			$attributes = 'method="post"';
 		}
@@ -62,9 +62,11 @@
 		{
 			$action = $CI->config->site_url($action);
 		}
-
-		// If no action is provided then set to the current url
-		$action OR $action = $CI->config->site_url($CI->uri->uri_string());
+		elseif ( ! $action)
+		{
+			// If no action is provided then set to the current url
+			$action = $CI->config->site_url($CI->uri->uri_string());
+		}
 
 		$form = '<form action="'.$action.'"'._attributes_to_string($attributes, TRUE).">\n";
 
@@ -76,7 +78,7 @@
 
 		if (is_array($hidden) && count($hidden) > 0)
 		{
-			$form .= sprintf('<div style="display:none;">%s</div>', form_hidden($hidden));
+			$form .= '<div style="display:none;">'.form_hidden($hidden).'</div>';
 		}
 
 		return $form;
@@ -325,7 +327,10 @@
 			$selected = array($_POST[$name]);
 		}
 
-		if ($extra != '') $extra = ' '.$extra;
+		if ($extra != '')
+		{
+			$extra = ' '.$extra;
+		}
 
 		$multiple = (count($selected) > 1 && strpos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : '';
 
@@ -506,7 +511,7 @@
 
 		$label = '<label';
 
-		if ($id != '')
+		if ($id !== '')
 		{
 			$label .= ' for="'.$id.'"';
 		}
@@ -540,7 +545,7 @@
 	function form_fieldset($legend_text = '', $attributes = array())
 	{
 		$fieldset = '<fieldset'._attributes_to_string($attributes, FALSE).">\n";
-		if ($legend_text != '')
+		if ($legend_text !== '')
 		{
 			return $fieldset.'<legend>'.$legend_text."</legend>\n";
 		}
@@ -623,7 +628,7 @@
 			return $str;
 		}
 
-		if ($field_name != '')
+		if ($field_name !== '')
 		{
 			$prepped_fields[$field_name] = $field_name;
 		}
@@ -686,7 +691,7 @@
 		{
 			if ( ! isset($_POST[$field]))
 			{
-				if (count($_POST) === 0 && $default == TRUE)
+				if (count($_POST) === 0 && $default === TRUE)
 				{
 					return ' selected="selected"';
 				}
@@ -702,7 +707,7 @@
 					return '';
 				}
 			}
-			elseif (($field == '' OR $value == '') OR ($field != $value))
+			elseif (($field == '' OR $value == '') OR $field !== $value)
 			{
 				return '';
 			}
@@ -737,7 +742,7 @@
 		{
 			if ( ! isset($_POST[$field]))
 			{
-				if (count($_POST) === 0 && $default == TRUE)
+				if (count($_POST) === 0 && $default === TRUE)
 				{
 					return ' checked="checked"';
 				}
@@ -753,7 +758,7 @@
 					return '';
 				}
 			}
-			elseif (($field == '' OR $value == '') OR ($field != $value))
+			elseif (($field == '' OR $value == '') OR $field !== $value)
 			{
 				return '';
 			}
@@ -788,7 +793,7 @@
 		{
 			if ( ! isset($_POST[$field]))
 			{
-				if (count($_POST) === 0 && $default == TRUE)
+				if (count($_POST) === 0 && $default === TRUE)
 				{
 					return ' checked="checked"';
 				}
@@ -806,7 +811,7 @@
 			}
 			else
 			{
-				if (($field == '' OR $value == '') OR ($field != $value))
+				if (($field == '' OR $value == '') OR $field !== $value)
 				{
 					return '';
 				}
@@ -907,7 +912,7 @@
 
 		foreach ($default as $key => $val)
 		{
-			if ($key == 'value')
+			if ($key === 'value')
 			{
 				$val = form_prep($val, $default['name']);
 			}
@@ -936,12 +941,12 @@
 	{
 		if (is_string($attributes) && strlen($attributes) > 0)
 		{
-			if ($formtag == TRUE && strpos($attributes, 'method=') === FALSE)
+			if ($formtag === TRUE && strpos($attributes, 'method=') === FALSE)
 			{
 				$attributes .= ' method="post"';
 			}
 
-			if ($formtag == TRUE && strpos($attributes, 'accept-charset=') === FALSE)
+			if ($formtag === TRUE && strpos($attributes, 'accept-charset=') === FALSE)
 			{
 				$attributes .= ' accept-charset="'.strtolower(config_item('charset')).'"';
 			}
diff --git a/system/helpers/html_helper.php b/system/helpers/html_helper.php
index 92a6db4..68ce702 100644
--- a/system/helpers/html_helper.php
+++ b/system/helpers/html_helper.php
@@ -51,7 +51,7 @@
 	 */
 	function heading($data = '', $h = '1', $attributes = '')
 	{
-		return '<h'.$h.($attributes != '' ? ' ' : '').$attributes.'>'.$data.'</h'.$h.'>';
+		return '<h'.$h.($attributes !== '' ? ' ' : '').$attributes.'>'.$data.'</h'.$h.'>';
 	}
 }
 
@@ -334,12 +334,12 @@
 
 			$link .= 'rel="'.$rel.'" type="'.$type.'" ';
 
-			if ($media != '')
+			if ($media !== '')
 			{
 				$link .= 'media="'.$media.'" ';
 			}
 
-			if ($title != '')
+			if ($title !== '')
 			{
 				$link .= 'title="'.$title.'" ';
 			}
diff --git a/system/helpers/language_helper.php b/system/helpers/language_helper.php
index 34f0307..bd567ed 100644
--- a/system/helpers/language_helper.php
+++ b/system/helpers/language_helper.php
@@ -53,7 +53,7 @@
 		$CI =& get_instance();
 		$line = $CI->lang->line($line);
 
-		if ($id != '')
+		if ($id !== '')
 		{
 			$line = '<label for="'.$id.'">'.$line.'</label>';
 		}
diff --git a/system/helpers/path_helper.php b/system/helpers/path_helper.php
index 1341054..fec4a1a 100644
--- a/system/helpers/path_helper.php
+++ b/system/helpers/path_helper.php
@@ -55,7 +55,7 @@
 		}
 
 		// Resolve the path
-		if (function_exists('realpath') && @realpath($path) !== FALSE)
+		if (@realpath($path) !== FALSE)
 		{
 			$path = realpath($path);
 		}
diff --git a/system/helpers/security_helper.php b/system/helpers/security_helper.php
index 6187a4a..0e8e9f9 100644
--- a/system/helpers/security_helper.php
+++ b/system/helpers/security_helper.php
@@ -77,6 +77,10 @@
 	/**
 	 * Hash encode a string
 	 *
+	 * This function is DEPRECATED and should be removed in
+	 * CodeIgniter 3.1+. Use hash() instead.
+	 *
+	 * @deprecated
 	 * @param	string
 	 * @param	string
 	 * @return	string
@@ -104,7 +108,8 @@
 	 */
 	function strip_image_tags($str)
 	{
-		return preg_replace(array('#<img\s+.*?src\s*=\s*["\'](.+?)["\'].*?\>#', '#<img\s+.*?src\s*=\s*(.+?).*?\>#'), '\\1', $str);
+		$CI =& get_instance();
+		return $CI->security->strip_image_tags($str);
 	}
 }
 
@@ -120,7 +125,7 @@
 	 */
 	function encode_php_tags($str)
 	{
-		return str_replace(array('<?php', '<?PHP', '<?', '?>'),  array('&lt;?php', '&lt;?PHP', '&lt;?', '?&gt;'), $str);
+		return str_replace(array('<?', '?>'),  array('&lt;?', '?&gt;'), $str);
 	}
 }
 
diff --git a/system/helpers/smiley_helper.php b/system/helpers/smiley_helper.php
index 5e6de08..b6b2afc 100644
--- a/system/helpers/smiley_helper.php
+++ b/system/helpers/smiley_helper.php
@@ -55,7 +55,7 @@
 		static $do_setup = TRUE;
 		$r = '';
 
-		if ($alias != '' && ! is_array($alias))
+		if ($alias !== '' && ! is_array($alias))
 		{
 			$alias = array($alias => $field_id);
 		}
@@ -184,7 +184,7 @@
 	 */
 	function parse_smileys($str = '', $image_url = '', $smileys = NULL)
 	{
-		if ($image_url == '' OR ( ! is_array($smileys) && FALSE === ($smileys = _get_smiley_array())))
+		if ($image_url === '' OR ( ! is_array($smileys) && FALSE === ($smileys = _get_smiley_array())))
 		{
 			return $str;
 		}
diff --git a/system/helpers/string_helper.php b/system/helpers/string_helper.php
index 0c47d44..4eee2a2 100644
--- a/system/helpers/string_helper.php
+++ b/system/helpers/string_helper.php
@@ -259,7 +259,7 @@
 	{
 		static $i;
 
-		if (func_num_args() == 0)
+		if (func_num_args() === 0)
 		{
 			$i = 0;
 			return '';
diff --git a/system/helpers/text_helper.php b/system/helpers/text_helper.php
index e4451ae..8a1f01b 100644
--- a/system/helpers/text_helper.php
+++ b/system/helpers/text_helper.php
@@ -51,7 +51,7 @@
 	 */
 	function word_limiter($str, $limit = 100, $end_char = '&#8230;')
 	{
-		if (trim($str) == '')
+		if (trim($str) === '')
 		{
 			return $str;
 		}
@@ -258,7 +258,7 @@
 
 		foreach ($censored as $badword)
 		{
-			if ($replacement != '')
+			if ($replacement !== '')
 			{
 				$str = preg_replace("/({$delim})(".str_replace('\*', '\w*?', preg_quote($badword, '/')).")({$delim})/i", "\\1{$replacement}\\3", $str);
 			}
@@ -338,12 +338,12 @@
 	 */
 	function highlight_phrase($str, $phrase, $tag_open = '<strong>', $tag_close = '</strong>')
 	{
-		if ($str == '')
+		if ($str === '')
 		{
 			return '';
 		}
 
-		if ($phrase != '')
+		if ($phrase !== '')
 		{
 			return preg_replace('/('.preg_quote($phrase, '/').')/i', $tag_open.'\\1'.$tag_close, $str);
 		}
@@ -464,7 +464,7 @@
 
 			// If $temp contains data it means we had to split up an over-length
 			// word into smaller chunks so we'll add it back to our current line
-			if ($temp != '')
+			if ($temp !== '')
 			{
 				$output .= $temp."\n".$line."\n";
 			}
diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php
index 0e933c1..39e6343 100644
--- a/system/helpers/url_helper.php
+++ b/system/helpers/url_helper.php
@@ -158,12 +158,12 @@
 			$site_url = site_url($uri);
 		}
 
-		if ($title == '')
+		if ($title === '')
 		{
 			$title = $site_url;
 		}
 
-		if ($attributes != '')
+		if ($attributes !== '')
 		{
 			$attributes = _parse_attributes($attributes);
 		}
@@ -192,33 +192,40 @@
 		$title = (string) $title;
 		$site_url = preg_match('!^\w+://! i', $uri) ? $uri : site_url($uri);
 
-		if ($title == '')
+		if ($title === '')
 		{
 			$title = $site_url;
 		}
 
 		if ($attributes === FALSE)
 		{
-			return '<a href="javascript:void(0);" onclick="window.open(\''.$site_url."', '_blank');\">".$title.'</a>';
+			return '<a href="'.$site_url.'" onclick="window.open(\''.$site_url."', '_blank'); return false;\">".$title.'</a>';
 		}
 
 		if ( ! is_array($attributes))
 		{
-			$attributes = array();
+			$attributes = array($attributes);
+
+			// Ref: http://www.w3schools.com/jsref/met_win_open.asp
+			$window_name = '_blank';
+		}
+		elseif ( ! empty($attributes['window_name']))
+		{
+			$window_name = $attributes['window_name'];
+			unset($attributes['window_name']);
 		}
 
-		foreach (array('width' => '800', 'height' => '600', 'scrollbars' => 'yes', 'status' => 'yes', 'resizable' => 'yes', 'screenx' => '0', 'screeny' => '0', ) as $key => $val)
+		foreach (array('width' => '800', 'height' => '600', 'scrollbars' => 'yes', 'status' => 'yes', 'resizable' => 'yes', 'screenx' => '0', 'screeny' => '0') as $key => $val)
 		{
 			$atts[$key] = isset($attributes[$key]) ? $attributes[$key] : $val;
 			unset($attributes[$key]);
 		}
 
-		if ($attributes != '')
-		{
-			$attributes = _parse_attributes($attributes);
-		}
+		$attributes = empty($attributes) ? '' : _parse_attributes($attributes);
 
-		return '<a href="javascript:void(0);" onclick="window.open(\''.$site_url."', '_blank', '"._parse_attributes($atts, TRUE)."');\"".$attributes.'>'.$title.'</a>';
+		return '<a href="'.$site_url
+			.'" onclick="window.open(\''.$site_url."', '".$window_name."', '"._parse_attributes($atts, TRUE)."'); return false;\""
+			.$attributes.'>'.$title.'</a>';
 	}
 }
 
@@ -238,7 +245,7 @@
 	{
 		$title = (string) $title;
 
-		if ($title == '')
+		if ($title === '')
 		{
 			$title = $email;
 		}
@@ -265,7 +272,7 @@
 	{
 		$title = (string) $title;
 
-		if ($title == '')
+		if ($title === '')
 		{
 			$title = $email;
 		}
@@ -279,7 +286,7 @@
 
 		$x[] = '"';
 
-		if ($attributes != '')
+		if ($attributes !== '')
 		{
 			if (is_array($attributes))
 			{
@@ -345,7 +352,7 @@
 	for ($i = 0, $c = count($x); $i < $c; $i++) { ?>l[<?php echo $i; ?>]='<?php echo $x[$i]; ?>';<?php } ?>
 
 	for (var i = l.length-1; i >= 0; i=i-1){
-	if (l[i].substring(0, 1) == '|') document.write("&#"+unescape(l[i].substring(1))+";");
+	if (l[i].substring(0, 1) === '|') document.write("&#"+unescape(l[i].substring(1))+";");
 	else document.write(unescape(l[i]));}
 	//]]>
 	</script><?php
@@ -436,7 +443,7 @@
 	 */
 	function prep_url($str = '')
 	{
-		if ($str === 'http://' OR $str == '')
+		if ($str === 'http://' OR $str === '')
 		{
 			return '';
 		}
@@ -519,7 +526,7 @@
 	 * @param	int
 	 * @return	string
 	 */
-	function redirect($uri = '', $method = 'auto', $http_response_code = 302)
+	function redirect($uri = '', $method = 'auto', $code = NULL)
 	{
 		if ( ! preg_match('#^https?://#i', $uri))
 		{
@@ -531,14 +538,22 @@
 		{
 			$method = 'refresh';
 		}
+		elseif ($method !== 'refresh' && (empty($code) OR ! is_numeric($code)))
+		{
+			// Reference: http://en.wikipedia.org/wiki/Post/Redirect/Get
+			$code = (isset($_SERVER['REQUEST_METHOD'], $_SERVER['SERVER_PROTOCOL'])
+					&& $_SERVER['REQUEST_METHOD'] === 'POST'
+					&& $_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.1')
+				? 303 : 302;
+		}
 
-		switch($method)
+		switch ($method)
 		{
 			case 'refresh':
 				header('Refresh:0;url='.$uri);
 				break;
 			default:
-				header('Location: '.$uri, TRUE, $http_response_code);
+				header('Location: '.$uri, TRUE, $code);
 				break;
 		}
 		exit;
@@ -562,13 +577,13 @@
 	{
 		if (is_string($attributes))
 		{
-			return ($attributes != '') ? ' '.$attributes : '';
+			return ($attributes !== '') ? ' '.$attributes : '';
 		}
 
 		$att = '';
 		foreach ($attributes as $key => $val)
 		{
-			if ($javascript == TRUE)
+			if ($javascript === TRUE)
 			{
 				$att .= $key.'='.$val.',';
 			}
@@ -578,7 +593,7 @@
 			}
 		}
 
-		if ($javascript == TRUE && $att != '')
+		if ($javascript === TRUE && $att !== '')
 		{
 			return substr($att, 0, -1);
 		}
diff --git a/system/helpers/xml_helper.php b/system/helpers/xml_helper.php
index 958c633..1431777 100644
--- a/system/helpers/xml_helper.php
+++ b/system/helpers/xml_helper.php
@@ -54,7 +54,7 @@
 		// ampersands won't get messed up
 		$str = preg_replace('/&#(\d+);/', $temp.'\\1;', $str);
 
-		if ($protect_all == TRUE)
+		if ($protect_all === TRUE)
 		{
 			$str = preg_replace('/&(\w+);/', $temp.'\\1;', $str);
 		}
@@ -66,7 +66,7 @@
 		// Decode the temp markers back to entities
 		$str = preg_replace('/'.$temp.'(\d+);/', '&#\\1;', $str);
 
-		if ($protect_all == TRUE)
+		if ($protect_all === TRUE)
 		{
 			return preg_replace('/'.$temp.'(\w+);/', '&\\1;', $str);
 		}
diff --git a/system/language/english/calendar_lang.php b/system/language/english/calendar_lang.php
index 2d477e6..48939d4 100644
--- a/system/language/english/calendar_lang.php
+++ b/system/language/english/calendar_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
diff --git a/system/language/english/date_lang.php b/system/language/english/date_lang.php
index 5335637..38532b7 100644
--- a/system/language/english/date_lang.php
+++ b/system/language/english/date_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
diff --git a/system/language/english/db_lang.php b/system/language/english/db_lang.php
index a135d1a..479cbb1 100644
--- a/system/language/english/db_lang.php
+++ b/system/language/english/db_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
diff --git a/system/language/english/email_lang.php b/system/language/english/email_lang.php
index 23296a2..95a16d1 100644
--- a/system/language/english/email_lang.php
+++ b/system/language/english/email_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
diff --git a/system/language/english/form_validation_lang.php b/system/language/english/form_validation_lang.php
index 6cf0b46..eb4624e 100644
--- a/system/language/english/form_validation_lang.php
+++ b/system/language/english/form_validation_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
diff --git a/system/language/english/ftp_lang.php b/system/language/english/ftp_lang.php
index 4e39e43..d00126b 100644
--- a/system/language/english/ftp_lang.php
+++ b/system/language/english/ftp_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
diff --git a/system/language/english/imglib_lang.php b/system/language/english/imglib_lang.php
index 67ca942..67a36e1 100644
--- a/system/language/english/imglib_lang.php
+++ b/system/language/english/imglib_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
diff --git a/system/language/english/migration_lang.php b/system/language/english/migration_lang.php
index 2085cee..af92066 100644
--- a/system/language/english/migration_lang.php
+++ b/system/language/english/migration_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
@@ -26,7 +26,7 @@
  */
 
 $lang['migration_none_found']			= "No migrations were found.";
-$lang['migration_not_found']			= "This migration could not be found.";
+$lang['migration_not_found']			= "No migration could be found with the version number: %d.";
 $lang['migration_multiple_version']		= "This are multiple migrations with the same version number: %d.";
 $lang['migration_class_doesnt_exist']	= "The migration class \"%s\" could not be found.";
 $lang['migration_missing_up_method']	= "The migration class \"%s\" is missing an 'up' method.";
diff --git a/system/language/english/number_lang.php b/system/language/english/number_lang.php
index 0c19ec6..429c647 100644
--- a/system/language/english/number_lang.php
+++ b/system/language/english/number_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
@@ -25,11 +25,11 @@
  * @filesource
  */
 
-$lang['terabyte_abbr'] = "TB";
-$lang['gigabyte_abbr'] = "GB";
-$lang['megabyte_abbr'] = "MB";
-$lang['kilobyte_abbr'] = "KB";
-$lang['bytes'] = "Bytes";
+$lang['terabyte_abbr'] = 'TB';
+$lang['gigabyte_abbr'] = 'GB';
+$lang['megabyte_abbr'] = 'MB';
+$lang['kilobyte_abbr'] = 'KB';
+$lang['bytes'] = 'Bytes';
 
 /* End of file number_lang.php */
 /* Location: ./system/language/english/number_lang.php */
\ No newline at end of file
diff --git a/system/language/english/profiler_lang.php b/system/language/english/profiler_lang.php
index 11d7912..112527f 100644
--- a/system/language/english/profiler_lang.php
+++ b/system/language/english/profiler_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
diff --git a/system/language/english/unit_test_lang.php b/system/language/english/unit_test_lang.php
index 3a8e144..36e9aca 100644
--- a/system/language/english/unit_test_lang.php
+++ b/system/language/english/unit_test_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
diff --git a/system/language/english/upload_lang.php b/system/language/english/upload_lang.php
index 4fa8394..c3cb9c3 100644
--- a/system/language/english/upload_lang.php
+++ b/system/language/english/upload_lang.php
@@ -5,9 +5,9 @@
  * An open source application development framework for PHP 5.2.4 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
diff --git a/system/libraries/Cache/Cache.php b/system/libraries/Cache/Cache.php
index ba732ee..4395cf4 100644
--- a/system/libraries/Cache/Cache.php
+++ b/system/libraries/Cache/Cache.php
@@ -41,11 +41,12 @@
 	 *
 	 * @var array
 	 */
-	protected $valid_drivers = array(
+	protected $valid_drivers 	= array(
 		'cache_apc',
+		'cache_dummy',
 		'cache_file',
 		'cache_memcached',
-		'cache_dummy',
+		'cache_redis',
 		'cache_wincache'
 	);
 
@@ -68,7 +69,7 @@
 	 *
 	 * @param string
 	 */
-	protected $_backup_driver;
+	protected $_backup_driver = 'dummy';
 
 	/**
 	 * Constructor
@@ -102,6 +103,22 @@
 				$this->_backup_driver = $config['backup'];
 			}
 		}
+
+		// If the specified adapter isn't available, check the backup.
+		if ( ! $this->is_supported($this->_adapter))
+		{
+			if ( ! $this->is_supported($this->_backup_driver))
+			{
+				// Backup isn't supported either. Default to 'Dummy' driver.
+				log_message('error', 'Cache adapter "'.$this->_adapter.'" and backup "'.$this->_backup_driver.'" are both unavailable. Cache is now using "Dummy" adapter.');
+				$this->_adapter = 'dummy';
+			}
+			else
+			{
+				// Backup is supported. Set it to primary.
+				$this->_adapter = $this->_backup_driver;
+			}
+		}
 	}
 
 	// ------------------------------------------------------------------------
@@ -206,26 +223,6 @@
 		return $support[$driver];
 	}
 
-	// ------------------------------------------------------------------------
-
-	/**
-	 * __get()
-	 *
-	 * @param	child
-	 * @return	object
-	 */
-	public function __get($child)
-	{
-		$obj = parent::__get($child);
-
-		if ( ! $this->is_supported($child))
-		{
-			$this->_adapter = $this->_backup_driver;
-		}
-
-		return $obj;
-	}
-
 }
 
 /* End of file Cache.php */
diff --git a/system/libraries/Cache/drivers/Cache_file.php b/system/libraries/Cache/drivers/Cache_file.php
index f0eb8bd..37d77c2 100644
--- a/system/libraries/Cache/drivers/Cache_file.php
+++ b/system/libraries/Cache/drivers/Cache_file.php
@@ -26,7 +26,7 @@
  */
 
 /**
- * CodeIgniter Memcached Caching Class
+ * CodeIgniter File Caching Class
  *
  * @package		CodeIgniter
  * @subpackage	Libraries
@@ -53,7 +53,7 @@
 		$CI =& get_instance();
 		$CI->load->helper('file');
 		$path = $CI->config->item('cache_path');
-		$this->_cache_path = ($path == '') ? APPPATH.'cache/' : $path;
+		$this->_cache_path = ($path === '') ? APPPATH.'cache/' : $path;
 	}
 
 	// ------------------------------------------------------------------------
@@ -71,9 +71,9 @@
 			return FALSE;
 		}
 
-		$data = unserialize(read_file($this->_cache_path.$id));
+		$data = unserialize(file_get_contents($this->_cache_path.$id));
 
-		if (time() >  $data['time'] + $data['ttl'])
+		if ($data['ttl'] > 0 && time() >  $data['time'] + $data['ttl'])
 		{
 			unlink($this->_cache_path.$id);
 			return FALSE;
@@ -165,19 +165,19 @@
 			return FALSE;
 		}
 
-		$data = unserialize(read_file($this->_cache_path.$id));
+		$data = unserialize(file_get_contents($this->_cache_path.$id));
 
 		if (is_array($data))
 		{
 			$mtime = filemtime($this->_cache_path.$id);
 
-			if ( ! isset($data['data']['ttl']))
+			if ( ! isset($data['ttl']))
 			{
 				return FALSE;
 			}
 
 			return array(
-				'expire' => $mtime + $data['data']['ttl'],
+				'expire' => $mtime + $data['ttl'],
 				'mtime'	 => $mtime
 			);
 		}
diff --git a/system/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php
index 1df149c..bf90f61 100644
--- a/system/libraries/Cache/drivers/Cache_memcached.php
+++ b/system/libraries/Cache/drivers/Cache_memcached.php
@@ -212,7 +212,7 @@
 				$cache_server['weight'] = $this->_memcache_conf['default']['default_weight'];
 			}
 
-			if (get_class($this->_memcached) == 'Memcache')
+			if (get_class($this->_memcached) === 'Memcache')
 			{
 				// Third parameter is persistance and defaults to TRUE.
 				$this->_memcached->addServer(
diff --git a/system/libraries/Cache/drivers/Cache_redis.php b/system/libraries/Cache/drivers/Cache_redis.php
new file mode 100644
index 0000000..e4a26b5
--- /dev/null
+++ b/system/libraries/Cache/drivers/Cache_redis.php
@@ -0,0 +1,236 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.2.4 or newer
+ *
+ * NOTICE OF LICENSE
+ *
+ * Licensed under the Open Software License version 3.0
+ *
+ * This source file is subject to the Open Software License (OSL 3.0) that is
+ * bundled with this package in the files license.txt / license.rst.  It is
+ * also available through the world wide web at this URL:
+ * http://opensource.org/licenses/OSL-3.0
+ * If you did not receive a copy of the license and are unable to obtain it
+ * through the world wide web, please send an email to
+ * licensing@ellislab.com so we can send you a copy immediately.
+ *
+ * @package		CodeIgniter
+ * @author		EllisLab Dev Team
+ * @copyright	Copyright (c) 2006 - 2012 EllisLab, Inc.
+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ * @link		http://codeigniter.com
+ * @since		Version 3.0
+ * @filesource
+ */
+
+/**
+ * CodeIgniter Redis Caching Class
+ *
+ * @package	   CodeIgniter
+ * @subpackage Libraries
+ * @category   Core
+ * @author	   Anton Lindqvist <anton@qvister.se>
+ * @link
+ */
+class CI_Cache_redis extends CI_Driver
+{
+	/**
+	 * Default config
+	 *
+	 * @static
+	 * @var	array
+	 */
+	protected static $_default_config = array(
+		'host' => '127.0.0.1',
+		'password' => NULL,
+		'port' => 6379,
+		'timeout' => 0
+	);
+
+	/**
+	 * Redis connection
+	 *
+	 * @var	Redis
+	 */
+	protected $_redis;
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Get cache
+	 *
+	 * @param	string	Cache key identifier
+	 * @return	mixed
+	 */
+	public function get($key)
+	{
+		return $this->_redis->get($key);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Save cache
+	 *
+	 * @param	string	Cache key identifier
+	 * @param	mixed	Data to save
+	 * @param	int	Time to live
+	 * @return	bool
+	 */
+	public function save($key, $value, $ttl = NULL)
+	{
+		return ($ttl)
+			? $this->_redis->setex($key, $ttl, $value)
+			: $this->_redis->set($key, $value);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Delete from cache
+	 *
+	 * @param	string	Cache key
+	 * @return	bool
+	 */
+	public function delete($key)
+	{
+		return ($this->_redis->delete($key) === 1);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Clean cache
+	 *
+	 * @return	bool
+	 * @see		Redis::flushDB()
+	 */
+	public function clean()
+	{
+		return $this->_redis->flushDB();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Get cache driver info
+	 *
+	 * @param	string	Not supported in Redis.
+	 *			Only included in order to offer a
+	 *			consistent cache API.
+	 * @return	array
+	 * @see		Redis::info()
+	 */
+	public function cache_info($type = NULL)
+	{
+		return $this->_redis->info();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Get cache metadata
+	 *
+	 * @param	string	Cache key
+	 * @return	array
+	 */
+	public function get_metadata($key)
+	{
+		$value = $this->get($key);
+
+		if ($value)
+		{
+			return array(
+				'expire' => time() + $this->_redis->ttl($key),
+				'data' => $value
+			);
+		}
+
+		return FALSE;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Check if Redis driver is supported
+	 *
+	 * @return	bool
+	 */
+	public function is_supported()
+	{
+		if (extension_loaded('redis'))
+		{
+			$this->_setup_redis();
+			return TRUE;
+		}
+		else
+		{
+			log_message('error', 'The Redis extension must be loaded to use Redis cache.');
+			return FALSE;
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Setup Redis config and connection
+	 *
+	 * Loads Redis config file if present. Will halt execution
+	 * if a Redis connection can't be established.
+	 *
+	 * @return	bool
+	 * @see		Redis::connect()
+	 */
+	protected function _setup_redis()
+	{
+		$config = array();
+		$CI =& get_instance();
+
+		if ($CI->config->load('redis', TRUE, TRUE))
+		{
+			$config += $CI->config->item('redis');
+		}
+
+		$config = array_merge(self::$_default_config, $config);
+
+		$this->_redis = new Redis();
+
+		try
+		{
+			$this->_redis->connect($config['host'], $config['port'], $config['timeout']);
+		}
+		catch (RedisException $e)
+		{
+			show_error('Redis connection refused. ' . $e->getMessage());
+		}
+
+		if (isset($config['password']))
+		{
+			$this->_redis->auth($config['password']);
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+
+	 * Class destructor
+	 *
+	 * Closes the connection to Redis if present.
+	 *
+	 * @return	void
+	 */
+	public function __destruct()
+	{
+		if ($this->_redis)
+		{
+			$this->_redis->close();
+		}
+	}
+
+}
+
+/* End of file Cache_redis.php */
+/* Location: ./system/libraries/Cache/drivers/Cache_redis.php */
\ No newline at end of file
diff --git a/system/libraries/Calendar.php b/system/libraries/Calendar.php
index 92f372b..a49f171 100644
--- a/system/libraries/Calendar.php
+++ b/system/libraries/Calendar.php
@@ -155,7 +155,7 @@
 	public function generate($year = '', $month = '', $data = array())
 	{
 		// Set and validate the supplied month/year
-		if ($year == '')
+		if (empty($year))
 		{
 			$year  = date('Y', $this->local_time);
 		}
@@ -168,7 +168,7 @@
 			$year = '20'.$year;
 		}
 
-		if ($month == '')
+		if (empty($month))
 		{
 			$month = date('m', $this->local_time);
 		}
@@ -214,7 +214,7 @@
 		$out = $this->temp['table_open']."\n\n".$this->temp['heading_row_start']."\n";
 
 		// "previous" month link
-		if ($this->show_next_prev == TRUE)
+		if ($this->show_next_prev === TRUE)
 		{
 			// Add a trailing slash to the  URL if needed
 			$this->next_prev_url = preg_replace('/(.+?)\/*$/', '\\1/',  $this->next_prev_url);
@@ -224,7 +224,7 @@
 		}
 
 		// Heading containing the month/year
-		$colspan = ($this->show_next_prev == TRUE) ? 5 : 7;
+		$colspan = ($this->show_next_prev === TRUE) ? 5 : 7;
 
 		$this->temp['heading_title_cell'] = str_replace('{colspan}', $colspan,
 								str_replace('{heading}', $this->get_month_name($month).'&nbsp;'.$year, $this->temp['heading_title_cell']));
@@ -232,7 +232,7 @@
 		$out .= $this->temp['heading_title_cell']."\n";
 
 		// "next" month link
-		if ($this->show_next_prev == TRUE)
+		if ($this->show_next_prev === TRUE)
 		{
 			$adjusted_date = $this->adjust_date($month + 1, $year);
 			$out .= str_replace('{next_url}', $this->next_prev_url.$adjusted_date['year'].'/'.$adjusted_date['month'], $this->temp['heading_next_cell']);
@@ -306,7 +306,7 @@
 	 */
 	public function get_month_name($month)
 	{
-		if ($this->month_type == 'short')
+		if ($this->month_type === 'short')
 		{
 			$month_names = array('01' => 'cal_jan', '02' => 'cal_feb', '03' => 'cal_mar', '04' => 'cal_apr', '05' => 'cal_may', '06' => 'cal_jun', '07' => 'cal_jul', '08' => 'cal_aug', '09' => 'cal_sep', '10' => 'cal_oct', '11' => 'cal_nov', '12' => 'cal_dec');
 		}
@@ -333,7 +333,7 @@
 	 */
 	public function get_day_names($day_type = '')
 	{
-		if ($day_type != '')
+		if ($day_type !== '')
 		{
 			$this->day_type = $day_type;
 		}
@@ -421,7 +421,7 @@
 		// Is the year a leap year?
 		if ($month == 2)
 		{
-			if ($year % 400 == 0 OR ($year % 4 == 0 && $year % 100 != 0))
+			if ($year % 400 === 0 OR ($year % 4 === 0 && $year % 100 !== 0))
 			{
 				return 29;
 			}
@@ -480,7 +480,7 @@
 	{
 		$this->temp = $this->default_template();
 
-		if ($this->template == '')
+		if ($this->template === '')
 		{
 			return;
 		}
diff --git a/system/libraries/Cart.php b/system/libraries/Cart.php
index 8270503..c442f88 100644
--- a/system/libraries/Cart.php
+++ b/system/libraries/Cart.php
@@ -520,7 +520,7 @@
 	 */
 	public function format_number($n = '')
 	{
-		return ($n == '') ? '' : number_format( (float) $n, 2, '.', ',');
+		return ($n === '') ? '' : number_format( (float) $n, 2, '.', ',');
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php
index c79698c..d67ee25 100644
--- a/system/libraries/Driver.php
+++ b/system/libraries/Driver.php
@@ -51,7 +51,7 @@
 	 *
 	 * @var string
 	 */
-	protected static $lib_name;
+	protected $lib_name;
 
 	/**
 	 * The first time a child is used it won't exist, so we instantiate it
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index 56d60c8..fdb9be4 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -104,7 +104,7 @@
 		}
 		else
 		{
-			$this->_smtp_auth = ! ($this->smtp_user == '' && $this->smtp_pass == '');
+			$this->_smtp_auth = ! ($this->smtp_user === '' && $this->smtp_pass === '');
 			$this->_safe_mode = (bool) @ini_get('safe_mode');
 		}
 
@@ -139,7 +139,7 @@
 		}
 		$this->clear();
 
-		$this->_smtp_auth = ! ($this->smtp_user == '' && $this->smtp_pass == '');
+		$this->_smtp_auth = ! ($this->smtp_user === '' && $this->smtp_pass === '');
 		$this->_safe_mode = (bool) @ini_get('safe_mode');
 
 		return $this;
@@ -166,8 +166,8 @@
 		$this->_headers		= array();
 		$this->_debug_msg	= array();
 
-		$this->_set_header('User-Agent', $this->useragent);
-		$this->_set_header('Date', $this->_set_date());
+		$this->set_header('User-Agent', $this->useragent);
+		$this->set_header('Date', $this->_set_date());
 
 		if ($clear_attachments !== FALSE)
 		{
@@ -201,7 +201,7 @@
 		}
 
 		// prepare the display name
-		if ($name != '')
+		if ($name !== '')
 		{
 			// only use Q encoding if there are characters that would require it
 			if ( ! preg_match('/[\200-\377]/', $name))
@@ -215,8 +215,8 @@
 			}
 		}
 
-		$this->_set_header('From', $name.' <'.$from.'>');
-		$this->_set_header('Return-Path', '<'.$from.'>');
+		$this->set_header('From', $name.' <'.$from.'>');
+		$this->set_header('Return-Path', '<'.$from.'>');
 
 		return $this;
 	}
@@ -242,17 +242,17 @@
 			$this->validate_email($this->_str_to_array($replyto));
 		}
 
-		if ($name == '')
+		if ($name === '')
 		{
 			$name = $replyto;
 		}
 
-		if (strncmp($name, '"', 1) !== 0)
+		if (strpos($name, '"') !== 0)
 		{
 			$name = '"'.$name.'"';
 		}
 
-		$this->_set_header('Reply-To', $name.' <'.$replyto.'>');
+		$this->set_header('Reply-To', $name.' <'.$replyto.'>');
 		$this->_replyto_flag = TRUE;
 
 		return $this;
@@ -278,7 +278,7 @@
 
 		if ($this->_get_protocol() !== 'mail')
 		{
-			$this->_set_header('To', implode(', ', $to));
+			$this->set_header('To', implode(', ', $to));
 		}
 
 		switch ($this->_get_protocol())
@@ -312,7 +312,7 @@
 			$this->validate_email($cc);
 		}
 
-		$this->_set_header('Cc', implode(', ', $cc));
+		$this->set_header('Cc', implode(', ', $cc));
 
 		if ($this->_get_protocol() === 'smtp')
 		{
@@ -333,7 +333,7 @@
 	 */
 	public function bcc($bcc, $limit = '')
 	{
-		if ($limit != '' && is_numeric($limit))
+		if ($limit !== '' && is_numeric($limit))
 		{
 			$this->bcc_batch_mode = TRUE;
 			$this->bcc_batch_size = $limit;
@@ -352,7 +352,7 @@
 		}
 		else
 		{
-			$this->_set_header('Bcc', implode(', ', $bcc));
+			$this->set_header('Bcc', implode(', ', $bcc));
 		}
 
 		return $this;
@@ -369,7 +369,7 @@
 	public function subject($subject)
 	{
 		$subject = $this->_prep_q_encoding($subject);
-		$this->_set_header('Subject', $subject);
+		$this->set_header('Subject', $subject);
 		return $this;
 	}
 
@@ -424,7 +424,7 @@
 	 * @param	string
 	 * @return	void
 	 */
-	protected function _set_header($header, $value)
+	public function set_header($header, $value)
 	{
 		$this->_headers[$header] = $value;
 	}
@@ -586,7 +586,7 @@
 		$this->protocol = strtolower($this->protocol);
 		in_array($this->protocol, $this->_protocols, TRUE) OR $this->protocol = 'mail';
 
-		if ($return == TRUE)
+		if ($return === TRUE)
 		{
 			return $this->protocol;
 		}
@@ -606,13 +606,13 @@
 
 		foreach ($this->_base_charsets as $charset)
 		{
-			if (strncmp($charset, $this->charset, strlen($charset)) === 0)
+			if (strpos($charset, $this->charset) === 0)
 			{
 				$this->_encoding = '7bit';
 			}
 		}
 
-		if ($return == TRUE)
+		if ($return === TRUE)
 		{
 			return $this->_encoding;
 		}
@@ -629,7 +629,7 @@
 	{
 		if ($this->mailtype === 'html')
 		{
-			return (count($this->_attach_name) == 0) ? 'html' : 'html-attach';
+			return (count($this->_attach_name) === 0) ? 'html' : 'html-attach';
 		}
 		elseif	($this->mailtype === 'text' && count($this->_attach_name) > 0)
 		{
@@ -651,7 +651,7 @@
 	protected function _set_date()
 	{
 		$timezone = date('Z');
-		$operator = (strncmp($timezone, '-', 1) === 0) ? '-' : '+';
+		$operator = ($timezone[0] === '-') ? '-' : '+';
 		$timezone = abs($timezone);
 		$timezone = floor($timezone/3600) * 100 + ($timezone % 3600) / 60;
 
@@ -706,9 +706,9 @@
 	 * @param	string
 	 * @return	bool
 	 */
-	public function valid_email($address)
+	public function valid_email($email)
 	{
-		return (bool) preg_match('/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix', $address);
+		return (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
 	}
 
 	// --------------------------------------------------------------------
@@ -750,7 +750,7 @@
 	 */
 	protected function _get_alt_message()
 	{
-		if ($this->alt_message != '')
+		if ($this->alt_message !== '')
 		{
 			return $this->word_wrap($this->alt_message, '76');
 		}
@@ -778,9 +778,9 @@
 	public function word_wrap($str, $charlim = '')
 	{
 		// Se the character limit
-		if ($charlim == '')
+		if ($charlim === '')
 		{
-			$charlim = ($this->wrapchars == '') ? 76 : $this->wrapchars;
+			$charlim = ($this->wrapchars === '') ? 76 : $this->wrapchars;
 		}
 
 		// Reduce multiple spaces
@@ -838,7 +838,7 @@
 
 			// If $temp contains data it means we had to split up an over-length
 			// word into smaller chunks so we'll add it back to our current line
-			if ($temp != '')
+			if ($temp !== '')
 			{
 				$output .= $temp.$this->newline;
 			}
@@ -867,11 +867,11 @@
 	 */
 	protected function _build_headers()
 	{
-		$this->_set_header('X-Sender', $this->clean_email($this->_headers['From']));
-		$this->_set_header('X-Mailer', $this->useragent);
-		$this->_set_header('X-Priority', $this->_priorities[$this->priority - 1]);
-		$this->_set_header('Message-ID', $this->_get_message_id());
-		$this->_set_header('Mime-Version', '1.0');
+		$this->set_header('X-Sender', $this->clean_email($this->_headers['From']));
+		$this->set_header('X-Mailer', $this->useragent);
+		$this->set_header('X-Priority', $this->_priorities[$this->priority - 1]);
+		$this->set_header('Message-ID', $this->_get_message_id());
+		$this->set_header('Mime-Version', '1.0');
 	}
 
 	// --------------------------------------------------------------------
@@ -896,7 +896,7 @@
 		{
 			$val = trim($val);
 
-			if ($val != '')
+			if ($val !== '')
 			{
 				$this->_header_str .= $key.': '.$val.$this->newline;
 			}
@@ -1043,7 +1043,7 @@
 			$ctype = $this->_attach_type[$i];
 			$file_content = '';
 
-			if ($this->_attach_type[$i] == '')
+			if ($this->_attach_type[$i] === '')
 			{
 				if ( ! file_exists($filename))
 				{
@@ -1099,7 +1099,7 @@
 		// Set the character limit
 		// Don't allow over 76, as that will make servers and MUAs barf
 		// all over quoted-printable data
-		if ($charlim == '' OR $charlim > 76)
+		if ($charlim === '' OR $charlim > 76)
 		{
 			$charlim = 76;
 		}
@@ -1240,7 +1240,7 @@
 	 */
 	public function send()
 	{
-		if ($this->_replyto_flag == FALSE)
+		if ($this->_replyto_flag === FALSE)
 		{
 			$this->reply_to($this->_headers['From']);
 		}
@@ -1284,7 +1284,7 @@
 				$set .= ', '.$this->_bcc_array[$i];
 			}
 
-			if ($i == $float)
+			if ($i === $float)
 			{
 				$chunk[] = substr($set, 1);
 				$float += $this->bcc_batch_size;
@@ -1305,7 +1305,7 @@
 
 			if ($this->protocol !== 'smtp')
 			{
-				$this->_set_header('Bcc', implode(', ', $bcc));
+				$this->set_header('Bcc', implode(', ', $bcc));
 			}
 			else
 			{
@@ -1377,7 +1377,7 @@
 	 */
 	protected function _send_with_mail()
 	{
-		if ($this->_safe_mode == TRUE)
+		if ($this->_safe_mode === TRUE)
 		{
 			return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str);
 		}
@@ -1430,7 +1430,7 @@
 	 */
 	protected function _send_with_smtp()
 	{
-		if ($this->smtp_host == '')
+		if ($this->smtp_host === '')
 		{
 			$this->_set_error_message('lang:email_no_hostname');
 			return FALSE;
@@ -1452,7 +1452,7 @@
 		{
 			foreach ($this->_cc_array as $val)
 			{
-				if ($val != '')
+				if ($val !== '')
 				{
 					$this->_send_command('to', $val);
 				}
@@ -1463,7 +1463,7 @@
 		{
 			foreach ($this->_bcc_array as $val)
 			{
-				if ($val != '')
+				if ($val !== '')
 				{
 					$this->_send_command('to', $val);
 				}
@@ -1481,7 +1481,7 @@
 
 		$this->_set_error_message($reply);
 
-		if (strncmp($reply, '250', 3) !== 0)
+		if (strpos($reply, '250') !== 0)
 		{
 			$this->_set_error_message('lang:email_smtp_error', $reply);
 			return FALSE;
@@ -1501,7 +1501,7 @@
 	 */
 	protected function _smtp_connect()
 	{
-		$ssl = ($this->smtp_crypto == 'ssl') ? 'ssl://' : NULL;
+		$ssl = ($this->smtp_crypto === 'ssl') ? 'ssl://' : NULL;
 
 		$this->_smtp_connect = fsockopen($ssl.$this->smtp_host,
 							$this->smtp_port,
@@ -1517,7 +1517,7 @@
 
 		$this->_set_error_message($this->_get_smtp_data());
 
-		if ($this->smtp_crypto == 'tls')
+		if ($this->smtp_crypto === 'tls')
 		{
 			$this->_send_command('hello');
 			$this->_send_command('starttls');
@@ -1599,13 +1599,13 @@
 
 		$this->_debug_msg[] = '<pre>'.$cmd.': '.$reply.'</pre>';
 
-		if (substr($reply, 0, 3) != $resp)
+		if ( (int) substr($reply, 0, 3) !== $resp)
 		{
 			$this->_set_error_message('lang:email_smtp_error', $reply);
 			return FALSE;
 		}
 
-		if ($cmd == 'quit')
+		if ($cmd === 'quit')
 		{
 			fclose($this->_smtp_connect);
 		}
@@ -1627,7 +1627,7 @@
 			return TRUE;
 		}
 
-		if ($this->smtp_user == '' && $this->smtp_pass == '')
+		if ($this->smtp_user === '' && $this->smtp_pass === '')
 		{
 			$this->_set_error_message('lang:email_no_smtp_unpw');
 			return FALSE;
@@ -1637,7 +1637,7 @@
 
 		$reply = $this->_get_smtp_data();
 
-		if (strncmp($reply, '334', 3) !== 0)
+		if (strpos($reply, '334') !== 0)
 		{
 			$this->_set_error_message('lang:email_failed_smtp_login', $reply);
 			return FALSE;
@@ -1647,7 +1647,7 @@
 
 		$reply = $this->_get_smtp_data();
 
-		if (strncmp($reply, '334', 3) !== 0)
+		if (strpos($reply, '334') !== 0)
 		{
 			$this->_set_error_message('lang:email_smtp_auth_un', $reply);
 			return FALSE;
@@ -1657,7 +1657,7 @@
 
 		$reply = $this->_get_smtp_data();
 
-		if (strncmp($reply, '235', 3) !== 0)
+		if (strpos($reply, '235') !== 0)
 		{
 			$this->_set_error_message('lang:email_smtp_auth_pw', $reply);
 			return FALSE;
@@ -1699,7 +1699,7 @@
 		{
 			$data .= $str;
 
-			if ($str[3] == ' ')
+			if ($str[3] === ' ')
 			{
 				break;
 			}
@@ -1816,98 +1816,23 @@
 	 */
 	protected function _mime_types($ext = '')
 	{
-		$mimes = array(
-						'hqx'	=>	'application/mac-binhex40',
-						'cpt'	=>	'application/mac-compactpro',
-						'doc'	=>	'application/msword',
-						'bin'	=>	'application/macbinary',
-						'dms'	=>	'application/octet-stream',
-						'lha'	=>	'application/octet-stream',
-						'lzh'	=>	'application/octet-stream',
-						'exe'	=>	'application/octet-stream',
-						'class'	=>	'application/octet-stream',
-						'psd'	=>	'application/octet-stream',
-						'so'	=>	'application/octet-stream',
-						'sea'	=>	'application/octet-stream',
-						'dll'	=>	'application/octet-stream',
-						'oda'	=>	'application/oda',
-						'pdf'	=>	'application/pdf',
-						'ai'	=>	'application/postscript',
-						'eps'	=>	'application/postscript',
-						'ps'	=>	'application/postscript',
-						'smi'	=>	'application/smil',
-						'smil'	=>	'application/smil',
-						'mif'	=>	'application/vnd.mif',
-						'xls'	=>	'application/vnd.ms-excel',
-						'ppt'	=>	'application/vnd.ms-powerpoint',
-						'wbxml'	=>	'application/vnd.wap.wbxml',
-						'wmlc'	=>	'application/vnd.wap.wmlc',
-						'dcr'	=>	'application/x-director',
-						'dir'	=>	'application/x-director',
-						'dxr'	=>	'application/x-director',
-						'dvi'	=>	'application/x-dvi',
-						'gtar'	=>	'application/x-gtar',
-						'php'	=>	'application/x-httpd-php',
-						'php4'	=>	'application/x-httpd-php',
-						'php3'	=>	'application/x-httpd-php',
-						'phtml'	=>	'application/x-httpd-php',
-						'phps'	=>	'application/x-httpd-php-source',
-						'js'	=>	'application/x-javascript',
-						'swf'	=>	'application/x-shockwave-flash',
-						'sit'	=>	'application/x-stuffit',
-						'tar'	=>	'application/x-tar',
-						'tgz'	=>	'application/x-tar',
-						'xhtml'	=>	'application/xhtml+xml',
-						'xht'	=>	'application/xhtml+xml',
-						'zip'	=>	'application/zip',
-						'mid'	=>	'audio/midi',
-						'midi'	=>	'audio/midi',
-						'mpga'	=>	'audio/mpeg',
-						'mp2'	=>	'audio/mpeg',
-						'mp3'	=>	'audio/mpeg',
-						'aif'	=>	'audio/x-aiff',
-						'aiff'	=>	'audio/x-aiff',
-						'aifc'	=>	'audio/x-aiff',
-						'ram'	=>	'audio/x-pn-realaudio',
-						'rm'	=>	'audio/x-pn-realaudio',
-						'rpm'	=>	'audio/x-pn-realaudio-plugin',
-						'ra'	=>	'audio/x-realaudio',
-						'rv'	=>	'video/vnd.rn-realvideo',
-						'wav'	=>	'audio/x-wav',
-						'bmp'	=>	'image/bmp',
-						'gif'	=>	'image/gif',
-						'jpeg'	=>	'image/jpeg',
-						'jpg'	=>	'image/jpeg',
-						'jpe'	=>	'image/jpeg',
-						'png'	=>	'image/png',
-						'tiff'	=>	'image/tiff',
-						'tif'	=>	'image/tiff',
-						'css'	=>	'text/css',
-						'ics'	=>	'text/calendar',
-						'html'	=>	'text/html',
-						'htm'	=>	'text/html',
-						'shtml'	=>	'text/html',
-						'txt'	=>	'text/plain',
-						'text'	=>	'text/plain',
-						'log'	=>	'text/plain',
-						'rtx'	=>	'text/richtext',
-						'rtf'	=>	'text/rtf',
-						'xml'	=>	'text/xml',
-						'xsl'	=>	'text/xml',
-						'mpeg'	=>	'video/mpeg',
-						'mpg'	=>	'video/mpeg',
-						'mpe'	=>	'video/mpeg',
-						'qt'	=>	'video/quicktime',
-						'mov'	=>	'video/quicktime',
-						'avi'	=>	'video/x-msvideo',
-						'movie'	=>	'video/x-sgi-movie',
-						'doc'	=>	'application/msword',
-						'word'	=>	'application/msword',
-						'xl'	=>	'application/excel',
-						'eml'	=>	'message/rfc822'
-					);
+		static $mimes;
 
-		return isset($mimes[strtolower($ext)]) ? $mimes[strtolower($ext)] : 'application/x-unknown-content-type';
+		$ext = strtolower($ext);
+
+		if ( ! is_array($mimes))
+		{
+			$mimes =& get_mimes();
+		}
+
+		if (isset($mimes[$ext]))
+		{
+			return is_array($mimes[$ext])
+				? current($mimes[$ext])
+				: $mimes[$ext];
+		}
+
+		return 'application/x-unknown-content-type';
 	}
 
 }
diff --git a/system/libraries/Encrypt.php b/system/libraries/Encrypt.php
index 751557f..8ffd93a 100644
--- a/system/libraries/Encrypt.php
+++ b/system/libraries/Encrypt.php
@@ -97,15 +97,14 @@
 	 */
 	public function get_key($key = '')
 	{
-		if ($key == '')
+		if ($key === '')
 		{
-			if ($this->encryption_key != '')
+			if ($this->encryption_key !== '')
 			{
 				return $this->encryption_key;
 			}
 
-			$CI =& get_instance();
-			$key = $CI->config->item('encryption_key');
+			$key = config_item('encryption_key');
 
 			if ($key === FALSE)
 			{
@@ -214,6 +213,7 @@
 		$dec = base64_decode($string);
 		if (($dec = $this->mcrypt_decode($dec, $key)) === FALSE)
 		{
+			$this->set_mode($current_mode);
 			return FALSE;
 		}
 
@@ -449,7 +449,7 @@
 	 */
 	protected function _get_cipher()
 	{
-		if ($this->_mcrypt_cipher == '')
+		if ($this->_mcrypt_cipher === NULL)
 		{
 			return $this->_mcrypt_cipher = MCRYPT_RIJNDAEL_256;
 		}
@@ -466,7 +466,7 @@
 	 */
 	protected function _get_mode()
 	{
-		if ($this->_mcrypt_mode == '')
+		if ($this->_mcrypt_mode === NULL)
 		{
 			return $this->_mcrypt_mode = MCRYPT_MODE_CBC;
 		}
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php
index 67cbfd1..b490a34 100644
--- a/system/libraries/Form_validation.php
+++ b/system/libraries/Form_validation.php
@@ -187,14 +187,20 @@
 			return $this;
 		}
 
+		// Convert an array of rules to a string
+		if (is_array($rules))
+		{
+			$rules = implode('|', $rules);
+		}
+
 		// No fields? Nothing to do...
-		if ( ! is_string($field) OR ! is_string($rules) OR $field == '')
+		if ( ! is_string($field) OR ! is_string($rules) OR $field === '')
 		{
 			return $this;
 		}
 
 		// If the field label wasn't passed we use the field name
-		$label = ($label == '') ? $field : $label;
+		$label = ($label === '') ? $field : $label;
 
 		// 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
@@ -207,7 +213,7 @@
 
 			for ($i = 0, $c = count($matches[0]); $i < $c; $i++)
 			{
-				if ($matches[1][$i] != '')
+				if ($matches[1][$i] !== '')
 				{
 					$indexes[] = $matches[1][$i];
 				}
@@ -318,12 +324,12 @@
 			return '';
 		}
 
-		if ($prefix == '')
+		if ($prefix === '')
 		{
 			$prefix = $this->_error_prefix;
 		}
 
-		if ($suffix == '')
+		if ($suffix === '')
 		{
 			$suffix = $this->_error_suffix;
 		}
@@ -364,12 +370,12 @@
 			return '';
 		}
 
-		if ($prefix == '')
+		if ($prefix === '')
 		{
 			$prefix = $this->_error_prefix;
 		}
 
-		if ($suffix == '')
+		if ($suffix === '')
 		{
 			$suffix = $this->_error_suffix;
 		}
@@ -378,7 +384,7 @@
 		$str = '';
 		foreach ($this->_error_array as $val)
 		{
-			if ($val != '')
+			if ($val !== '')
 			{
 				$str .= $prefix.$val.$suffix."\n";
 			}
@@ -417,9 +423,9 @@
 			}
 
 			// Is there a validation rule for the particular URI being accessed?
-			$uri = ($group == '') ? trim($this->CI->uri->ruri_string(), '/') : $group;
+			$uri = ($group === '') ? trim($this->CI->uri->ruri_string(), '/') : $group;
 
-			if ($uri != '' && isset($this->_config_rules[$uri]))
+			if ($uri !== '' && isset($this->_config_rules[$uri]))
 			{
 				$this->set_rules($this->_config_rules[$uri]);
 			}
@@ -454,6 +460,12 @@
 				$this->_field_data[$field]['postdata'] = $validation_array[$field];
 			}
 
+			// Don't try to validate if we have no rules set
+			if (empty($row['rules']))
+			{
+				continue;
+			}
+
 			$this->_execute($row, explode('|', $row['rules']), $this->_field_data[$field]['postdata']);
 		}
 
@@ -565,8 +577,7 @@
 		{
 			foreach ($postdata as $key => $val)
 			{
-				$this->_execute($row, $rules, $val, $cycles);
-				$cycles++;
+				$this->_execute($row, $rules, $val, $key);
 			}
 
 			return;
@@ -629,7 +640,7 @@
 
 			// We set the $postdata variable with the current data in our master array so that
 			// each cycle of the loop is dealing with the processed data from the last cycle
-			if ($row['is_array'] == TRUE && is_array($this->_field_data[$row['field']]['postdata']))
+			if ($row['is_array'] === TRUE && is_array($this->_field_data[$row['field']]['postdata']))
 			{
 				// We shouldn't need this safety, but just in case there isn't an array index
 				// associated with this cycle we'll bail out
@@ -643,7 +654,12 @@
 			}
 			else
 			{
-				$postdata = $this->_field_data[$row['field']]['postdata'];
+				// If we get an array field, but it's not expected - then it is most likely
+				// somebody messing with the form on the client side, so we'll just consider
+				// it an empty field
+				$postdata = is_array($this->_field_data[$row['field']]['postdata'])
+						? NULL
+						: $this->_field_data[$row['field']]['postdata'];
 			}
 
 			// Is the rule a callback?
@@ -852,7 +868,7 @@
 				return '';
 			}
 		}
-		elseif (($field == '' OR $value == '') OR ($field != $value))
+		elseif (($field === '' OR $value === '') OR ($field !== $value))
 		{
 			return '';
 		}
@@ -888,7 +904,7 @@
 				return '';
 			}
 		}
-		elseif (($field == '' OR $value == '') OR ($field != $value))
+		elseif (($field === '' OR $value === '') OR ($field !== $value))
 		{
 			return '';
 		}
@@ -987,15 +1003,19 @@
 	 * Minimum Length
 	 *
 	 * @param	string
-	 * @param	int
+	 * @param	string
 	 * @return	bool
 	 */
 	public function min_length($str, $val)
 	{
-		if (preg_match('/[^0-9]/', $val))
+		if ( ! is_numeric($val))
 		{
 			return FALSE;
 		}
+		else
+		{
+			$val = (int) $val;
+		}
 
 		return (MB_ENABLED === TRUE)
 			? ($val <= mb_strlen($str))
@@ -1008,15 +1028,19 @@
 	 * Max Length
 	 *
 	 * @param	string
-	 * @param	int
+	 * @param	string
 	 * @return	bool
 	 */
 	public function max_length($str, $val)
 	{
-		if (preg_match('/[^0-9]/', $val))
+		if ( ! is_numeric($val))
 		{
 			return FALSE;
 		}
+		else
+		{
+			$val = (int) $val;
+		}
 
 		return (MB_ENABLED === TRUE)
 			? ($val >= mb_strlen($str))
@@ -1029,19 +1053,23 @@
 	 * Exact Length
 	 *
 	 * @param	string
-	 * @param	int
+	 * @param	string
 	 * @return	bool
 	 */
 	public function exact_length($str, $val)
 	{
-		if (preg_match('/[^0-9]/', $val))
+		if ( ! is_numeric($val))
 		{
 			return FALSE;
 		}
+		else
+		{
+			$val = (int) $val;
+		}
 
 		return (MB_ENABLED === TRUE)
-			? (mb_strlen($str) == $val)
-			: (strlen($str) == $val);
+			? (mb_strlen($str) === $val)
+			: (strlen($str) === $val);
 	}
 
 	// --------------------------------------------------------------------
@@ -1054,7 +1082,7 @@
 	 */
 	public function valid_email($str)
 	{
-		return (bool) preg_match('/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix', $str);
+		return (bool) filter_var($str, FILTER_VALIDATE_EMAIL);
 	}
 
 	// --------------------------------------------------------------------
@@ -1089,11 +1117,12 @@
 	 * Validate IP Address
 	 *
 	 * @param	string
+	 * @param	string	'ipv4' or 'ipv6' to validate a specific IP format
 	 * @return	bool
 	 */
-	public function valid_ip($ip)
+	public function valid_ip($ip, $which = '')
 	{
-		return $this->CI->input->valid_ip($ip);
+		return $this->CI->input->valid_ip($ip, $which);
 	}
 
 	// --------------------------------------------------------------------
@@ -1106,7 +1135,7 @@
 	 */
 	public function alpha($str)
 	{
-		return (bool) preg_match('/^[a-z]+$/i', $str);
+		return ctype_alpha($str);
 	}
 
 	// --------------------------------------------------------------------
@@ -1119,7 +1148,7 @@
 	 */
 	public function alpha_numeric($str)
 	{
-		return (bool) preg_match('/^[a-z0-9]+$/i', $str);
+		return ctype_alnum((string) $str);
 	}
 
 	// --------------------------------------------------------------------
@@ -1241,7 +1270,7 @@
 	 */
 	public function is_natural($str)
 	{
-		return (bool) preg_match('/^[0-9]+$/', $str);
+		return ctype_digit((string) $str);
 	}
 
 	// --------------------------------------------------------------------
@@ -1254,7 +1283,7 @@
 	 */
 	public function is_natural_no_zero($str)
 	{
-		return ($str != 0 && preg_match('/^[0-9]+$/', $str));
+		return ($str != 0 && ctype_digit((string) $str));
 	}
 
 	// --------------------------------------------------------------------
@@ -1296,7 +1325,7 @@
 			return $data;
 		}
 
-		if ($this->_safe_form_data == FALSE OR $data === '')
+		if ($this->_safe_form_data === FALSE OR $data === '')
 		{
 			return $data;
 		}
@@ -1314,7 +1343,7 @@
 	 */
 	public function prep_url($str = '')
 	{
-		if ($str === 'http://' OR $str == '')
+		if ($str === 'http://' OR $str === '')
 		{
 			return '';
 		}
@@ -1337,7 +1366,7 @@
 	 */
 	public function strip_image_tags($str)
 	{
-		return $this->CI->input->strip_image_tags($str);
+		return $this->CI->security->strip_image_tags($str);
 	}
 
 	// --------------------------------------------------------------------
@@ -1363,7 +1392,7 @@
 	 */
 	public function encode_php_tags($str)
 	{
-		return str_replace(array('<?php', '<?PHP', '<?', '?>'),  array('&lt;?php', '&lt;?PHP', '&lt;?', '?&gt;'), $str);
+		return str_replace(array('<?', '?>'),  array('&lt;?', '?&gt;'), $str);
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/libraries/Ftp.php b/system/libraries/Ftp.php
index 3cfe1b2..76f5e15 100644
--- a/system/libraries/Ftp.php
+++ b/system/libraries/Ftp.php
@@ -93,7 +93,7 @@
 
 		if (FALSE === ($this->conn_id = @ftp_connect($this->hostname, $this->port)))
 		{
-			if ($this->debug == TRUE)
+			if ($this->debug === TRUE)
 			{
 				$this->_error('ftp_unable_to_connect');
 			}
@@ -102,7 +102,7 @@
 
 		if ( ! $this->_login())
 		{
-			if ($this->debug == TRUE)
+			if ($this->debug === TRUE)
 			{
 				$this->_error('ftp_unable_to_login');
 			}
@@ -110,7 +110,7 @@
 		}
 
 		// Set passive mode if needed
-		if ($this->passive == TRUE)
+		if ($this->passive === TRUE)
 		{
 			ftp_pasv($this->conn_id, TRUE);
 		}
@@ -141,7 +141,7 @@
 	{
 		if ( ! is_resource($this->conn_id))
 		{
-			if ($this->debug == TRUE)
+			if ($this->debug === TRUE)
 			{
 				$this->_error('ftp_no_connection');
 			}
@@ -167,7 +167,7 @@
 	 */
 	public function changedir($path = '', $supress_debug = FALSE)
 	{
-		if ($path == '' OR ! $this->_is_conn())
+		if ($path === '' OR ! $this->_is_conn())
 		{
 			return FALSE;
 		}
@@ -176,7 +176,7 @@
 
 		if ($result === FALSE)
 		{
-			if ($this->debug == TRUE && $supress_debug == FALSE)
+			if ($this->debug === TRUE && $supress_debug === FALSE)
 			{
 				$this->_error('ftp_unable_to_changedir');
 			}
@@ -197,7 +197,7 @@
 	 */
 	public function mkdir($path = '', $permissions = NULL)
 	{
-		if ($path == '' OR ! $this->_is_conn())
+		if ($path === '' OR ! $this->_is_conn())
 		{
 			return FALSE;
 		}
@@ -206,7 +206,7 @@
 
 		if ($result === FALSE)
 		{
-			if ($this->debug == TRUE)
+			if ($this->debug === TRUE)
 			{
 				$this->_error('ftp_unable_to_makdir');
 			}
@@ -260,7 +260,7 @@
 
 		if ($result === FALSE)
 		{
-			if ($this->debug == TRUE)
+			if ($this->debug === TRUE)
 			{
 				$this->_error('ftp_unable_to_upload');
 			}
@@ -307,7 +307,7 @@
 
 		if ($result === FALSE)
 		{
-			if ($this->debug == TRUE)
+			if ($this->debug === TRUE)
 			{
 				$this->_error('ftp_unable_to_download');
 			}
@@ -338,9 +338,9 @@
 
 		if ($result === FALSE)
 		{
-			if ($this->debug == TRUE)
+			if ($this->debug === TRUE)
 			{
-				$this->_error('ftp_unable_to_' . ($move == FALSE ? 'rename' : 'move'));
+				$this->_error('ftp_unable_to_' . ($move === FALSE ? 'rename' : 'move'));
 			}
 			return FALSE;
 		}
@@ -381,7 +381,7 @@
 
 		if ($result === FALSE)
 		{
-			if ($this->debug == TRUE)
+			if ($this->debug === TRUE)
 			{
 				$this->_error('ftp_unable_to_delete');
 			}
@@ -429,7 +429,7 @@
 
 		if ($result === FALSE)
 		{
-			if ($this->debug == TRUE)
+			if ($this->debug === TRUE)
 			{
 				$this->_error('ftp_unable_to_delete');
 			}
@@ -445,7 +445,7 @@
 	 * Set file permissions
 	 *
 	 * @param	string	the file path
-	 * @param	string	the permissions
+	 * @param	int	the permissions
 	 * @return	bool
 	 */
 	public function chmod($path, $perm)
@@ -459,7 +459,7 @@
 
 		if ($result === FALSE)
 		{
-			if ($this->debug == TRUE)
+			if ($this->debug === TRUE)
 			{
 				$this->_error('ftp_unable_to_chmod');
 			}
diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php
index 0cb1894..899b995 100644
--- a/system/libraries/Image_lib.php
+++ b/system/libraries/Image_lib.php
@@ -466,7 +466,7 @@
 		}
 
 		// Is there a source image? If not, there's no reason to continue
-		if ($this->source_image == '')
+		if ($this->source_image === '')
 		{
 			$this->set_error('imglib_source_image_required');
 			return FALSE;
@@ -519,7 +519,7 @@
 		 * it means we are altering the original. We'll
 		 * set the destination filename and path accordingly.
 		 */
-		if ($this->new_image == '')
+		if ($this->new_image === '')
 		{
 			$this->dest_image = $this->source_image;
 			$this->dest_folder = $this->source_folder;
@@ -562,7 +562,7 @@
 		 * We'll also split the destination image name
 		 * so we can insert the thumbnail marker if needed.
 		 */
-		if ($this->create_thumb === FALSE OR $this->thumb_marker == '')
+		if ($this->create_thumb === FALSE OR $this->thumb_marker === '')
 		{
 			$this->thumb_marker = '';
 		}
@@ -581,7 +581,7 @@
 		 * might not be in correct proportion with the source
 		 * image's width/height. We'll recalculate it here.
 		 */
-		if ($this->maintain_ratio === TRUE && ($this->width != 0 OR $this->height != 0))
+		if ($this->maintain_ratio === TRUE && ($this->width !== 0 OR $this->height !== 0))
 		{
 			$this->image_reproportion();
 		}
@@ -591,12 +591,12 @@
 		 * If the destination width/height was not submitted we
 		 * will use the values from the actual file
 		 */
-		if ($this->width == '')
+		if ($this->width === '')
 		{
 			$this->width = $this->orig_width;
 		}
 
-		if ($this->height == '')
+		if ($this->height === '')
 		{
 			$this->height = $this->orig_height;
 		}
@@ -604,31 +604,31 @@
 		// 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 ! preg_match('/^[0-9]+$/', $this->quality))
 		{
 			$this->quality = 90;
 		}
 
 		// Set the x/y coordinates
-		$this->x_axis = ($this->x_axis == '' OR ! preg_match('/^[0-9]+$/', $this->x_axis)) ? 0 : $this->x_axis;
-		$this->y_axis = ($this->y_axis == '' OR ! preg_match('/^[0-9]+$/', $this->y_axis)) ? 0 : $this->y_axis;
+		is_numeric($this->x_axis) OR $this->x_axis = 0;
+		is_numeric($this->y_axis) OR $this->y_axis = 0;
 
 		// Watermark-related Stuff...
-		if ($this->wm_overlay_path != '')
+		if ($this->wm_overlay_path !== '')
 		{
 			$this->wm_overlay_path = str_replace('\\', '/', realpath($this->wm_overlay_path));
 		}
 
-		if ($this->wm_shadow_color != '')
+		if ($this->wm_shadow_color !== '')
 		{
 			$this->wm_use_drop_shadow = TRUE;
 		}
-		elseif ($this->wm_use_drop_shadow == TRUE && $this->wm_shadow_color == '')
+		elseif ($this->wm_use_drop_shadow === TRUE && $this->wm_shadow_color === '')
 		{
 			$this->wm_use_drop_shadow = FALSE;
 		}
 
-		if ($this->wm_font_path != '')
+		if ($this->wm_font_path !== '')
 		{
 			$this->wm_use_truetype = TRUE;
 		}
@@ -683,14 +683,14 @@
 		// Allowed rotation values
 		$degs = array(90, 180, 270, 'vrt', 'hor');
 
-		if ($this->rotation_angle == '' OR ! in_array($this->rotation_angle, $degs))
+		if ($this->rotation_angle === '' OR ! in_array($this->rotation_angle, $degs))
 		{
 			$this->set_error('imglib_rotation_angle_required');
 			return FALSE;
 		}
 
 		// Reassign the width and height
-		if ($this->rotation_angle == 90 OR $this->rotation_angle == 270)
+		if ($this->rotation_angle === 90 OR $this->rotation_angle === 270)
 		{
 			$this->width	= $this->orig_height;
 			$this->height	= $this->orig_width;
@@ -729,9 +729,9 @@
 
 		// If the target width/height match the source, AND if the new file name is not equal to the old file name
 		// we'll simply make a copy of the original with the new name... assuming dynamic rendering is off.
-		if ($this->dynamic_output === FALSE && $this->orig_width == $this->width && $this->orig_height == $this->height)
+		if ($this->dynamic_output === FALSE && $this->orig_width === $this->width && $this->orig_height === $this->height)
 		{
-			if ($this->source_image != $this->new_image && @copy($this->full_src_path, $this->full_dst_path))
+			if ($this->source_image !== $this->new_image && @copy($this->full_src_path, $this->full_dst_path))
 			{
 				@chmod($this->full_dst_path, FILE_WRITE_MODE);
 			}
@@ -740,7 +740,7 @@
 		}
 
 		// Let's set up our values based on the action
-		if ($action == 'crop')
+		if ($action === 'crop')
 		{
 			// Reassign the source width/height if cropping
 			$this->orig_width  = $this->width;
@@ -750,7 +750,7 @@
 			if ($this->gd_version() !== FALSE)
 			{
 				$gd_version = str_replace('0', '', $this->gd_version());
-				$v2_override = ($gd_version == 2) ? TRUE : FALSE;
+				$v2_override = ($gd_version === 2);
 			}
 		}
 		else
@@ -772,7 +772,7 @@
 		 * it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment
 		 * below should that ever prove inaccurate.
 		 *
-		 * if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor') && $v2_override == FALSE)
+		 * if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor') && $v2_override === FALSE)
 		 */
 		if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor'))
 		{
@@ -787,7 +787,7 @@
 
 		$dst_img = $create($this->width, $this->height);
 
-		if ($this->image_type == 3) // png we can actually preserve transparency
+		if ($this->image_type === 3) // png we can actually preserve transparency
 		{
 			imagealphablending($dst_img, FALSE);
 			imagesavealpha($dst_img, TRUE);
@@ -796,7 +796,7 @@
 		$copy($dst_img, $src_img, 0, 0, $this->x_axis, $this->y_axis, $this->width, $this->height, $this->orig_width, $this->orig_height);
 
 		// Show the image
-		if ($this->dynamic_output == TRUE)
+		if ($this->dynamic_output === TRUE)
 		{
 			$this->image_display_gd($dst_img);
 		}
@@ -828,7 +828,7 @@
 	public function image_process_imagemagick($action = 'resize')
 	{
 		//  Do we have a vaild library path?
-		if ($this->library_path == '')
+		if ($this->library_path === '')
 		{
 			$this->set_error('imglib_libpath_invalid');
 			return FALSE;
@@ -842,11 +842,11 @@
 		// Execute the command
 		$cmd = $this->library_path.' -quality '.$this->quality;
 
-		if ($action == 'crop')
+		if ($action === 'crop')
 		{
 			$cmd .= ' -crop '.$this->width.'x'.$this->height.'+'.$this->x_axis.'+'.$this->y_axis.' "'.$this->full_src_path.'" "'.$this->full_dst_path .'" 2>&1';
 		}
-		elseif ($action == 'rotate')
+		elseif ($action === 'rotate')
 		{
 			$angle = ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt')
 					? '-flop' : '-rotate '.$this->rotation_angle;
@@ -855,7 +855,14 @@
 		}
 		else // Resize
 		{
-			$cmd .= ' -resize '.$this->width.'x'.$this->height.' "'.$this->full_src_path.'" "'.$this->full_dst_path.'" 2>&1';
+			if($this->maintain_ratio === TRUE)
+			{
+				$cmd .= ' -resize '.$this->width.'x'.$this->height.' "'.$this->full_src_path.'" "'.$this->full_dst_path.'" 2>&1';
+			}
+			else
+			{
+				$cmd .= ' -resize '.$this->width.'x'.$this->height.'\! "'.$this->full_src_path.'" "'.$this->full_dst_path.'" 2>&1';
+			}
 		}
 
 		$retval = 1;
@@ -886,7 +893,7 @@
 	 */
 	public function image_process_netpbm($action = 'resize')
 	{
-		if ($this->library_path == '')
+		if ($this->library_path === '')
 		{
 			$this->set_error('imglib_libpath_invalid');
 			return FALSE;
@@ -909,11 +916,11 @@
 				break;
 		}
 
-		if ($action == 'crop')
+		if ($action === 'crop')
 		{
 			$cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height;
 		}
-		elseif ($action == 'rotate')
+		elseif ($action === 'rotate')
 		{
 			switch ($this->rotation_angle)
 			{
@@ -984,7 +991,7 @@
 		$dst_img = imagerotate($src_img, $this->rotation_angle, $white);
 
 		// Show the image
-		if ($this->dynamic_output == TRUE)
+		if ($this->dynamic_output === TRUE)
 		{
 			$this->image_display_gd($dst_img);
 		}
@@ -1058,7 +1065,7 @@
 		}
 
 		// Show the image
-		if ($this->dynamic_output == TRUE)
+		if ($this->dynamic_output === TRUE)
 		{
 			$this->image_display_gd($src_img);
 		}
@@ -1129,10 +1136,10 @@
 		$this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]);
 		$this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]);
 
-		if ($this->wm_vrt_alignment == 'B')
+		if ($this->wm_vrt_alignment === 'B')
 			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
 
-		if ($this->wm_hor_alignment == 'R')
+		if ($this->wm_hor_alignment === 'R')
 			$this->wm_hor_offset = $this->wm_hor_offset * -1;
 
 		// Set the base x and y axis values
@@ -1160,7 +1167,7 @@
 		}
 
 		//  Build the finalized image
-		if ($wm_img_type == 3 && function_exists('imagealphablending'))
+		if ($wm_img_type === 3 && function_exists('imagealphablending'))
 		{
 			@imagealphablending($src_img, TRUE);
 		}
@@ -1183,7 +1190,7 @@
 		}
 
 		// Output the image
-		if ($this->dynamic_output == TRUE)
+		if ($this->dynamic_output === TRUE)
 		{
 			$this->image_display_gd($src_img);
 		}
@@ -1212,7 +1219,7 @@
 			return FALSE;
 		}
 
-		if ($this->wm_use_truetype == TRUE && ! file_exists($this->wm_font_path))
+		if ($this->wm_use_truetype === TRUE && ! file_exists($this->wm_font_path))
 		{
 			$this->set_error('imglib_missing_font');
 			return FALSE;
@@ -1228,18 +1235,18 @@
 		// invert the offset. Note: The horizontal
 		// offset flips itself automatically
 
-		if ($this->wm_vrt_alignment == 'B')
+		if ($this->wm_vrt_alignment === 'B')
 			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
 
-		if ($this->wm_hor_alignment == 'R')
+		if ($this->wm_hor_alignment === 'R')
 			$this->wm_hor_offset = $this->wm_hor_offset * -1;
 
 		// Set font width and height
 		// These are calculated differently depending on
 		// whether we are using the true type font or not
-		if ($this->wm_use_truetype == TRUE)
+		if ($this->wm_use_truetype === TRUE)
 		{
-			if ($this->wm_font_size == '')
+			if ($this->wm_font_size === '')
 			{
 				$this->wm_font_size = 17;
 			}
@@ -1258,7 +1265,7 @@
 		$x_axis = $this->wm_hor_offset + $this->wm_padding;
 		$y_axis = $this->wm_vrt_offset + $this->wm_padding;
 
-		if ($this->wm_use_drop_shadow == FALSE)
+		if ($this->wm_use_drop_shadow === FALSE)
 			$this->wm_shadow_distance = 0;
 
 		$this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1));
@@ -1316,7 +1323,7 @@
 		}
 
 		// Output the final image
-		if ($this->dynamic_output == TRUE)
+		if ($this->dynamic_output === TRUE)
 		{
 			$this->image_display_gd($src_img);
 		}
@@ -1344,10 +1351,10 @@
 	 */
 	public function image_create_gd($path = '', $image_type = '')
 	{
-		if ($path == '')
+		if ($path === '')
 			$path = $this->full_src_path;
 
-		if ($image_type == '')
+		if ($image_type === '')
 			$image_type = $this->image_type;
 
 
@@ -1494,7 +1501,7 @@
 	 */
 	public function image_reproportion()
 	{
-		if (($this->width == 0 && $this->height == 0) OR $this->orig_width == 0 OR $this->orig_height == 0
+		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))
 		{
@@ -1549,7 +1556,7 @@
 		// For now we require GD but we should
 		// find a way to determine this using IM or NetPBM
 
-		if ($path == '')
+		if ($path === '')
 		{
 			$path = $this->full_src_path;
 		}
@@ -1564,7 +1571,7 @@
 		$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
 		$mime = (isset($types[$vals[2]])) ? 'image/'.$types[$vals[2]] : 'image/jpg';
 
-		if ($return == TRUE)
+		if ($return === TRUE)
 		{
 			return array(
 					'width' =>	$vals[0],
@@ -1620,16 +1627,16 @@
 			}
 		}
 
-		if ($vals['width'] == 0 OR $vals['height'] == 0)
+		if ($vals['width'] === 0 OR $vals['height'] === 0)
 		{
 			return $vals;
 		}
 
-		if ($vals['new_width'] == 0)
+		if ($vals['new_width'] === 0)
 		{
 			$vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']);
 		}
-		elseif ($vals['new_height'] == 0)
+		elseif ($vals['new_height'] === 0)
 		{
 			$vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']);
 		}
@@ -1715,14 +1722,14 @@
 		{
 			foreach ($msg as $val)
 			{
-				$msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);
+				$msg = ($CI->lang->line($val) === FALSE) ? $val : $CI->lang->line($val);
 				$this->error_msg[] = $msg;
 				log_message('error', $msg);
 			}
 		}
 		else
 		{
-			$msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
+			$msg = ($CI->lang->line($msg) === FALSE) ? $msg : $CI->lang->line($msg);
 			$this->error_msg[] = $msg;
 			log_message('error', $msg);
 		}
diff --git a/system/libraries/Javascript.php b/system/libraries/Javascript.php
index dd2df69..5c8b092 100644
--- a/system/libraries/Javascript.php
+++ b/system/libraries/Javascript.php
@@ -615,12 +615,12 @@
 		{
 			$this->_javascript_location = $external_file;
 		}
-		elseif ($this->CI->config->item('javascript_location') != '')
+		elseif ($this->CI->config->item('javascript_location') !== '')
 		{
 			$this->_javascript_location = $this->CI->config->item('javascript_location');
 		}
 
-		if ($relative === TRUE OR strncmp($external_file, 'http://', 7) === 0 OR strncmp($external_file, 'https://', 8) === 0)
+		if ($relative === TRUE OR strpos($external_file, 'http://') === 0 OR strpos($external_file, 'https://') === 0)
 		{
 			$str = $this->_open_script($external_file);
 		}
@@ -667,7 +667,7 @@
 	protected function _open_script($src = '')
 	{
 		return '<script type="text/javascript" charset="'.strtolower($this->CI->config->item('charset')).'"'
-			.($src == '' ? '>' : ' src="'.$src.'">');
+			.($src === '' ? '>' : ' src="'.$src.'">');
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/libraries/Log.php b/system/libraries/Log.php
index 51ce43d..baac801 100644
--- a/system/libraries/Log.php
+++ b/system/libraries/Log.php
@@ -94,7 +94,7 @@
 	{
 		$config =& get_config();
 
-		$this->_log_path = ($config['log_path'] != '') ? $config['log_path'] : APPPATH.'logs/';
+		$this->_log_path = ($config['log_path'] !== '') ? $config['log_path'] : APPPATH.'logs/';
 
 		if ( ! is_dir($this->_log_path) OR ! is_really_writable($this->_log_path))
 		{
@@ -111,7 +111,7 @@
 			$this->_threshold_array = array_flip($config['log_threshold']);
 		}
 
-		if ($config['log_date_format'] != '')
+		if ($config['log_date_format'] !== '')
 		{
 			$this->_date_fmt = $config['log_date_format'];
 		}
diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php
index 0a88e69..c786703 100644
--- a/system/libraries/Migration.php
+++ b/system/libraries/Migration.php
@@ -109,7 +109,7 @@
 		}
 
 		// If not set, set it
-		$this->_migration_path != '' OR $this->_migration_path = APPPATH.'migrations/';
+		$this->_migration_path !== '' OR $this->_migration_path = APPPATH.'migrations/';
 
 		// Add trailing slash if not set
 		$this->_migration_path = rtrim($this->_migration_path, '/').'/';
@@ -292,7 +292,7 @@
 
 		// Calculate the last migration step from existing migration
 		// filenames and procceed to the standard version migration
-		return $this->version((int) substr($last_migration, 0, 3));
+		return $this->version((int) $last_migration);
 	}
 
 	// --------------------------------------------------------------------
@@ -322,9 +322,9 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Set's the schema to the latest migration
+	 * Retrieves list of available migration scripts
 	 *
-	 * @return	mixed	true if already latest, false if failed, int if upgraded
+	 * @return	array	list of migration file paths sorted by version
 	 */
 	protected function find_migrations()
 	{
diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php
index 58f86fa..75745dd 100644
--- a/system/libraries/Pagination.php
+++ b/system/libraries/Pagination.php
@@ -65,9 +65,11 @@
 	protected $num_tag_open			= '&nbsp;';
 	protected $num_tag_close		= '';
 	protected $page_query_string	= FALSE;
-	protected $query_string_segment = 'per_page';
+	protected $query_string_segment 	= 'per_page';
 	protected $display_pages		= TRUE;
-	protected $anchor_class			= '';
+	protected $_attributes			= '';
+	protected $_link_types			= array();
+	protected $reuse_query_string   = FALSE;
 
 	/**
 	 * Constructor
@@ -91,15 +93,29 @@
 	 */
 	public function initialize($params = array())
 	{
+		$attributes = array();
+
+		if (isset($params['attributes']) && is_array($params['attributes']))
+		{
+			$attributes = $params['attributes'];
+			unset($params['attributes']);
+		}
+
+		// Deprecated legacy support for the anchor_class option
+		// Should be removed in CI 3.1+
+		if (isset($params['anchor_class']))
+		{
+			empty($params['anchor_class']) OR $attributes['class'] = $params['anchor_class'];
+			unset($params['anchor_class']);
+		}
+
+		$this->_parse_attributes($attributes);
+
 		if (count($params) > 0)
 		{
 			foreach ($params as $key => $val)
 			{
-				if ($key === 'anchor_class')
-				{
-					$this->anchor_class = ($val != '') ? 'class="'.$val.'" ' : '';
-				}
-				elseif (isset($this->$key))
+				if (isset($this->$key))
 				{
 					$this->$key = $val;
 				}
@@ -117,7 +133,7 @@
 	public function create_links()
 	{
 		// If our item count or per-page total is zero there is no need to continue.
-		if ($this->total_rows == 0 OR $this->per_page == 0)
+		if ($this->total_rows === 0 OR $this->per_page === 0)
 		{
 			return '';
 		}
@@ -138,7 +154,7 @@
 		$CI =& get_instance();
 
 		// See if we are using a prefix or suffix on links
-		if ($this->prefix != '' OR $this->suffix != '')
+		if ($this->prefix !== '' OR $this->suffix !== '')
 		{
 			$this->cur_page = (int) str_replace(array($this->prefix, $this->suffix), '', $CI->uri->segment($this->uri_segment));
 		}
@@ -150,13 +166,13 @@
 				$this->cur_page = (int) $CI->input->get($this->query_string_segment);
 			}
 		}
-		elseif ( ! $this->cur_page && $CI->uri->segment($this->uri_segment) != $base_page)
+		elseif ( ! $this->cur_page && $CI->uri->segment($this->uri_segment) !== $base_page)
 		{
 			$this->cur_page = (int) $CI->uri->segment($this->uri_segment);
 		}
 
 		// Set current page to 1 if it's not valid or if using page numbers instead of offset
-		if ( ! is_numeric($this->cur_page) OR ($this->use_page_numbers && $this->cur_page == 0))
+		if ( ! is_numeric($this->cur_page) OR ($this->use_page_numbers && $this->cur_page === 0))
 		{
 			$this->cur_page = $base_page;
 		}
@@ -207,27 +223,47 @@
 
 		// And here we go...
 		$output = '';
+		$query_string = '';
+
+		// Add anything in the query string back to the links
+		// Note: Nothing to do with query_string_segment or any other query string options
+		if ($this->reuse_query_string === TRUE)
+		{
+			$get = $CI->input->get();
+			
+			// Unset the controll, method, old-school routing options
+			unset($get['c'], $get['m'], $get[$this->query_string_segment]);
+
+			// Put everything else onto the end
+			$query_string = (strpos($this->base_url, '&amp;') !== FALSE ? '&amp;' : '?') . http_build_query($get, '', '&amp;');
+
+			// Add this after the suffix to put it into more links easily
+			$this->suffix .= $query_string;
+		}
 
 		// Render the "First" link
 		if ($this->first_link !== FALSE && $this->cur_page > ($this->num_links + 1))
 		{
-			$first_url = ($this->first_url == '') ? $this->base_url : $this->first_url;
-			$output .= $this->first_tag_open.'<a '.$this->anchor_class.'href="'.$first_url.'">'.$this->first_link.'</a>'.$this->first_tag_close;
+			$first_url = ($this->first_url === '') ? $this->base_url : $this->first_url;
+			$output .= $this->first_tag_open.'<a href="'.$first_url.'"'.$this->_attributes.$this->_attr_rel('start').'>'
+				.$this->first_link.'</a>'.$this->first_tag_close;
 		}
 
 		// Render the "previous" link
-		if  ($this->prev_link !== FALSE && $this->cur_page != 1)
+		if ($this->prev_link !== FALSE && $this->cur_page !== 1)
 		{
 			$i = ($this->use_page_numbers) ? $uri_page_number - 1 : $uri_page_number - $this->per_page;
 
-			if ($i == $base_page && $this->first_url != '')
+			if ($i === $base_page && $this->first_url !== '')
 			{
-				$output .= $this->prev_tag_open.'<a '.$this->anchor_class.'href="'.$this->first_url.'">'.$this->prev_link.'</a>'.$this->prev_tag_close;
+				$output .= $this->prev_tag_open.'<a href="'.$this->first_url.$query_string.'"'.$this->_attributes.$this->_attr_rel('prev').'>'
+					.$this->prev_link.'</a>'.$this->prev_tag_close;
 			}
 			else
 			{
-				$i = ($i == $base_page) ? '' : $this->prefix.$i.$this->suffix;
-				$output .= $this->prev_tag_open.'<a '.$this->anchor_class.'href="'.$this->base_url.$i.'">'.$this->prev_link.'</a>'.$this->prev_tag_close;
+				$append = ($i === $base_page) ? $query_string : $this->prefix.$i.$this->suffix;
+				$output .= $this->prev_tag_open.'<a href="'.$this->base_url.$append.'"'.$this->_attributes.$this->_attr_rel('prev').'>'
+					.$this->prev_link.'</a>'.$this->prev_tag_close;
 			}
 
 		}
@@ -239,26 +275,25 @@
 			for ($loop = $start -1; $loop <= $end; $loop++)
 			{
 				$i = ($this->use_page_numbers) ? $loop : ($loop * $this->per_page) - $this->per_page;
-
 				if ($i >= $base_page)
 				{
-					if ($this->cur_page == $loop)
+					if ($this->cur_page === $loop)
 					{
 						$output .= $this->cur_tag_open.$loop.$this->cur_tag_close; // Current page
 					}
 					else
 					{
-						$n = ($i == $base_page) ? '' : $i;
-
-						if ($n == '' && $this->first_url != '')
+						$n = ($i === $base_page) ? '' : $i;
+						if ($n === '' && ! empty($this->first_url))
 						{
-							$output .= $this->num_tag_open.'<a '.$this->anchor_class.'href="'.$this->first_url.'">'.$loop.'</a>'.$this->num_tag_close;
+							$output .= $this->num_tag_open.'<a href="'.$this->first_url.$query_string.'"'.$this->_attributes.$this->_attr_rel('start').'>'
+								.$loop.'</a>'.$this->num_tag_close;
 						}
 						else
 						{
-							$n = ($n == '') ? '' : $this->prefix.$n.$this->suffix;
-
-							$output .= $this->num_tag_open.'<a '.$this->anchor_class.'href="'.$this->base_url.$n.'">'.$loop.'</a>'.$this->num_tag_close;
+							$append = ($n === '') ? $query_string : $this->prefix.$n.$this->suffix;
+							$output .= $this->num_tag_open.'<a href="'.$this->base_url.$append.'"'.$this->_attributes.$this->_attr_rel('start').'>'
+								.$loop.'</a>'.$this->num_tag_close;
 						}
 					}
 				}
@@ -270,7 +305,8 @@
 		{
 			$i = ($this->use_page_numbers) ? $this->cur_page + 1 : $this->cur_page * $this->per_page;
 
-			$output .= $this->next_tag_open.'<a '.$this->anchor_class.'href="'.$this->base_url.$this->prefix.$i.$this->suffix.'">'.$this->next_link.'</a>'.$this->next_tag_close;
+			$output .= $this->next_tag_open.'<a href="'.$this->base_url.$this->prefix.$i.$this->suffix.'"'.$this->_attributes
+				.$this->_attr_rel('next').'>'.$this->next_link.'</a>'.$this->next_tag_close;
 		}
 
 		// Render the "Last" link
@@ -278,7 +314,8 @@
 		{
 			$i = ($this->use_page_numbers) ? $num_pages : ($num_pages * $this->per_page) - $this->per_page;
 
-			$output .= $this->last_tag_open.'<a '.$this->anchor_class.'href="'.$this->base_url.$this->prefix.$i.$this->suffix.'">'.$this->last_link.'</a>'.$this->last_tag_close;
+			$output .= $this->last_tag_open.'<a href="'.$this->base_url.$this->prefix.$i.$this->suffix.'"'.$this->_attributes.'>'
+				.$this->last_link.'</a>'.$this->last_tag_close;
 		}
 
 		// Kill double slashes. Note: Sometimes we can end up with a double slash
@@ -289,6 +326,49 @@
 		return $this->full_tag_open.$output.$this->full_tag_close;
 	}
 
+	// --------------------------------------------------------------------
+
+	/**
+	 * Parse attributes
+	 *
+	 * @param	array
+	 * @return	void
+	 */
+	protected function _parse_attributes($attributes)
+	{
+		isset($attributes['rel']) OR $attributes['rel'] = TRUE;
+		$this->_link_types = ($attributes['rel'])
+					? array('start' => 'start', 'prev' => 'prev', 'next' => 'next')
+					: array();
+		unset($attributes['rel']);
+
+		$this->_attributes = '';
+		foreach ($attributes as $key => $value)
+		{
+			$this->_attributes .= ' '.$key.'="'.$value.'"';
+		}
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Add "rel" attribute
+	 *
+	 * @link	http://www.w3.org/TR/html5/links.html#linkTypes
+	 * @param	string
+	 * @return	string
+	 */
+	protected function _attr_rel($type)
+	{
+		if (isset($this->_link_types[$type]))
+		{
+			unset($this->_link_types[$type]);
+			return ' rel="'.$type.'"';
+		}
+
+		return '';
+	}
+
 }
 
 /* End of file Pagination.php */
diff --git a/system/libraries/Parser.php b/system/libraries/Parser.php
index a0b60ed..b64c782 100644
--- a/system/libraries/Parser.php
+++ b/system/libraries/Parser.php
@@ -109,7 +109,7 @@
 	 */
 	protected function _parse($template, $data, $return = FALSE)
 	{
-		if ($template == '')
+		if ($template === '')
 		{
 			return FALSE;
 		}
@@ -121,7 +121,7 @@
 					: $template = $this->_parse_single($key, (string) $val, $template);
 		}
 
-		if ($return == FALSE)
+		if ($return === FALSE)
 		{
 			$this->CI->output->append_output($template);
 		}
diff --git a/system/libraries/Profiler.php b/system/libraries/Profiler.php
index e219d20..1e961f6 100644
--- a/system/libraries/Profiler.php
+++ b/system/libraries/Profiler.php
@@ -116,6 +116,12 @@
 	 */
 	public function set_sections($config)
 	{
+		if (isset($config['query_toggle_count']))
+		{
+			$this->_query_toggle_count = (int) $config['query_toggle_count'];
+			unset($config['query_toggle_count']);
+		}
+
 		foreach ($config as $method => $enable)
 		{
 			if (in_array($method, $this->_available_sections))
@@ -219,7 +225,7 @@
 
 			$show_hide_js = '(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_queries_db_'.$count.'\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_hide').'\'?\''.$this->CI->lang->line('profiler_section_show').'\':\''.$this->CI->lang->line('profiler_section_hide').'\';">'.$this->CI->lang->line('profiler_section_hide').'</span>)';
 
-			if ($hide_queries != '')
+			if ($hide_queries !== '')
 			{
 				$show_hide_js = '(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_queries_db_'.$count.'\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show').'</span>)';
 			}
@@ -315,7 +321,7 @@
 			."\n"
 			.'<legend style="color:#009900;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_post_data')."&nbsp;&nbsp;</legend>\n";
 
-		if (count($_POST) == 0)
+		if (count($_POST) === 0)
 		{
 			$output .= '<div style="color:#009900;font-weight:normal;padding:4px 0 4px 0;">'.$this->CI->lang->line('profiler_no_post').'</div>';
 		}
@@ -365,7 +371,7 @@
 			."\n"
 			.'<legend style="color:#000;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_uri_string')."&nbsp;&nbsp;</legend>\n"
 			.'<div style="color:#000;font-weight:normal;padding:4px 0 4px 0;">'
-			.($this->CI->uri->uri_string == '' ? $this->CI->lang->line('profiler_no_uri') : $this->CI->uri->uri_string)
+			.($this->CI->uri->uri_string === '' ? $this->CI->lang->line('profiler_no_uri') : $this->CI->uri->uri_string)
 			.'</div></fieldset>';
 	}
 
@@ -402,7 +408,7 @@
 			."\n"
 			.'<legend style="color:#5a0099;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_memory_usage')."&nbsp;&nbsp;</legend>\n"
 			.'<div style="color:#5a0099;font-weight:normal;padding:4px 0 4px 0;">'
-			.((function_exists('memory_get_usage') && ($usage = memory_get_usage()) != '') ? number_format($usage).' bytes' : $this->CI->lang->line('profiler_no_memory'))
+			.(($usage = memory_get_usage()) != '' ? number_format($usage).' bytes' : $this->CI->lang->line('profiler_no_memory'))
 			.'</div></fieldset>';
 	}
 
diff --git a/system/libraries/Session.php b/system/libraries/Session.php
index 4d6aa0c..af38dc3 100644
--- a/system/libraries/Session.php
+++ b/system/libraries/Session.php
@@ -149,18 +149,12 @@
 	public $flashdata_key			= 'flash';
 
 	/**
-	 * Function to use to get the current time
+	 * Timezone to use for the current time
 	 *
 	 * @var string
 	 */
-	public $time_reference			= 'time';
+	public $time_reference			= 'local';
 
-	/**
-	 * Probablity level of garbage collection of old sessions
-	 *
-	 * @var int
-	 */
-	public $gc_probability			= 5;
 
 	/**
 	 * Session data
@@ -203,10 +197,10 @@
 		// manually via the $params array above or via the config file
 		foreach (array('sess_encrypt_cookie', 'sess_use_database', 'sess_table_name', 'sess_expiration', 'sess_expire_on_close', 'sess_match_ip', 'sess_match_useragent', 'sess_cookie_name', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key)
 		{
-			$this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key);
+			$this->$key = isset($params[$key]) ? $params[$key] : $this->CI->config->item($key);
 		}
 
-		if ($this->encryption_key == '')
+		if ($this->encryption_key === '')
 		{
 			show_error('In order to use the Session class you are required to set an encryption key in your config file.');
 		}
@@ -215,13 +209,13 @@
 		$this->CI->load->helper('string');
 
 		// Do we need encryption? If so, load the encryption class
-		if ($this->sess_encrypt_cookie == TRUE)
+		if ($this->sess_encrypt_cookie === TRUE)
 		{
 			$this->CI->load->library('encrypt');
 		}
 
 		// Are we using a database? If so, load it
-		if ($this->sess_use_database === TRUE && $this->sess_table_name != '')
+		if ($this->sess_use_database === TRUE && $this->sess_table_name !== '')
 		{
 			$this->CI->load->database();
 		}
@@ -232,7 +226,7 @@
 
 		// Set the session length. If the session expiration is
 		// set to zero we'll set the expiration two years from now.
-		if ($this->sess_expiration == 0)
+		if ($this->sess_expiration === 0)
 		{
 			$this->sess_expiration = (60*60*24*365*2);
 		}
@@ -283,7 +277,7 @@
 		}
 
 		// Decrypt the cookie data
-		if ($this->sess_encrypt_cookie == TRUE)
+		if ($this->sess_encrypt_cookie === TRUE)
 		{
 			$session = $this->CI->encrypt->decode($session);
 		}
@@ -320,14 +314,14 @@
 		}
 
 		// Does the IP match?
-		if ($this->sess_match_ip == TRUE && $session['ip_address'] !== $this->CI->input->ip_address())
+		if ($this->sess_match_ip === TRUE && $session['ip_address'] !== $this->CI->input->ip_address())
 		{
 			$this->sess_destroy();
 			return FALSE;
 		}
 
 		// Does the User Agent Match?
-		if ($this->sess_match_useragent == TRUE && trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120)))
+		if ($this->sess_match_useragent === TRUE && trim($session['user_agent']) !== trim(substr($this->CI->input->user_agent(), 0, 120)))
 		{
 			$this->sess_destroy();
 			return FALSE;
@@ -338,12 +332,12 @@
 		{
 			$this->CI->db->where('session_id', $session['session_id']);
 
-			if ($this->sess_match_ip == TRUE)
+			if ($this->sess_match_ip === TRUE)
 			{
 				$this->CI->db->where('ip_address', $session['ip_address']);
 			}
 
-			if ($this->sess_match_useragent == TRUE)
+			if ($this->sess_match_useragent === TRUE)
 			{
 				$this->CI->db->where('user_agent', $session['user_agent']);
 			}
@@ -359,7 +353,7 @@
 
 			// Is there custom data?  If so, add it to the main session array
 			$row = $query->row();
-			if (isset($row->user_data) && $row->user_data != '')
+			if ( ! empty($row->user_data))
 			{
 				$custom_data = $this->_unserialize($row->user_data);
 
@@ -786,9 +780,15 @@
 	 */
 	protected function _get_time()
 	{
-		return (strtolower($this->time_reference) === 'gmt')
-			? mktime(gmdate('H'), gmdate('i'), gmdate('s'), gmdate('m'), gmdate('d'), gmdate('Y'))
-			: time();
+		if ($this->time_reference === 'local' OR $this->time_reference === date_default_timezone_get())
+		{
+			return time();
+		}
+
+		$datetime = new DateTime('now', new DateTimeZone($this->time_reference));
+		sscanf($datetime->format('j-n-Y G:i:s'), '%d-%d-%d %d:%d:%d', $day, $month, $year, $hour, $minute, $second);
+
+		return mktime($hour, $minute, $second, $month, $day, $year);
 	}
 
 	// --------------------------------------------------------------------
@@ -809,7 +809,7 @@
 		// Serialize the userdata for the cookie
 		$cookie_data = $this->_serialize($cookie_data);
 
-		if ($this->sess_encrypt_cookie == TRUE)
+		if ($this->sess_encrypt_cookie === TRUE)
 		{
 			$cookie_data = $this->CI->encrypt->encode($cookie_data);
 		}
@@ -929,13 +929,16 @@
 	 */
 	protected function _sess_gc()
 	{
-		if ($this->sess_use_database != TRUE)
+		if ($this->sess_use_database !== TRUE)
 		{
 			return;
 		}
 
+		$probability = ini_get('session.gc_probability');
+		$divisor = ini_get('session.gc_divisor');
+
 		srand(time());
-		if ((rand() % 100) < $this->gc_probability)
+		if ((mt_rand(0, $divisor) / $divisor) < $probability)
 		{
 			$expire = $this->now - $this->sess_expiration;
 
diff --git a/system/libraries/Table.php b/system/libraries/Table.php
index f844d64..0f8404d 100644
--- a/system/libraries/Table.php
+++ b/system/libraries/Table.php
@@ -169,7 +169,7 @@
 		// will want headings from a one-dimensional array
 		$this->auto_heading = FALSE;
 
-		if ($col_limit == 0)
+		if ($col_limit === 0)
 		{
 			return $array;
 		}
@@ -298,7 +298,7 @@
 			}
 			elseif (is_array($table_data))
 			{
-				$set_heading = (count($this->heading) !== 0 OR $this->auto_heading != FALSE);
+				$set_heading = (count($this->heading) !== 0 OR $this->auto_heading !== FALSE);
 				$this->_set_from_array($table_data, $set_heading);
 			}
 		}
@@ -336,7 +336,7 @@
 
 				foreach ($heading as $key => $val)
 				{
-					if ($key != 'data')
+					if ($key !== 'data')
 					{
 						$temp = str_replace('<th', '<th '.$key.'="'.$val.'"', $temp);
 					}
@@ -481,7 +481,7 @@
 		foreach ($data as $row)
 		{
 			// If a heading hasn't already been set we'll use the first row of the array as the heading
-			if ($i++ === 0 && count($data) > 1 && count($this->heading) === 0 && $set_heading == TRUE)
+			if ($i++ === 0 && count($data) > 1 && count($this->heading) === 0 && $set_heading === TRUE)
 			{
 				$this->heading = $this->_prep_args($row);
 			}
@@ -501,7 +501,7 @@
 	 */
 	protected function _compile_template()
 	{
-		if ($this->template == NULL)
+		if ($this->template === NULL)
 		{
 			$this->template = $this->_default_template();
 			return;
diff --git a/system/libraries/Trackback.php b/system/libraries/Trackback.php
index 6761f63..9a680dc 100644
--- a/system/libraries/Trackback.php
+++ b/system/libraries/Trackback.php
@@ -88,7 +88,7 @@
 			}
 
 			// Convert High ASCII Characters
-			if ($this->convert_ascii == TRUE && in_array($item, array('excerpt', 'title', 'blog_name')))
+			if ($this->convert_ascii === TRUE && in_array($item, array('excerpt', 'title', 'blog_name')))
 			{
 				$$item = $this->convert_ascii($$item);
 			}
@@ -106,7 +106,7 @@
 		{
 			foreach ($ping_url as $url)
 			{
-				if ($this->process($url, $data) == FALSE)
+				if ($this->process($url, $data) === FALSE)
 				{
 					$return = FALSE;
 				}
@@ -132,7 +132,7 @@
 	{
 		foreach (array('url', 'title', 'blog_name', 'excerpt') as $val)
 		{
-			if ( ! isset($_POST[$val]) OR $_POST[$val] == '')
+			if (empty($_POST[$val]))
 			{
 				$this->set_error('The following required POST variable is missing: '.$val);
 				return FALSE;
@@ -140,14 +140,14 @@
 
 			$this->data['charset'] = isset($_POST['charset']) ? strtoupper(trim($_POST['charset'])) : 'auto';
 
-			if ($val != 'url' && MB_ENABLED === TRUE)
+			if ($val !== 'url' && MB_ENABLED === TRUE)
 			{
 				$_POST[$val] = mb_convert_encoding($_POST[$val], $this->charset, $this->data['charset']);
 			}
 
-			$_POST[$val] = ($val != 'url') ? $this->convert_xml(strip_tags($_POST[$val])) : strip_tags($_POST[$val]);
+			$_POST[$val] = ($val !== 'url') ? $this->convert_xml(strip_tags($_POST[$val])) : strip_tags($_POST[$val]);
 
-			if ($val == 'excerpt')
+			if ($val === 'excerpt')
 			{
 				$_POST['excerpt'] = $this->limit_characters($_POST['excerpt']);
 			}
diff --git a/system/libraries/Typography.php b/system/libraries/Typography.php
index 6aaa993..a50934f 100644
--- a/system/libraries/Typography.php
+++ b/system/libraries/Typography.php
@@ -95,7 +95,7 @@
 	 */
 	public function auto_typography($str, $reduce_linebreaks = FALSE)
 	{
-		if ($str == '')
+		if ($str === '')
 		{
 			return '';
 		}
@@ -173,7 +173,7 @@
 					$process = ($match[1] === '/');
 				}
 
-				if ($match[1] == '')
+				if ($match[1] === '')
 				{
 					$this->last_block_element = $match[2];
 				}
@@ -344,7 +344,7 @@
 	 */
 	protected function _format_newlines($str)
 	{
-		if ($str == '' OR (strpos($str, "\n") === FALSE && ! in_array($this->last_block_element, $this->inner_block_required)))
+		if ($str === '' OR (strpos($str, "\n") === FALSE && ! in_array($this->last_block_element, $this->inner_block_required)))
 		{
 			return $str;
 		}
@@ -356,7 +356,7 @@
 		$str = preg_replace("/([^\n])(\n)([^\n])/", '\\1<br />\\2\\3', $str);
 
 		// Wrap the whole enchilada in enclosing paragraphs
-		if ($str != "\n")
+		if ($str !== "\n")
 		{
 			// We trim off the right-side new line so that the closing </p> tag
 			// will be positioned immediately following the string, matching
diff --git a/system/libraries/Unit_test.php b/system/libraries/Unit_test.php
index 6ec2dcd..70ad8dc 100644
--- a/system/libraries/Unit_test.php
+++ b/system/libraries/Unit_test.php
@@ -93,7 +93,7 @@
 	 */
 	public function run($test, $expected = TRUE, $test_name = 'undefined', $notes = '')
 	{
-		if ($this->active == FALSE)
+		if ($this->active === FALSE)
 		{
 			return FALSE;
 		}
@@ -106,7 +106,7 @@
 		}
 		else
 		{
-			$result = ($this->strict == TRUE) ? ($test === $expected) : ($test == $expected);
+			$result = ($this->strict === TRUE) ? ($test === $expected) : ($test === $expected);
 			$extype = gettype($expected);
 		}
 
@@ -124,7 +124,7 @@
 
 		$this->results[] = $report;
 
-		return($this->report($this->result($report)));
+		return $this->report($this->result($report));
 	}
 
 	// --------------------------------------------------------------------
@@ -155,13 +155,13 @@
 
 			foreach ($res as $key => $val)
 			{
-				if ($key == $CI->lang->line('ut_result'))
+				if ($key === $CI->lang->line('ut_result'))
 				{
-					if ($val == $CI->lang->line('ut_passed'))
+					if ($val === $CI->lang->line('ut_passed'))
 					{
 						$val = '<span style="color: #0C0;">'.$val.'</span>';
 					}
-					elseif ($val == $CI->lang->line('ut_failed'))
+					elseif ($val === $CI->lang->line('ut_failed'))
 					{
 						$val = '<span style="color: #C00;">'.$val.'</span>';
 					}
@@ -289,15 +289,11 @@
 	 */
 	protected function _backtrace()
 	{
-		if (function_exists('debug_backtrace'))
-		{
-			$back = debug_backtrace();
-			return array(
-					'file' => (isset($back[1]['file']) ? $back[1]['file'] : ''),
-					'line' => (isset($back[1]['line']) ? $back[1]['line'] : '')
-				);
-		}
-		return array('file' => 'Unknown', 'line' => 'Unknown');
+		$back = debug_backtrace();
+		return array(
+				'file' => (isset($back[1]['file']) ? $back[1]['file'] : ''),
+				'line' => (isset($back[1]['line']) ? $back[1]['line'] : '')
+			);
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php
index 7456e92..d381440 100644
--- a/system/libraries/Upload.php
+++ b/system/libraries/Upload.php
@@ -59,6 +59,7 @@
 	public $error_msg		= array();
 	public $mimes			= array();
 	public $remove_spaces		= TRUE;
+	public $detect_mime		= TRUE;
 	public $xss_clean		= FALSE;
 	public $temp_prefix		= 'temp_file_';
 	public $client_name		= '';
@@ -78,6 +79,8 @@
 			$this->initialize($props);
 		}
 
+		$this->mimes =& get_mimes();
+
 		log_message('debug', 'Upload Class Initialized');
 	}
 
@@ -113,8 +116,8 @@
 					'image_type'			=> '',
 					'image_size_str'		=> '',
 					'error_msg'			=> array(),
-					'mimes'				=> array(),
 					'remove_spaces'			=> TRUE,
+					'detect_mime'			=> TRUE,
 					'xss_clean'			=> FALSE,
 					'temp_prefix'			=> 'temp_file_',
 					'client_name'			=> ''
@@ -208,7 +211,13 @@
 		// Set the uploaded data as class variables
 		$this->file_temp = $_FILES[$field]['tmp_name'];
 		$this->file_size = $_FILES[$field]['size'];
-		$this->_file_mime_type($_FILES[$field]);
+
+		// Skip MIME type detection?
+		if ($this->detect_mime !== FALSE)
+		{
+			$this->_file_mime_type($_FILES[$field]);
+		}
+
 		$this->file_type = preg_replace('/^(.+?);.*$/', '\\1', $this->file_type);
 		$this->file_type = strtolower(trim(stripslashes($this->file_type), '"'));
 		$this->file_name = $this->_prep_filename($_FILES[$field]['name']);
@@ -223,7 +232,7 @@
 		}
 
 		// if we're overriding, let's now make sure the new name and type is allowed
-		if ($this->_file_name_override != '')
+		if ($this->_file_name_override !== '')
 		{
 			$this->file_name = $this->_prep_filename($this->_file_name_override);
 
@@ -276,7 +285,7 @@
 		}
 
 		// Remove white spaces in the name
-		if ($this->remove_spaces == TRUE)
+		if ($this->remove_spaces === TRUE)
 		{
 			$this->file_name = preg_replace('/\s+/', '_', $this->file_name);
 		}
@@ -289,7 +298,7 @@
 		 */
 		$this->orig_name = $this->file_name;
 
-		if ($this->overwrite == FALSE)
+		if ($this->overwrite === FALSE)
 		{
 			$this->file_name = $this->set_filename($this->upload_path, $this->file_name);
 
@@ -346,11 +355,12 @@
 	 * Returns an associative array containing all of the information
 	 * related to the upload, allowing the developer easy access in one array.
 	 *
-	 * @return	array
+	 * @param	string
+	 * @return	mixed
 	 */
-	public function data()
+	public function data($index = NULL)
 	{
-		return array(
+		$data = array(
 				'file_name'		=> $this->file_name,
 				'file_type'		=> $this->file_type,
 				'file_path'		=> $this->upload_path,
@@ -366,6 +376,13 @@
 				'image_type'		=> $this->image_type,
 				'image_size_str'	=> $this->image_size_str,
 			);
+
+		if ( ! empty($index))
+		{
+			return isset($data[$index]) ? $data[$index] : NULL;
+		}
+
+		return $data;
 	}
 
 	// --------------------------------------------------------------------
@@ -397,7 +414,7 @@
 	 */
 	public function set_filename($path, $filename)
 	{
-		if ($this->encrypt_name == TRUE)
+		if ($this->encrypt_name === TRUE)
 		{
 			mt_srand();
 			$filename = md5(uniqid(mt_rand())).$this->file_ext;
@@ -420,7 +437,7 @@
 			}
 		}
 
-		if ($new_filename == '')
+		if ($new_filename === '')
 		{
 			$this->set_error('upload_bad_filename');
 			return FALSE;
@@ -545,7 +562,7 @@
 	 */
 	public function set_xss_clean($flag = FALSE)
 	{
-		$this->xss_clean = ($flag == TRUE);
+		$this->xss_clean = ($flag === TRUE);
 	}
 
 	// --------------------------------------------------------------------
@@ -641,7 +658,7 @@
 	 */
 	public function is_allowed_filesize()
 	{
-		return ($this->max_size == 0 OR $this->max_size > $this->file_size);
+		return ($this->max_size === 0 OR $this->max_size > $this->file_size);
 	}
 
 	// --------------------------------------------------------------------
@@ -687,13 +704,13 @@
 	 */
 	public function validate_upload_path()
 	{
-		if ($this->upload_path == '')
+		if ($this->upload_path === '')
 		{
 			$this->set_error('upload_no_filepath');
 			return FALSE;
 		}
 
-		if (function_exists('realpath') && @realpath($this->upload_path) !== FALSE)
+		if (@realpath($this->upload_path) !== FALSE)
 		{
 			$this->upload_path = str_replace('\\', '/', realpath($this->upload_path));
 		}
@@ -814,17 +831,17 @@
 			return FALSE;
 		}
 
-		if (function_exists('memory_get_usage') && memory_get_usage() && ini_get('memory_limit') != '')
+		if (memory_get_usage() && ($memory_limit = ini_get('memory_limit')))
 		{
-			$current = ini_get('memory_limit') * 1024 * 1024;
+			$memory_limit *= 1024 * 1024;
 
 			// There was a bug/behavioural change in PHP 5.2, where numbers over one million get output
 			// into scientific notation. number_format() ensures this number is an integer
 			// http://bugs.php.net/bug.php?id=43053
 
-			$new_memory = number_format(ceil(filesize($file) + $current), 0, '.', '');
+			$memory_limit = number_format(ceil(filesize($file) + $memory_limit), 0, '.', '');
 
-			ini_set('memory_limit', $new_memory); // When an integer is used, the value is measured in bytes. - PHP.net
+			ini_set('memory_limit', $memory_limit); // When an integer is used, the value is measured in bytes. - PHP.net
 		}
 
 		// If the file being uploaded is an image, then we should have no problem with XSS attacks (in theory), but
@@ -848,14 +865,8 @@
 			// <a, <body, <head, <html, <img, <plaintext, <pre, <script, <table, <title
 			// title is basically just in SVG, but we filter it anyhow
 
-			if ( ! preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\s>]/i', $opening_bytes))
-			{
-				return TRUE; // its an image, no "triggers" detected in the first 256 bytes, we're good
-			}
-			else
-			{
-				return FALSE;
-			}
+			// if its an image or no "triggers" detected in the first 256 bytes - we're good
+			return ! preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\s>]/i', $opening_bytes);
 		}
 
 		if (($data = @file_get_contents($file)) === FALSE)
@@ -884,14 +895,14 @@
 		{
 			foreach ($msg as $val)
 			{
-				$msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);
+				$msg = ($CI->lang->line($val) === FALSE) ? $val : $CI->lang->line($val);
 				$this->error_msg[] = $msg;
 				log_message('error', $msg);
 			}
 		}
 		else
 		{
-			$msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
+			$msg = ($CI->lang->line($msg) === FALSE) ? $msg : $CI->lang->line($msg);
 			$this->error_msg[] = $msg;
 			log_message('error', $msg);
 		}
@@ -924,26 +935,6 @@
 	 */
 	public function mimes_types($mime)
 	{
-		global $mimes;
-
-		if (count($this->mimes) == 0)
-		{
-			if (defined('ENVIRONMENT') && is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
-			{
-				include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
-			}
-			elseif (is_file(APPPATH.'config/mimes.php'))
-			{
-				include(APPPATH.'config/mimes.php');
-			}
-			else
-			{
-				return FALSE;
-			}
-
-			$this->mimes = $mimes;
-		}
-
 		return isset($this->mimes[$mime]) ? $this->mimes[$mime] : FALSE;
 	}
 
@@ -960,7 +951,7 @@
 	 */
 	protected function _prep_filename($filename)
 	{
-		if (strpos($filename, '.') === FALSE OR $this->allowed_types == '*')
+		if (strpos($filename, '.') === FALSE OR $this->allowed_types === '*')
 		{
 			return $filename;
 		}
@@ -1007,7 +998,7 @@
 		 */
 		if (function_exists('finfo_file'))
 		{
-			$finfo = finfo_open(FILEINFO_MIME);
+			$finfo = @finfo_open(FILEINFO_MIME);
 			if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system
 			{
 				$mime = @finfo_file($finfo, $file['tmp_name']);
@@ -1038,7 +1029,9 @@
 		 */
 		if (DIRECTORY_SEPARATOR !== '\\')
 		{
-			$cmd = 'file --brief --mime '.escapeshellarg($file['tmp_name']).' 2>&1';
+			$cmd = function_exists('escapeshellarg')
+				? 'file --brief --mime '.escapeshellarg($file['tmp_name']).' 2>&1'
+				: 'file --brief --mime '.$file['tmp_name'].' 2>&1';
 
 			if (function_exists('exec'))
 			{
diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php
index 0d25338..cbb91c4 100644
--- a/system/libraries/Xmlrpc.php
+++ b/system/libraries/Xmlrpc.php
@@ -174,7 +174,7 @@
 	 * @param	int	port
 	 * @return	void
 	 */
-	public function server($url, $port = 80)
+	public function server($url, $port = 80, $proxy = FALSE, $proxy_port = 8080)
 	{
 		if (strpos($url, 'http') !== 0)
 		{
@@ -190,7 +190,7 @@
 			$path .= '?'.$parts['query'];
 		}
 
-		$this->client = new XML_RPC_Client($path, $parts['host'], $port);
+		$this->client = new XML_RPC_Client($path, $parts['host'], $port, $proxy, $proxy_port);
 	}
 
 	// --------------------------------------------------------------------
@@ -256,7 +256,7 @@
 	 */
 	public function set_debug($flag = TRUE)
 	{
-		$this->debug = ($flag == TRUE);
+		$this->debug = ($flag === TRUE);
 	}
 
 	// --------------------------------------------------------------------
@@ -277,7 +277,7 @@
 			}
 			else
 			{
-				if (is_array($value[0]) && ($value[1] == 'struct' OR $value[1] == 'array'))
+				if (is_array($value[0]) && ($value[1] === 'struct' OR $value[1] === 'array'))
 				{
 					while (list($k) = each($value[0]))
 					{
@@ -385,6 +385,8 @@
 	public $path			= '';
 	public $server			= '';
 	public $port			= 80;
+	public $proxy			= FALSE;
+	public $proxy_port		= 8080;
 	public $errno			= '';
 	public $errstring		= '';
 	public $timeout		= 5;
@@ -398,13 +400,15 @@
 	 * @param	int
 	 * @return	void
 	 */
-	public function __construct($path, $server, $port = 80)
+	public function __construct($path, $server, $port = 80, $proxy = FALSE, $proxy_port = 8080)
 	{
 		parent::__construct();
 
 		$this->port = $port;
 		$this->server = $server;
 		$this->path = $path;
+		$this->proxy = $proxy;
+		$this->proxy_port = $proxy_port;
 	}
 
 	// --------------------------------------------------------------------
@@ -436,7 +440,18 @@
 	 */
 	public function sendPayload($msg)
 	{
-		$fp = @fsockopen($this->server, $this->port,$this->errno, $this->errstring, $this->timeout);
+		if ($this->proxy === FALSE)
+		{
+			$server = $this->server;
+			$port = $this->port;
+		}
+		else
+		{
+			$server = $this->proxy;
+			$port = $this->proxy_port;
+		}
+
+		$fp = @fsockopen($server, $port, $this->errno, $this->errstring, $this->timeout);
 
 		if ( ! is_resource($fp))
 		{
@@ -496,7 +511,7 @@
 	 */
 	public function __construct($val, $code = 0, $fstr = '')
 	{
-		if ($code != 0)
+		if ($code !== 0)
 		{
 			// error
 			$this->errno = $code;
@@ -636,11 +651,11 @@
 	{
 		$kind = $xmlrpc_val->kindOf();
 
-		if ($kind == 'scalar')
+		if ($kind === 'scalar')
 		{
 			return $xmlrpc_val->scalarval();
 		}
-		elseif ($kind == 'array')
+		elseif ($kind === 'array')
 		{
 			reset($xmlrpc_val->me);
 			$b = current($xmlrpc_val->me);
@@ -652,7 +667,7 @@
 			}
 			return $arr;
 		}
-		elseif ($kind == 'struct')
+		elseif ($kind === 'struct')
 		{
 			reset($xmlrpc_val->me['struct']);
 			$arr = array();
@@ -680,7 +695,7 @@
 		$t = 0;
 		if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs))
 		{
-			$fnc = ($utc == TRUE) ? 'gmmktime' : 'mktime';
+			$fnc = ($utc === TRUE) ? 'gmmktime' : 'mktime';
 			$t = $fnc($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
 		}
 		return $t;
@@ -778,7 +793,7 @@
 		}
 
 		// Check for HTTP 200 Response
-		if (strncmp($data, 'HTTP', 4) === 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data))
+		if (strpos($data, 'HTTP') === 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data))
 		{
 			$errstr = substr($data, 0, strpos($data, "\n")-1);
 			return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error'].' ('.$errstr.')');
@@ -873,7 +888,7 @@
 			$errstr_v = $v->me['struct']['faultString'];
 			$errno = $errno_v->scalarval();
 
-			if ($errno == 0)
+			if ($errno === 0)
 			{
 				// FAULT returned, errno needs to reflect that
 				$errno = -1;
@@ -921,9 +936,9 @@
 		if ($this->xh[$the_parser]['isf'] > 1) return;
 
 		// Evaluate and check for correct nesting of XML elements
-		if (count($this->xh[$the_parser]['stack']) == 0)
+		if (count($this->xh[$the_parser]['stack']) === 0)
 		{
-			if ($name != 'METHODRESPONSE' && $name != 'METHODCALL')
+			if ($name !== 'METHODRESPONSE' && $name !== 'METHODCALL')
 			{
 				$this->xh[$the_parser]['isf'] = 2;
 				$this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing';
@@ -968,7 +983,7 @@
 			case 'DOUBLE':
 			case 'DATETIME.ISO8601':
 			case 'BASE64':
-				if ($this->xh[$the_parser]['vt'] != 'value')
+				if ($this->xh[$the_parser]['vt'] !== 'value')
 				{
 					//two data elements inside a value: an error occurred!
 					$this->xh[$the_parser]['isf'] = 2;
@@ -1002,7 +1017,7 @@
 		// Add current element name to stack, to allow validation of nesting
 		array_unshift($this->xh[$the_parser]['stack'], $name);
 
-		$name == 'VALUE' OR $this->xh[$the_parser]['lv'] = 0;
+		$name === 'VALUE' OR $this->xh[$the_parser]['lv'] = 0;
 	}
 
 	// --------------------------------------------------------------------
@@ -1045,20 +1060,20 @@
 			case 'BASE64':
 				$this->xh[$the_parser]['vt'] = strtolower($name);
 
-				if ($name == 'STRING')
+				if ($name === 'STRING')
 				{
 					$this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
 				}
-				elseif ($name == 'DATETIME.ISO8601')
+				elseif ($name === 'DATETIME.ISO8601')
 				{
 					$this->xh[$the_parser]['vt']	= $this->xmlrpcDateTime;
 					$this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac'];
 				}
-				elseif ($name == 'BASE64')
+				elseif ($name === 'BASE64')
 				{
 					$this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']);
 				}
-				elseif ($name == 'BOOLEAN')
+				elseif ($name === 'BOOLEAN')
 				{
 					// Translated BOOLEAN values to TRUE AND FALSE
 					$this->xh[$the_parser]['value'] = (bool) $this->xh[$the_parser]['ac'];
@@ -1093,7 +1108,7 @@
 				// build the XML-RPC value out of the data received, and substitute it
 				$temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']);
 
-				if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] == 'ARRAY')
+				if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] === 'ARRAY')
 				{
 					// Array
 					$this->xh[$the_parser]['valuestack'][0]['values'][] = $temp;
@@ -1151,9 +1166,9 @@
 		if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already
 
 		// If a value has not been found
-		if ($this->xh[$the_parser]['lv'] != 3)
+		if ($this->xh[$the_parser]['lv'] !== 3)
 		{
-			if ($this->xh[$the_parser]['lv'] == 1)
+			if ($this->xh[$the_parser]['lv'] === 1)
 			{
 				$this->xh[$the_parser]['lv'] = 2; // Found a value
 			}
@@ -1204,7 +1219,7 @@
 				{
 					// 'bits' is for the MetaWeblog API image bits
 					// @todo - this needs to be made more general purpose
-					$array[$key] = ($key == 'bits' OR $this->xss_clean == FALSE) ? $array[$key] : $CI->security->xss_clean($array[$key]);
+					$array[$key] = ($key === 'bits' OR $this->xss_clean === FALSE) ? $array[$key] : $CI->security->xss_clean($array[$key]);
 				}
 			}
 
@@ -1242,11 +1257,11 @@
 	{
 		$kind = $param->kindOf();
 
-		if ($kind == 'scalar')
+		if ($kind === 'scalar')
 		{
 			return $param->scalarval();
 		}
-		elseif ($kind == 'array')
+		elseif ($kind === 'array')
 		{
 			reset($param->me);
 			$b = current($param->me);
@@ -1259,7 +1274,7 @@
 
 			return $arr;
 		}
-		elseif ($kind == 'struct')
+		elseif ($kind === 'struct')
 		{
 			reset($param->me['struct']);
 			$arr = array();
@@ -1298,19 +1313,19 @@
 	{
 		parent::__construct();
 
-		if ($val != -1 OR $type != '')
+		if ($val !== -1 OR $type !== '')
 		{
-			$type = $type == '' ? 'string' : $type;
+			$type = $type === '' ? 'string' : $type;
 
-			if ($this->xmlrpcTypes[$type] == 1)
+			if ($this->xmlrpcTypes[$type] === 1)
 			{
 				$this->addScalar($val,$type);
 			}
-			elseif ($this->xmlrpcTypes[$type] == 2)
+			elseif ($this->xmlrpcTypes[$type] === 2)
 			{
 				$this->addArray($val);
 			}
-			elseif ($this->xmlrpcTypes[$type] == 3)
+			elseif ($this->xmlrpcTypes[$type] === 3)
 			{
 				$this->addStruct($val);
 			}
@@ -1330,24 +1345,24 @@
 	{
 		$typeof = $this->xmlrpcTypes[$type];
 
-		if ($this->mytype == 1)
+		if ($this->mytype === 1)
 		{
 			echo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />';
 			return 0;
 		}
 
-		if ($typeof != 1)
+		if ($typeof !== 1)
 		{
 			echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />';
 			return 0;
 		}
 
-		if ($type == $this->xmlrpcBoolean)
+		if ($type === $this->xmlrpcBoolean)
 		{
 			$val = (int) (strcasecmp($val,'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false')));
 		}
 
-		if ($this->mytype == 2)
+		if ($this->mytype === 2)
 		{
 			// adding to an array here
 			$ar = $this->me['array'];
@@ -1374,7 +1389,7 @@
 	 */
 	public function addArray($vals)
 	{
-		if ($this->mytype != 0)
+		if ($this->mytype !== 0)
 		{
 			echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
 			return 0;
@@ -1395,7 +1410,7 @@
 	 */
 	public function addStruct($vals)
 	{
-		if ($this->mytype != 0)
+		if ($this->mytype !== 0)
 		{
 			echo '<strong>XML_RPC_Values</strong>: already initialized as a [' . $this->kindOf() . ']<br />';
 			return 0;
diff --git a/system/libraries/Xmlrpcs.php b/system/libraries/Xmlrpcs.php
index 1853906..e81f2ca 100644
--- a/system/libraries/Xmlrpcs.php
+++ b/system/libraries/Xmlrpcs.php
@@ -208,7 +208,7 @@
 		//  Get Data
 		//-------------------------------------
 
-		if ($data == '')
+		if ($data === '')
 		{
 			$data = $HTTP_RAW_POST_DATA;
 		}
@@ -303,9 +303,9 @@
 		$methName = $m->method_name;
 
 		// Check to see if it is a system call
-		$system_call = (strncmp($methName, 'system', 5) === 0);
+		$system_call = (strpos($methName, 'system') === 0);
 
-		if ($this->xss_clean == FALSE)
+		if ($this->xss_clean === FALSE)
 		{
 			$m->xss_clean = FALSE;
 		}
@@ -324,7 +324,7 @@
 		//-------------------------------------
 
 		$method_parts = explode('.', $this->methods[$methName]['function']);
-		$objectCall = (isset($method_parts[1]) && $method_parts[1] != '');
+		$objectCall = (isset($method_parts[1]) && $method_parts[1] !== '');
 
 		if ($system_call === TRUE)
 		{
@@ -356,9 +356,9 @@
 					for ($n = 0, $mc = count($m->params); $n < $mc; $n++)
 					{
 						$p = $m->params[$n];
-						$pt = ($p->kindOf() == 'scalar') ? $p->scalarval() : $p->kindOf();
+						$pt = ($p->kindOf() === 'scalar') ? $p->scalarval() : $p->kindOf();
 
-						if ($pt != $current_sig[$n+1])
+						if ($pt !== $current_sig[$n+1])
 						{
 							$pno = $n+1;
 							$wanted = $current_sig[$n+1];
@@ -527,7 +527,7 @@
 
 			$attempt = $this->_execute($m);
 
-			if ($attempt->faultCode() != 0)
+			if ($attempt->faultCode() !== 0)
 			{
 				return $attempt;
 			}
@@ -567,7 +567,7 @@
 	 */
 	public function do_multicall($call)
 	{
-		if ($call->kindOf() != 'struct')
+		if ($call->kindOf() !== 'struct')
 		{
 			return $this->multicall_error('notstruct');
 		}
@@ -577,13 +577,13 @@
 		}
 
 		list($scalar_type,$scalar_value)=each($methName->me);
-		$scalar_type = $scalar_type == $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type;
+		$scalar_type = $scalar_type === $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type;
 
-		if ($methName->kindOf() != 'scalar' OR $scalar_type != 'string')
+		if ($methName->kindOf() !== 'scalar' OR $scalar_type !== 'string')
 		{
 			return $this->multicall_error('notstring');
 		}
-		elseif ($scalar_value == 'system.multicall')
+		elseif ($scalar_value === 'system.multicall')
 		{
 			return $this->multicall_error('recursion');
 		}
@@ -591,7 +591,7 @@
 		{
 			return $this->multicall_error('noparams');
 		}
-		elseif ($params->kindOf() != 'array')
+		elseif ($params->kindOf() !== 'array')
 		{
 			return $this->multicall_error('notarray');
 		}
@@ -606,7 +606,7 @@
 
 		$result = $this->_execute($msg);
 
-		if ($result->faultCode() != 0)
+		if ($result->faultCode() !== 0)
 		{
 			return $this->multicall_error($result);
 		}
diff --git a/system/libraries/Zip.php b/system/libraries/Zip.php
index e0dc637..5c4c257 100644
--- a/system/libraries/Zip.php
+++ b/system/libraries/Zip.php
@@ -40,7 +40,7 @@
  * @author		EllisLab Dev Team
  * @link		http://codeigniter.com/user_guide/libraries/zip.html
  */
-class CI_Zip  {
+class CI_Zip {
 
 	/**
 	 * Zip data in string form
diff --git a/system/libraries/javascript/Jquery.php b/system/libraries/javascript/Jquery.php
index 3c9ae18..44c16b5 100644
--- a/system/libraries/javascript/Jquery.php
+++ b/system/libraries/javascript/Jquery.php
@@ -416,12 +416,12 @@
 
 		$animations = substr($animations, 0, -2); // remove the last ", "
 
-		if ($speed != '')
+		if ($speed !== '')
 		{
 			$speed = ', '.$speed;
 		}
 
-		if ($extra != '')
+		if ($extra !== '')
 		{
 			$extra = ', '.$extra;
 		}
@@ -446,7 +446,7 @@
 		$element = $this->_prep_element($element);
 		$speed = $this->_validate_speed($speed);
 
-		if ($callback != '')
+		if ($callback !== '')
 		{
 			$callback = ", function(){\n{$callback}\n}";
 		}
@@ -471,7 +471,7 @@
 		$element = $this->_prep_element($element);
 		$speed = $this->_validate_speed($speed);
 
-		if ($callback != '')
+		if ($callback !== '')
 		{
 			$callback = ", function(){\n{$callback}\n}";
 		}
@@ -496,7 +496,7 @@
 		$element = $this->_prep_element($element);
 		$speed = $this->_validate_speed($speed);
 
-		if ($callback != '')
+		if ($callback !== '')
 		{
 			$callback = ", function(){\n{$callback}\n}";
 		}
@@ -537,7 +537,7 @@
 		$element = $this->_prep_element($element);
 		$speed = $this->_validate_speed($speed);
 
-		if ($callback != '')
+		if ($callback !== '')
 		{
 			$callback = ", function(){\n{$callback}\n}";
 		}
@@ -562,7 +562,7 @@
 		$element = $this->_prep_element($element);
 		$speed = $this->_validate_speed($speed);
 
-		if ($callback != '')
+		if ($callback !== '')
 		{
 			$callback = ", function(){\n{$callback}\n}";
 		}
@@ -587,7 +587,7 @@
 		$element = $this->_prep_element($element);
 		$speed = $this->_validate_speed($speed);
 
-		if ($callback != '')
+		if ($callback !== '')
 		{
 			$callback = ", function(){\n{$callback}\n}";
 		}
@@ -644,7 +644,7 @@
 		$element = $this->_prep_element($element);
 		$speed = $this->_validate_speed($speed);
 
-		if ($callback != '')
+		if ($callback !== '')
 		{
 			$callback = ", function(){\n{$callback}\n}";
 		}
@@ -672,7 +672,7 @@
 		$controller = (strpos('://', $controller) === FALSE) ? $controller : $this->CI->config->site_url($controller);
 
 		// ajaxStart and ajaxStop are better choices here... but this is a stop gap
-		if ($this->CI->config->item('javascript_ajax_img') == '')
+		if ($this->CI->config->item('javascript_ajax_img') === '')
 		{
 			$loading_notifier = 'Loading...';
 		}
@@ -685,7 +685,7 @@
 			."\t\t$(".$container.').prepend("'.$loading_notifier."\");\n"; // to replace with an image
 
 		$request_options = '';
-		if ($options != '')
+		if ($options !== '')
 		{
 			$request_options .= ', {'
 					.(is_array($options) ? "'".implode("', '", $options)."'" : "'".str_replace(':', "':'", $options)."'")
@@ -709,12 +709,12 @@
 	 */
 	protected function _zebraTables($class = '', $odd = 'odd', $hover = '')
 	{
-		$class = ($class != '') ? '.'.$class : '';
+		$class = ($class !== '') ? '.'.$class : '';
 		$zebra  = "\t\$(\"table{$class} tbody tr:nth-child(even)\").addClass(\"{$odd}\");";
 
 		$this->jquery_code_for_compile[] = $zebra;
 
-		if ($hover != '')
+		if ($hover !== '')
 		{
 			$hover = $this->hover("table{$class} tbody tr", "$(this).addClass('hover');", "$(this).removeClass('hover');");
 		}
@@ -739,7 +739,7 @@
 		// may want to make this configurable down the road
 		$corner_location = '/plugins/jquery.corner.js';
 
-		if ($corner_style != '')
+		if ($corner_style !== '')
 		{
 			$corner_style = '"'.$corner_style.'"';
 		}
@@ -972,7 +972,7 @@
 	 */
 	protected function _prep_element($element)
 	{
-		if ($element != 'this')
+		if ($element !== 'this')
 		{
 			$element = '"'.$element.'"';
 		}
diff --git a/tests/README.md b/tests/README.md
index c8fc608..d600951 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -21,8 +21,8 @@
 
 vfsStream
 
-	pear channel-discover pear.php-tools.net
-	pear install pat/vfsStream-alpha
+	pear channel-discover pear.bovigo.org
+	pear install bovigo/vfsStream-beta
 
 #### Installation of PEAR and PHPUnit on Ubuntu
 
@@ -37,11 +37,11 @@
 	pear channel-discover pear.phpunit.de
 	pear channel-discover pear.symfony-project.com
 	pear channel-discover components.ez.no
-	pear channel-discover pear.php-tools.net 
+	pear channel-discover pear.bovigo.org
 
 	# Finally install PHPUnit and vfsStream (including dependencies)
 	pear install --alldeps phpunit/PHPUnit
-	pear install --alldeps pat/vfsStream-alpha
+	pear install --alldeps bovigo/vfsStream-beta
 
 	# Finally, run 'phpunit' from within the ./tests directory
 	# and you should be on your way!
diff --git a/tests/codeigniter/Setup_test.php b/tests/codeigniter/Setup_test.php
index b48e32b..5317c56 100644
--- a/tests/codeigniter/Setup_test.php
+++ b/tests/codeigniter/Setup_test.php
@@ -1,13 +1,13 @@
 <?php
 
 class Setup_test extends PHPUnit_Framework_TestCase {
-	
-	function test_bootstrap_constants()
+
+	public function test_bootstrap_constants()
 	{
 		$this->assertTrue(defined('PROJECT_BASE'));
 		$this->assertTrue(defined('BASEPATH'));
 		$this->assertTrue(defined('APPPATH'));
 		$this->assertTrue(defined('VIEWPATH'));
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/core/Benchmark_test.php b/tests/codeigniter/core/Benchmark_test.php
index 109b388..a239ba5 100644
--- a/tests/codeigniter/core/Benchmark_test.php
+++ b/tests/codeigniter/core/Benchmark_test.php
@@ -1,14 +1,14 @@
 <?php
 
 class Benchmark_test extends CI_TestCase {
-	
+
 	public function set_up()
 	{
 		$this->benchmark = new Mock_Core_Benchmark();
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	public function test_mark()
 	{
 		$this->assertEmpty($this->benchmark->marker);
@@ -18,7 +18,7 @@
 		$this->assertEquals(1, count($this->benchmark->marker));
 		$this->assertArrayHasKey('code_start', $this->benchmark->marker);
 	}
-	
+
 	// --------------------------------------------------------------------
 
 	public function test_elapsed_time()
@@ -29,7 +29,7 @@
 		$this->benchmark->mark('code_start');
 		sleep(1);
 		$this->benchmark->mark('code_end');
-		
+
 		$this->assertEquals('1.0', $this->benchmark->elapsed_time('code_start', 'code_end', 1));
 	}
 
@@ -39,4 +39,5 @@
 	{
 		$this->assertEquals('{memory_usage}', $this->benchmark->memory_usage());
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/core/Common_test.php b/tests/codeigniter/core/Common_test.php
index dded2e8..f9bf6c2 100644
--- a/tests/codeigniter/core/Common_test.php
+++ b/tests/codeigniter/core/Common_test.php
@@ -1,13 +1,13 @@
 <?php
 
 class Common_test extends CI_TestCase {
-	
+
 	// ------------------------------------------------------------------------
-	
+
 	public function test_is_php()
 	{
 		$this->assertEquals(TRUE, is_php('1.2.0'));
 		$this->assertEquals(FALSE, is_php('9999.9.9'));
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/core/Config_test.php b/tests/codeigniter/core/Config_test.php
index 30f0cc6..30cb90a 100644
--- a/tests/codeigniter/core/Config_test.php
+++ b/tests/codeigniter/core/Config_test.php
@@ -5,7 +5,7 @@
 	public function set_up()
 	{
 		$cls =& $this->ci_core_class('cfg');
-				
+
 		// set predictable config values
 		$this->ci_set_config(array(
 			'index_page'		=> 'index.php',
@@ -13,9 +13,9 @@
 			'subclass_prefix'	=> 'MY_'
 		));
 
-		$this->config = new $cls;	
+		$this->config = new $cls;
 	}
-	
+
 	// --------------------------------------------------------------------
 
 	public function test_item()
@@ -24,30 +24,30 @@
 
 		// Bad Config value
 		$this->assertFalse($this->config->item('no_good_item'));
-		
+
 		// Index
 		$this->assertFalse($this->config->item('no_good_item', 'bad_index'));
 		$this->assertFalse($this->config->item('no_good_item', 'default'));
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	public function test_set_item()
 	{
 		$this->assertFalse($this->config->item('not_yet_set'));
-		
+
 		$this->config->set_item('not_yet_set', 'is set');
-		
+
 		$this->assertEquals('is set', $this->config->item('not_yet_set'));
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_slash_item()
 	{
 		// Bad Config value
 		$this->assertFalse($this->config->slash_item('no_good_item'));
-		
+
 		$this->assertEquals('http://example.com/', $this->config->slash_item('base_url'));
 
 		$this->assertEquals('MY_/', $this->config->slash_item('subclass_prefix'));
@@ -58,33 +58,33 @@
 	public function test_site_url()
 	{
 		$this->assertEquals('http://example.com/index.php', $this->config->site_url());
-		
+
 		$base_url = $this->config->item('base_url');
-		
+
 		$this->config->set_item('base_url', '');
-		
+
 		$q_string = $this->config->item('enable_query_strings');
-		
+
 		$this->config->set_item('enable_query_strings', FALSE);
 
 		$this->assertEquals('index.php/test', $this->config->site_url('test'));
 		$this->assertEquals('index.php/test/1', $this->config->site_url(array('test', '1')));
-		
+
 		$this->config->set_item('enable_query_strings', TRUE);
 
 		$this->assertEquals('index.php?test', $this->config->site_url('test'));
 		$this->assertEquals('index.php?0=test&1=1', $this->config->site_url(array('test', '1')));
-		
+
 		$this->config->set_item('base_url', $base_url);
 
 		$this->assertEquals('http://example.com/index.php?test', $this->config->site_url('test'));
-		
+
 		// back to home base
-		$this->config->set_item('enable_query_strings', $q_string);				
+		$this->config->set_item('enable_query_strings', $q_string);
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_system_url()
 	{
 		$this->assertEquals('http://example.com/system/', $this->config->system_url());
diff --git a/tests/codeigniter/core/Input_test.php b/tests/codeigniter/core/Input_test.php
index a066d99..fe87388 100644
--- a/tests/codeigniter/core/Input_test.php
+++ b/tests/codeigniter/core/Input_test.php
@@ -1,7 +1,7 @@
 <?php
 
 class Input_test extends CI_TestCase {
-	
+
 	public function set_up()
 	{
 		// Set server variable to GET as default, since this will leave unset in STDIN env
@@ -17,9 +17,9 @@
 
 		$this->input = new Mock_Core_Input($security, $utf8);
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	public function test_get_not_exists()
 	{
 		$this->assertEmpty($this->input->get());
@@ -29,8 +29,8 @@
 		$this->assertTrue( ! $this->input->get('foo'));
 
 		// Test we're getting empty results
-		$this->assertTrue($this->input->get() == NULL);
-		$this->assertTrue($this->input->get('foo') == NULL);
+		$this->assertTrue($this->input->get() === NULL);
+		$this->assertTrue($this->input->get('foo') === NULL);
 
 		// Test new 3.0 behaviour for non existant results (used to be FALSE)
 		$this->assertTrue($this->input->get() === NULL);
@@ -38,7 +38,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_get_exist()
 	{
 		$_SERVER['REQUEST_METHOD'] = 'GET';
@@ -49,7 +49,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_get_exist_with_xss_clean()
 	{
 		$_SERVER['REQUEST_METHOD'] = 'GET';
@@ -61,7 +61,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_post_not_exists()
 	{
 		$this->assertEmpty($this->input->post());
@@ -70,15 +70,15 @@
 		$this->assertTrue( ! $this->input->post());
 		$this->assertTrue( ! $this->input->post('foo'));
 
-		$this->assertTrue($this->input->post() == NULL);
-		$this->assertTrue($this->input->post('foo') == NULL);
+		$this->assertTrue($this->input->post() === NULL);
+		$this->assertTrue($this->input->post('foo') === NULL);
 
 		$this->assertTrue($this->input->post() === NULL);
 		$this->assertTrue($this->input->post('foo') === NULL);
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_post_exist()
 	{
 		$_SERVER['REQUEST_METHOD'] = 'POST';
@@ -89,7 +89,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_post_exist_with_xss_clean()
 	{
 		$_SERVER['REQUEST_METHOD'] = 'POST';
@@ -101,7 +101,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_get_post()
 	{
 		$_SERVER['REQUEST_METHOD'] = 'POST';
@@ -111,7 +111,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_cookie()
 	{
 		$_COOKIE['foo'] = 'bar';
@@ -120,14 +120,14 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_server()
 	{
 		$this->assertEquals('GET', $this->input->server('REQUEST_METHOD'));
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_fetch_from_array()
 	{
 		$data = array(
@@ -143,4 +143,19 @@
 		$this->assertEquals("Hello, i try to <script>alert('Hack');</script> your site", $harm);
 		$this->assertEquals("Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site", $harmless);
 	}
+
+	// --------------------------------------------------------------------
+
+	public function test_valid_ip()
+	{
+		$ip_v4 = '192.18.0.1';
+		$this->assertTrue($this->input->valid_ip($ip_v4));
+
+		$ip_v6 = array('2001:0db8:0000:85a3:0000:0000:ac1f:8001', '2001:db8:0:85a3:0:0:ac1f:8001', '2001:db8:0:85a3::ac1f:8001');
+		foreach ($ip_v6 as $ip)
+		{
+			$this->assertTrue($this->input->valid_ip($ip));
+		}
+	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/core/Lang_test.php b/tests/codeigniter/core/Lang_test.php
index a414f0a..a410dab 100644
--- a/tests/codeigniter/core/Lang_test.php
+++ b/tests/codeigniter/core/Lang_test.php
@@ -1,9 +1,9 @@
 <?php
 
 class Lang_test extends CI_TestCase {
-	
+
 	protected $lang;
-	
+
 	public function set_up()
 	{
 		$loader_cls = $this->ci_core_class('load');
@@ -12,20 +12,21 @@
 		$cls = $this->ci_core_class('lang');
 		$this->lang = new $cls;
 	}
-	
-	// --------------------------------------------------------------------
-	
-	public function test_load()
-	{
-		$this->assertTrue($this->lang->load('profiler', 'english'));
-	}
-	
+
 	// --------------------------------------------------------------------
 
-	public function test_line()
+	public function test_load()
 	{
 		$this->assertTrue($this->lang->load('profiler', 'english'));
 		$this->assertEquals('URI STRING', $this->lang->line('profiler_uri_string'));
 	}
-	
+
+	// --------------------------------------------------------------------
+
+	public function test_load_with_unspecified_language()
+	{
+		$this->assertTrue($this->lang->load('profiler'));
+		$this->assertEquals('URI STRING', $this->lang->line('profiler_uri_string'));
+	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/core/Loader_test.php b/tests/codeigniter/core/Loader_test.php
index 4300865..fdea962 100644
--- a/tests/codeigniter/core/Loader_test.php
+++ b/tests/codeigniter/core/Loader_test.php
@@ -1,35 +1,35 @@
 <?php
 
 class Loader_test extends CI_TestCase {
-	
+
 	private $ci_obj;
-	
+
 	public function set_up()
 	{
 		// Instantiate a new loader
 		$this->load = new Mock_Core_Loader();
-		
+
 		// mock up a ci instance
-		$this->ci_obj = new StdClass;
-		
+		$this->ci_obj = new stdClass;
+
 		// Fix get_instance()
 		$this->ci_instance($this->ci_obj);
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_library()
 	{
 		$this->_setup_config_mock();
-		
+
 		// Test loading as an array.
 		$this->assertNull($this->load->library(array('table')));
 		$this->assertTrue(class_exists('CI_Table'), 'Table class exists');
 		$this->assertAttributeInstanceOf('CI_Table', 'table', $this->ci_obj);
-		
+
 		// Test no lib given
 		$this->assertEquals(FALSE, $this->load->library());
-		
+
 		// Test a string given to params
 		$this->assertEquals(NULL, $this->load->library('table', ' '));
 	}
@@ -39,20 +39,18 @@
 	public function test_load_library_in_application_dir()
 	{
 		$this->_setup_config_mock();
-		
+
 		$content = '<?php class Super_test_library {} ';
-		
-		$model = vfsStream::newFile('Super_test_library.php')->withContent($content)
-														->at($this->load->libs_dir);
-		
+
+		$model = vfsStream::newFile('Super_test_library.php')->withContent($content)->at($this->load->libs_dir);
 		$this->assertNull($this->load->library('super_test_library'));
-		
+
 		// Was the model class instantiated.
-		$this->assertTrue(class_exists('Super_test_library'));		
+		$this->assertTrue(class_exists('Super_test_library'));
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	private function _setup_config_mock()
 	{
 		// Mock up a config object until we
@@ -61,7 +59,7 @@
 		$config->expects($this->any())
 			   ->method('load')
 			   ->will($this->returnValue(TRUE));
-		
+
 		// Add the mock to our stdClass
 		$this->ci_instance_var('config', $config);
 	}
@@ -73,64 +71,62 @@
 		$this->setExpectedException(
 			'RuntimeException',
 			'CI Error: Unable to locate the model you have specified: ci_test_nonexistent_model.php'
-			);
-			
+		);
+
 		$this->load->model('ci_test_nonexistent_model.php');
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	/**
 	 * @coverts CI_Loader::model
 	 */
 	public function test_models()
 	{
 		$this->ci_set_core_class('model', 'CI_Model');
-		
+
 		$content = '<?php class Unit_test_model extends CI_Model {} ';
-		
-		$model = vfsStream::newFile('unit_test_model.php')->withContent($content)
-														->at($this->load->models_dir);
-		
+
+		$model = vfsStream::newFile('unit_test_model.php')->withContent($content)->at($this->load->models_dir);
+
 		$this->assertNull($this->load->model('unit_test_model'));
-		
+
 		// Was the model class instantiated.
 		$this->assertTrue(class_exists('Unit_test_model'));
-		
+
 		// Test no model given
-		$this->assertNull($this->load->model(''));	
+		$this->assertNull($this->load->model(''));
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	// public function testDatabase()
 	// {
 	// 	$this->assertEquals(NULL, $this->load->database());
-	// 	$this->assertEquals(NULL, $this->load->dbutil());		
+	// 	$this->assertEquals(NULL, $this->load->dbutil());
 	// }
 
 	// --------------------------------------------------------------------
-	
+
 	/**
 	 * @coverts CI_Loader::view
 	 */
 	public function test_load_view()
 	{
 		$this->ci_set_core_class('output', 'CI_Output');
-		
+
 		$content = 'This is my test page.  <?php echo $hello; ?>';
-		$view = vfsStream::newFile('unit_test_view.php')->withContent($content)
-														->at($this->load->views_dir);
-		
+		$view = vfsStream::newFile('unit_test_view.php')->withContent($content)->at($this->load->views_dir);
+
 		// Use the optional return parameter in this test, so the view is not
 		// run through the output class.
 		$this->assertEquals('This is my test page.  World!',
 		$this->load->view('unit_test_view', array('hello' => "World!"), TRUE));
-		
+
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	/**
 	 * @coverts CI_Loader::view
 	 */
@@ -139,8 +135,8 @@
 		$this->setExpectedException(
 			'RuntimeException',
 			'CI Error: Unable to load the requested file: ci_test_nonexistent_view.php'
-			);
-			
+		);
+
 		$this->load->view('ci_test_nonexistent_view', array('foo' => 'bar'));
 	}
 
@@ -149,87 +145,77 @@
 	public function test_file()
 	{
 		$content = 'Here is a test file, which we will load now.';
-		$file = vfsStream::newFile('ci_test_mock_file.php')->withContent($content)
-														   ->at($this->load->views_dir);
-		
+		$file = vfsStream::newFile('ci_test_mock_file.php')->withContent($content)->at($this->load->views_dir);
+
 		// Just like load->view(), take the output class out of the mix here.
-		$load = $this->load->file(vfsStream::url('application').'/views/ci_test_mock_file.php', 
-								TRUE);
-		
+		$load = $this->load->file(vfsStream::url('application').'/views/ci_test_mock_file.php', TRUE);
+
 		$this->assertEquals($content, $load);
-		
+
 		$this->setExpectedException(
 			'RuntimeException',
 			'CI Error: Unable to load the requested file: ci_test_file_not_exists'
-			);
-		
+		);
+
 		$this->load->file('ci_test_file_not_exists', TRUE);
-		
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_vars()
 	{
-		$vars = array(
-			'foo'	=> 'bar'
-		);
-		
-		$this->assertNull($this->load->vars($vars));
+		$this->assertNull($this->load->vars(array('foo' => 'bar')));
 		$this->assertNull($this->load->vars('foo', 'bar'));
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_helper()
 	{
 		$this->assertEquals(NULL, $this->load->helper('array'));
-		
+
 		$this->setExpectedException(
 			'RuntimeException',
 			'CI Error: Unable to load the requested file: helpers/bad_helper.php'
-			);
-		
+		);
+
 		$this->load->helper('bad');
 	}
-	
+
 	// --------------------------------------------------------------------
 
 	public function test_loading_multiple_helpers()
 	{
 		$this->assertEquals(NULL, $this->load->helpers(array('file', 'array', 'string')));
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	// public function testLanguage()
 	// {
 	// 	$this->assertEquals(NULL, $this->load->language('test'));
-	// }	
+	// }
 
 	// --------------------------------------------------------------------
 
 	public function test_load_config()
 	{
 		$this->_setup_config_mock();
-		
 		$this->assertNull($this->load->config('config', FALSE));
 	}
-	
+
 	// --------------------------------------------------------------------
 
 	public function test_load_bad_config()
 	{
 		$this->_setup_config_mock();
-		
+
 		$this->setExpectedException(
 			'RuntimeException',
 			'CI Error: The configuration file foobar.php does not exist.'
-			);
-		
+		);
+
 		$this->load->config('foobar', FALSE);
 	}
 
-	// --------------------------------------------------------------------
-	
-}
+}
\ No newline at end of file
diff --git a/tests/codeigniter/core/Security_test.php b/tests/codeigniter/core/Security_test.php
index b2f8c69..3f6e3b0 100644
--- a/tests/codeigniter/core/Security_test.php
+++ b/tests/codeigniter/core/Security_test.php
@@ -1,7 +1,7 @@
 <?php
 
 class Security_test extends CI_TestCase {
-	
+
 	public function set_up()
 	{
 		// Set cookie for security test
@@ -14,9 +14,9 @@
 
 		$this->security = new Mock_Core_Security();
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	public function test_csrf_verify()
 	{
 		$_SERVER['REQUEST_METHOD'] = 'GET';
@@ -25,7 +25,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_csrf_verify_invalid()
 	{
 		// Without issuing $_POST[csrf_token_name], this request will triggering CSRF error
@@ -37,7 +37,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_csrf_verify_valid()
 	{
 		$_SERVER['REQUEST_METHOD'] = 'POST';
@@ -47,21 +47,21 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_get_csrf_hash()
 	{
 		$this->assertEquals($this->security->csrf_hash, $this->security->get_csrf_hash());
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_get_csrf_token_name()
 	{
 		$this->assertEquals('ci_csrf_token', $this->security->get_csrf_token_name());
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_xss_clean()
 	{
 		$harm_string = "Hello, i try to <script>alert('Hack');</script> your site";
@@ -72,7 +72,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_xss_hash()
 	{
 		$this->assertEmpty($this->security->xss_hash);
@@ -84,7 +84,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_entity_decode()
 	{
 		$encoded = '&lt;div&gt;Hello &lt;b&gt;Booya&lt;/b&gt;&lt;/div&gt;';
@@ -94,7 +94,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_sanitize_filename()
 	{
 		$filename = './<!--foo-->';
@@ -102,4 +102,5 @@
 
 		$this->assertEquals('foo', $safe_filename);
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/core/URI_test.php b/tests/codeigniter/core/URI_test.php
index e340ddf..60ed1a4 100644
--- a/tests/codeigniter/core/URI_test.php
+++ b/tests/codeigniter/core/URI_test.php
@@ -1,7 +1,7 @@
 <?php
 
 class URI_test extends CI_TestCase {
-	
+
 	public function set_up()
 	{
 		$this->uri = new Mock_Core_URI();
@@ -9,23 +9,21 @@
 
 	// --------------------------------------------------------------------
 
+	/* As of the following commit, _set_uri_string() is a protected method:
+
+		https://github.com/EllisLab/CodeIgniter/commit/d461934184d95b0cfb2feec93f27b621ef72a5c2
+
 	public function test_set_uri_string()
 	{
 		// Slashes get killed
 		$this->uri->_set_uri_string('/');
-		
-		$a = '';
-		$b =& $this->uri->uri_string;
-		
-		$this->assertEquals($a, $b);
-		
+		$this->assertEquals('', $this->uri->uri_string);
+
 		$this->uri->_set_uri_string('nice/uri');
-		
-		$a = 'nice/uri';
-		
-		$this->assertEquals($a, $b);
+		$this->assertEquals('nice/uri', $this->uri->uri_string);
 	}
-	
+	*/
+
 	// --------------------------------------------------------------------
 
 	public function test_fetch_uri_string()
@@ -34,75 +32,61 @@
 
 		// uri_protocol: AUTO
 		$this->uri->config->set_item('uri_protocol', 'AUTO');
-		
+
 		// Test a variety of request uris
 		$requests = array(
 			'/index.php/controller/method' => 'controller/method',
 			'/index.php?/controller/method' => 'controller/method',
 			'/index.php?/controller/method/?var=foo' => 'controller/method'
 		);
-		
+
 		foreach($requests as $request => $expected)
 		{
 			$_SERVER['SCRIPT_NAME'] = '/index.php';
 			$_SERVER['REQUEST_URI'] = $request;
-			
+
 			$this->uri->_fetch_uri_string();
 			$this->assertEquals($expected, $this->uri->uri_string );
 		}
-		
+
 		// Test a subfolder
 		$_SERVER['SCRIPT_NAME'] = '/subfolder/index.php';
 		$_SERVER['REQUEST_URI'] = '/subfolder/index.php/controller/method';
-		
+
 		$this->uri->_fetch_uri_string();
-		
-		$a = 'controller/method';
-		$b = $this->uri->uri_string;
-		
-		$this->assertEquals($a, $b);
-		
+		$this->assertEquals('controller/method', $this->uri->uri_string);
+
 		// death to request uri
 		unset($_SERVER['REQUEST_URI']);
-		
-		// life to path info
-		$_SERVER['PATH_INFO'] = '/controller/method/';
-		
-		$this->uri->_fetch_uri_string();
-		
-		$a = '/controller/method/';
-		$b =& $this->uri->uri_string;
 
-		$this->assertEquals($a, $b);
-		
+		// life to path info
+		$_SERVER['PATH_INFO'] = $a = '/controller/method/';
+
+		$this->uri->_fetch_uri_string();
+		$this->assertEquals($a, $this->uri->uri_string);
+
 		// death to path info
 		// At this point your server must be seriously drunk
 		unset($_SERVER['PATH_INFO']);
-		
-		$_SERVER['QUERY_STRING'] = '/controller/method/';
-		
-		$this->uri->_fetch_uri_string();
 
-		$a = '/controller/method/';
-		$b = $this->uri->uri_string;
-		
-		$this->assertEquals($a, $b);
-		
-		// At this point your server is a labotomy victim
-		
-		unset($_SERVER['QUERY_STRING']);
-		
-		$_GET['/controller/method/'] = '';
-		
+		$_SERVER['QUERY_STRING'] = '/controller/method/';
+
 		$this->uri->_fetch_uri_string();
-		$this->assertEquals($a, $b);
+		$this->assertEquals($a, $this->uri->uri_string);
+
+		// At this point your server is a labotomy victim
+		unset($_SERVER['QUERY_STRING']);
+
+		$_GET['/controller/method/'] = '';
+
+		$this->uri->_fetch_uri_string();
+		$this->assertEquals($a, $this->uri->uri_string);
 
 		// Test coverage implies that these will work
 		// uri_protocol: REQUEST_URI
 		// uri_protocol: CLI
-				
 	}
-	
+
 	// --------------------------------------------------------------------
 
 	public function test_explode_segments()
@@ -113,18 +97,15 @@
 			'/test2/uri2' => array('test2', 'uri2'),
 			'//test3/test3///' => array('test3', 'test3')
 		);
-		
-		foreach($uris as $uri => $a)
+
+		foreach ($uris as $uri => $a)
 		{
 			$this->uri->segments = array();
 			$this->uri->uri_string = $uri;
 			$this->uri->_explode_segments();
-			
-			$b = $this->uri->segments;
-			
-			$this->assertEquals($a, $b);
+
+			$this->assertEquals($a, $this->uri->segments);
 		}
-		
 	}
 
 	// --------------------------------------------------------------------
@@ -133,7 +114,7 @@
 	{
 		$this->uri->config->set_item('enable_query_strings', FALSE);
 		$this->uri->config->set_item('permitted_uri_chars', 'a-z 0-9~%.:_\-');
-		
+
 		$str_in = 'abc01239~%.:_-';
 		$str = $this->uri->_filter_uri($str_in);
 
@@ -145,52 +126,52 @@
 	public function test_filter_uri_escaping()
 	{
 		// ensure escaping even if dodgey characters are permitted
-		
+
 		$this->uri->config->set_item('enable_query_strings', FALSE);
 		$this->uri->config->set_item('permitted_uri_chars', 'a-z 0-9~%.:_\-()$');
 
 		$str = $this->uri->_filter_uri('$destroy_app(foo)');
-		
+
 		$this->assertEquals($str, '&#36;destroy_app&#40;foo&#41;');
 	}
 
 	// --------------------------------------------------------------------
 
-    public function test_filter_uri_throws_error()
-    {
+	public function test_filter_uri_throws_error()
+	{
 		$this->setExpectedException('RuntimeException');
-		
+
 		$this->uri->config->set_item('enable_query_strings', FALSE);
 		$this->uri->config->set_item('permitted_uri_chars', 'a-z 0-9~%.:_\-');
 		$this->uri->_filter_uri('$this()');
-    }
+	}
 
 	// --------------------------------------------------------------------
 
 	public function test_remove_url_suffix()
 	{
 		$this->uri->config->set_item('url_suffix', '.html');
-		
+
 		$this->uri->uri_string = 'controller/method/index.html';
 		$this->uri->_remove_url_suffix();
-		
+
 		$this->assertEquals($this->uri->uri_string, 'controller/method/index');
-		
+
 		$this->uri->uri_string = 'controller/method/index.htmlify.html';
 		$this->uri->_remove_url_suffix();
-		
+
 		$this->assertEquals($this->uri->uri_string, 'controller/method/index.htmlify');
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_segment()
 	{
 		$this->uri->segments = array(1 => 'controller');
 		$this->assertEquals($this->uri->segment(1), 'controller');
 		$this->assertEquals($this->uri->segment(2, 'default'), 'default');
 	}
-	
+
 	// --------------------------------------------------------------------
 
 	public function test_rsegment()
@@ -205,32 +186,33 @@
 	public function test_uri_to_assoc()
 	{
 		$this->uri->segments = array('a', '1', 'b', '2', 'c', '3');
-		
-		$a = array('a' => '1', 'b' => '2', 'c' => '3');
-		$b = $this->uri->uri_to_assoc(1);
-		$this->assertEquals($a, $b);
-		
-		$a = array('b' => '2', 'c' => '3');
-		$b = $this->uri->uri_to_assoc(3);
-		$this->assertEquals($a, $b);
-		
-		
+
+		$this->assertEquals(
+			array('a' => '1', 'b' => '2', 'c' => '3'),
+			$this->uri->uri_to_assoc(1)
+		);
+
+		$this->assertEquals(
+			array('b' => '2', 'c' => '3'),
+			$this->uri->uri_to_assoc(3)
+		);
+
 		$this->uri->keyval = array(); // reset cache
-				
 		$this->uri->segments = array('a', '1', 'b', '2', 'c');
-		
-		$a = array('a' => '1', 'b' => '2', 'c' => FALSE);
-		$b = $this->uri->uri_to_assoc(1);
-		$this->assertEquals($a, $b);
-		
+
+		$this->assertEquals(
+			array('a' => '1', 'b' => '2', 'c' => FALSE),
+			$this->uri->uri_to_assoc(1)
+		);
+
 		$this->uri->keyval = array(); // reset cache
-		
 		$this->uri->segments = array('a', '1');
-		
+
 		// test default
-		$a = array('a' => '1', 'b' => FALSE);
-		$b = $this->uri->uri_to_assoc(1, array('a', 'b'));
-		$this->assertEquals($a, $b);
+		$this->assertEquals(
+			array('a' => '1', 'b' => FALSE),
+			$this->uri->uri_to_assoc(1, array('a', 'b'))
+		);
 	}
 
 	// --------------------------------------------------------------------
@@ -238,33 +220,33 @@
 	public function test_ruri_to_assoc()
 	{
 		$this->uri->rsegments = array('x', '1', 'y', '2', 'z', '3');
-		
-		$a = array('x' => '1', 'y' => '2', 'z' => '3');
-		$b = $this->uri->ruri_to_assoc(1);
-		$this->assertEquals($a, $b);
-		
-		$a = array('y' => '2', 'z' => '3');
-		$b = $this->uri->ruri_to_assoc(3);
-		$this->assertEquals($a, $b);
-		
-		
-		$this->uri->keyval = array(); // reset cache
-				
-		$this->uri->rsegments = array('x', '1', 'y', '2', 'z');
-		
-		$a = array('x' => '1', 'y' => '2', 'z' => FALSE);
-		$b = $this->uri->ruri_to_assoc(1);
-		$this->assertEquals($a, $b);
-		
-		$this->uri->keyval = array(); // reset cache
-		
-		$this->uri->rsegments = array('x', '1');
-		
-		// test default
-		$a = array('x' => '1', 'y' => FALSE);
-		$b = $this->uri->ruri_to_assoc(1, array('x', 'y'));
-		$this->assertEquals($a, $b);
 
+		$this->assertEquals(
+			array('x' => '1', 'y' => '2', 'z' => '3'),
+			$this->uri->ruri_to_assoc(1)
+		);
+
+		$this->assertEquals(
+			array('y' => '2', 'z' => '3'),
+			$this->uri->ruri_to_assoc(3)
+		);
+
+		$this->uri->keyval = array(); // reset cache
+		$this->uri->rsegments = array('x', '1', 'y', '2', 'z');
+
+		$this->assertEquals(
+			array('x' => '1', 'y' => '2', 'z' => FALSE),
+			$this->uri->ruri_to_assoc(1)
+		);
+
+		$this->uri->keyval = array(); // reset cache
+		$this->uri->rsegments = array('x', '1');
+
+		// test default
+		$this->assertEquals(
+			array('x' => '1', 'y' => FALSE),
+			$this->uri->ruri_to_assoc(1, array('x', 'y'))
+		);
 	}
 
 	// --------------------------------------------------------------------
@@ -272,11 +254,7 @@
 	public function test_assoc_to_uri()
 	{
 		$this->uri->config->set_item('uri_string_slashes', 'none');
-		
-		$arr = array('a' => 1, 'b' => 2);
-		$a = 'a/1/b/2';
-		$b = $this->uri->assoc_to_uri($arr);
-		$this->assertEquals($a, $b);
+		$this->assertEquals('a/1/b/2', $this->uri->assoc_to_uri(array('a' => '1', 'b' => '2')));
 	}
 
 	// --------------------------------------------------------------------
@@ -286,28 +264,18 @@
 		$this->uri->segments[1] = 'segment';
 		$this->uri->rsegments[1] = 'segment';
 
-		$a = '/segment/';
-		$b = $this->uri->slash_segment(1, 'both');
-		$this->assertEquals($a, $b);
-		$b = $this->uri->slash_rsegment(1, 'both');
-		$this->assertEquals($a, $b);
-		
+		$this->assertEquals('/segment/', $this->uri->slash_segment(1, 'both'));
+		$this->assertEquals('/segment/', $this->uri->slash_rsegment(1, 'both'));
+
 		$a = '/segment';
-		$b = $this->uri->slash_segment(1, 'leading');
-		$this->assertEquals($a, $b);
-		$b = $this->uri->slash_rsegment(1, 'leading');
-		$this->assertEquals($a, $b);
-		
-		$a = 'segment/';
-		$b = $this->uri->slash_segment(1, 'trailing');
-		$this->assertEquals($a, $b);
-		$b = $this->uri->slash_rsegment(1, 'trailing');
-		$this->assertEquals($a, $b);
+		$this->assertEquals('/segment', $this->uri->slash_segment(1, 'leading'));
+		$this->assertEquals('/segment', $this->uri->slash_rsegment(1, 'leading'));
+
+		$this->assertEquals('segment/', $this->uri->slash_segment(1, 'trailing'));
+		$this->assertEquals('segment/', $this->uri->slash_rsegment(1, 'trailing'));
 	}
 
-
 }
-// END URI_test Class
 
 /* End of file URI_test.php */
-/* Location: ./tests/core/URI_test.php */
+/* Location: ./tests/core/URI_test.php */
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/count_test.php b/tests/codeigniter/database/query_builder/count_test.php
index 5e69169..90ac528 100644
--- a/tests/codeigniter/database/query_builder/count_test.php
+++ b/tests/codeigniter/database/query_builder/count_test.php
@@ -22,10 +22,7 @@
 	 */
 	public function test_count_all()
 	{
-		$job_count = $this->db->count_all('job');
-		
-		// Check the result
-		$this->assertEquals(4, $job_count);
+		$this->assertEquals(4, $this->db->count_all('job'));
 	}
 
 	// ------------------------------------------------------------------------
@@ -35,10 +32,7 @@
 	 */
 	public function test_count_all_results()
 	{
-		$job_count = $this->db->like('name', 'ian')
-		                      ->count_all_results('job');
-		
-		// Check the result
-		$this->assertEquals(2, $job_count);
+		$this->assertEquals(2, $this->db->like('name', 'ian')->count_all_results('job'));
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/delete_test.php b/tests/codeigniter/database/query_builder/delete_test.php
index 84ea761..ab9d97f 100644
--- a/tests/codeigniter/database/query_builder/delete_test.php
+++ b/tests/codeigniter/database/query_builder/delete_test.php
@@ -23,9 +23,7 @@
 	public function test_delete()
 	{
 		// Check initial record
-		$job1 = $this->db->where('id', 1)
-							->get('job')
-							->row();
+		$job1 = $this->db->where('id', 1)->get('job')->row();
 
 		$this->assertEquals('Developer', $job1->name);
 
@@ -33,8 +31,7 @@
 		$this->db->delete('job', array('id' => 1));
 
 		// Check the record
-		$job1 = $this->db->where('id', 1)
-							->get('job');
+		$job1 = $this->db->where('id', 1)->get('job');
 
 		$this->assertEmpty($job1->result_array());
 	}
@@ -47,13 +44,8 @@
 	public function test_delete_several_tables()
 	{
 		// Check initial record
-		$user4 = $this->db->where('id', 4)
-							->get('user')
-							->row();
-
-		$job4 = $this->db->where('id', 4)
-							->get('job')
-							->row();
+		$user4 = $this->db->where('id', 4)->get('user')->row();
+		$job4 = $this->db->where('id', 4)->get('job')->row();
 
 		$this->assertEquals('Musician', $job4->name);
 		$this->assertEquals('Chris Martin', $user4->name);
diff --git a/tests/codeigniter/database/query_builder/distinct_test.php b/tests/codeigniter/database/query_builder/distinct_test.php
index 925eadb..cc98009 100644
--- a/tests/codeigniter/database/query_builder/distinct_test.php
+++ b/tests/codeigniter/database/query_builder/distinct_test.php
@@ -23,11 +23,10 @@
 	public function test_distinct()
 	{
 		$users = $this->db->select('country')
-							  ->distinct()
-		                      ->get('user')
-		                      ->result_array();
-		
-		// Check the result
+					->distinct()
+					->get('user')
+					->result_array();
+
 		$this->assertEquals(3, count($users));
 	}
 
diff --git a/tests/codeigniter/database/query_builder/escape_test.php b/tests/codeigniter/database/query_builder/escape_test.php
index 5d575a3..c6380dd 100644
--- a/tests/codeigniter/database/query_builder/escape_test.php
+++ b/tests/codeigniter/database/query_builder/escape_test.php
@@ -35,7 +35,7 @@
 		}
 
 		$res = $this->db->query($sql)->result_array();
-		
+
 		// Check the result
 		$this->assertEquals(1, count($res));
 	}
@@ -60,8 +60,9 @@
 		}
 
 		$res = $this->db->query($sql)->result_array();
-		
+
 		// Check the result
 		$this->assertEquals(2, count($res));
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/from_test.php b/tests/codeigniter/database/query_builder/from_test.php
index 95ae4df..7aaae34 100644
--- a/tests/codeigniter/database/query_builder/from_test.php
+++ b/tests/codeigniter/database/query_builder/from_test.php
@@ -23,10 +23,9 @@
 	public function test_from_simple()
 	{
 		$jobs = $this->db->from('job')
-		                      ->get()
-		                      ->result_array();
-		
-		// Check items
+					->get()
+					->result_array();
+
 		$this->assertEquals(4, count($jobs));
 	}
 
@@ -38,14 +37,13 @@
 	public function test_from_with_where()
 	{
 		$job1 = $this->db->from('job')
-							->where('id', 1)
-		                    ->get()
-		                    ->row();
-		
-		// Check the result
+					->where('id', 1)
+					->get()
+					->row();
+
 		$this->assertEquals('1', $job1->id);
 		$this->assertEquals('Developer', $job1->name);
 		$this->assertEquals('Awesome job, but sometimes makes you bored', $job1->description);
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/get_test.php b/tests/codeigniter/database/query_builder/get_test.php
index 0751c93..1560275 100644
--- a/tests/codeigniter/database/query_builder/get_test.php
+++ b/tests/codeigniter/database/query_builder/get_test.php
@@ -23,7 +23,7 @@
 	public function test_get_simple()
 	{
 		$jobs = $this->db->get('job')->result_array();
-		
+
 		// Dummy jobs contain 4 rows
 		$this->assertCount(4, $jobs);
 
@@ -41,13 +41,13 @@
 	 */
 	public function test_get_where()
 	{
-		$job1 = $this->db->get('job', array('id' => 1))->result_array();
-		
+		$job1 = $this->db->get_where('job', array('id' => 1))->result_array();
+
 		// Dummy jobs contain 1 rows
 		$this->assertCount(1, $job1);
 
 		// Check rows item
 		$this->assertEquals('Developer', $job1[0]['name']);
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/group_test.php b/tests/codeigniter/database/query_builder/group_test.php
index 7d8abc3..5249f7c 100644
--- a/tests/codeigniter/database/query_builder/group_test.php
+++ b/tests/codeigniter/database/query_builder/group_test.php
@@ -23,12 +23,11 @@
 	public function test_group_by()
 	{
 		$jobs = $this->db->select('name')
-							  ->from('job')
-							  ->group_by('name')
-		                      ->get()
-		                      ->result_array();
-		
-		// Check the result
+					->from('job')
+					->group_by('name')
+					->get()
+					->result_array();
+
 		$this->assertEquals(4, count($jobs));
 	}
 
@@ -40,14 +39,13 @@
 	public function test_having_by()
 	{
 		$jobs = $this->db->select('name')
-							  ->from('job')
-							  ->group_by('name')
-							  ->having('SUM(id) > 2')
-		                      ->get()
-		                      ->result_array();
-		
-		// Check the result
+					->from('job')
+					->group_by('name')
+					->having('SUM(id) > 2')
+					->get()
+					->result_array();
+
 		$this->assertEquals(2, count($jobs));
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/insert_test.php b/tests/codeigniter/database/query_builder/insert_test.php
index 8ba60e2..a9aafb6 100644
--- a/tests/codeigniter/database/query_builder/insert_test.php
+++ b/tests/codeigniter/database/query_builder/insert_test.php
@@ -26,7 +26,7 @@
 	public function test_insert()
 	{
 		$job_data = array('id' => 1, 'name' => 'Grocery Sales', 'description' => 'Discount!');
-		
+
 		// Do normal insert
 		$this->assertTrue($this->db->insert('job', $job_data));
 
@@ -45,10 +45,10 @@
 	public function test_insert_batch()
 	{
 		$job_datas = array(
-			array('id' => 2, 'name' => 'Commedian', 'description' => 'Theres something in your teeth'), 
+			array('id' => 2, 'name' => 'Commedian', 'description' => 'Theres something in your teeth'),
 			array('id' => 3, 'name' => 'Cab Driver', 'description' => 'Iam yellow'),
 		);
-		
+
 		// Do insert batch except for sqlite driver
 		if (strpos(DB_DRIVER, 'sqlite') === FALSE)
 		{
@@ -62,5 +62,5 @@
 			$this->assertEquals('Cab Driver', $job_3->name);
 		}
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/join_test.php b/tests/codeigniter/database/query_builder/join_test.php
index e05329d..25bd4ac 100644
--- a/tests/codeigniter/database/query_builder/join_test.php
+++ b/tests/codeigniter/database/query_builder/join_test.php
@@ -34,5 +34,25 @@
 		$this->assertEquals('Derek Jones', $job_user[0]['user_name']);
 		$this->assertEquals('Developer', $job_user[0]['job_name']);
 	}
-	
+
+	// ------------------------------------------------------------------------
+
+	public function test_join_escape_multiple_conditions()
+	{
+		// We just need a valid query produced, not one that makes sense
+		$fields = array($this->db->protect_identifiers('table1.field1'), $this->db->protect_identifiers('table2.field2'));
+
+		$expected = 'SELECT '.implode(', ', $fields)
+				."\nFROM ".$this->db->escape_identifiers('table1')
+				."\nLEFT JOIN ".$this->db->escape_identifiers('table2').' ON '.implode(' = ', $fields)
+				.' AND '.$fields[0]." = 'foo' AND ".$fields[1].' = 0';
+
+		$result = $this->db->select('table1.field1, table2.field2')
+				->from('table1')
+				->join('table2', "table1.field1 = table2.field2 AND table1.field1 = 'foo' AND table2.field2 = 0", 'LEFT')
+				->get_compiled_select();
+
+		$this->assertEquals($expected, $result);
+	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/like_test.php b/tests/codeigniter/database/query_builder/like_test.php
index df98c71..5f3e522 100644
--- a/tests/codeigniter/database/query_builder/like_test.php
+++ b/tests/codeigniter/database/query_builder/like_test.php
@@ -86,5 +86,5 @@
 		$this->assertEquals('Accountant', $jobs[1]['name']);
 		$this->assertEquals('Musician', $jobs[2]['name']);
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/limit_test.php b/tests/codeigniter/database/query_builder/limit_test.php
index 704f3b6..a0954c7 100644
--- a/tests/codeigniter/database/query_builder/limit_test.php
+++ b/tests/codeigniter/database/query_builder/limit_test.php
@@ -25,8 +25,7 @@
 		$jobs = $this->db->limit(2)
 		                      ->get('job')
 		                      ->result_array();
-		
-		// Check the result
+
 		$this->assertEquals(2, count($jobs));
 	}
 
@@ -40,10 +39,10 @@
 		$jobs = $this->db->limit(2, 2)
 		                      ->get('job')
 		                      ->result_array();
-		
-		// Check the result
+
 		$this->assertEquals(2, count($jobs));
 		$this->assertEquals('Accountant', $jobs[0]['name']);
 		$this->assertEquals('Musician', $jobs[1]['name']);
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/order_test.php b/tests/codeigniter/database/query_builder/order_test.php
index 01aa1c2..46f452b 100644
--- a/tests/codeigniter/database/query_builder/order_test.php
+++ b/tests/codeigniter/database/query_builder/order_test.php
@@ -25,7 +25,7 @@
 		$jobs = $this->db->order_by('name', 'asc')
 		                      ->get('job')
 		                      ->result_array();
-		
+
 		// Check the result
 		$this->assertEquals(4, count($jobs));
 		$this->assertEquals('Accountant', $jobs[0]['name']);
@@ -44,12 +44,12 @@
 		$jobs = $this->db->order_by('name', 'desc')
 		                      ->get('job')
 		                      ->result_array();
-		
-		// Check the result
+
 		$this->assertEquals(4, count($jobs));
 		$this->assertEquals('Politician', $jobs[0]['name']);
 		$this->assertEquals('Musician', $jobs[1]['name']);
 		$this->assertEquals('Developer', $jobs[2]['name']);
 		$this->assertEquals('Accountant', $jobs[3]['name']);
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/select_test.php b/tests/codeigniter/database/query_builder/select_test.php
index 0d299ed..877b5d8 100644
--- a/tests/codeigniter/database/query_builder/select_test.php
+++ b/tests/codeigniter/database/query_builder/select_test.php
@@ -25,7 +25,7 @@
 		$jobs_name = $this->db->select('name')
 		                      ->get('job')
 		                      ->result_array();
-		
+
 		// Check rows item
 		$this->assertArrayHasKey('name',$jobs_name[0]);
 		$this->assertFalse(array_key_exists('id', $jobs_name[0]));
@@ -42,7 +42,7 @@
 		$job_min = $this->db->select_min('id')
 		                    ->get('job')
 		                    ->row();
-		
+
 		// Minimum id was 1
 		$this->assertEquals('1', $job_min->id);
 	}
@@ -57,7 +57,7 @@
 		$job_max = $this->db->select_max('id')
 		                    ->get('job')
 		                    ->row();
-		
+
 		// Maximum id was 4
 		$this->assertEquals('4', $job_max->id);
 	}
@@ -72,7 +72,7 @@
 		$job_avg = $this->db->select_avg('id')
 		                    ->get('job')
 		                    ->row();
-		
+
 		// Average should be 2.5
 		$this->assertEquals('2.5', $job_avg->id);
 	}
@@ -87,9 +87,9 @@
 		$job_sum = $this->db->select_sum('id')
 		                    ->get('job')
 		                    ->row();
-		
+
 		// Sum of ids should be 10
 		$this->assertEquals('10', $job_sum->id);
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/truncate_test.php b/tests/codeigniter/database/query_builder/truncate_test.php
index 2a9c8a9..09923c7 100644
--- a/tests/codeigniter/database/query_builder/truncate_test.php
+++ b/tests/codeigniter/database/query_builder/truncate_test.php
@@ -24,7 +24,6 @@
 	{
 		// Check initial record
 		$jobs = $this->db->get('job')->result_array();
-
 		$this->assertEquals(4, count($jobs));
 
 		// Do the empty
@@ -32,7 +31,6 @@
 
 		// Check the record
 		$jobs = $this->db->get('job');
-
 		$this->assertEmpty($jobs->result_array());
 	}
 
@@ -45,16 +43,13 @@
 	{
 		// Check initial record
 		$users = $this->db->get('user')->result_array();
-
 		$this->assertEquals(4, count($users));
 
 		// Do the empty
-		$this->db->from('user')
-					->truncate();
+		$this->db->from('user')->truncate();
 
 		// Check the record
 		$users = $this->db->get('user');
-
 		$this->assertEmpty($users->result_array());
 	}
 
diff --git a/tests/codeigniter/database/query_builder/update_test.php b/tests/codeigniter/database/query_builder/update_test.php
index f5bbffd..27a647c 100644
--- a/tests/codeigniter/database/query_builder/update_test.php
+++ b/tests/codeigniter/database/query_builder/update_test.php
@@ -23,23 +23,14 @@
 	public function test_update()
 	{
 		// Check initial record
-		$job1 = $this->db->where('id', 1)
-							->get('job')
-							->row();
-
+		$job1 = $this->db->where('id', 1)->get('job')->row();
 		$this->assertEquals('Developer', $job1->name);
 
 		// Do the update
-		$job_data = array('name' => 'Programmer');
-
-		$this->db->where('id', 1)
-						->update('job', $job_data);
+		$this->db->where('id', 1)->update('job', array('name' => 'Programmer'));
 
 		// Check updated record
-		$job1 = $this->db->where('id', 1)
-							->get('job')
-							->row();
-
+		$job1 = $this->db->where('id', 1)->get('job')->row();
 		$this->assertEquals('Programmer', $job1->name);
 	}
 
@@ -51,10 +42,7 @@
 	public function test_update_with_set()
 	{
 		// Check initial record
-		$job1 = $this->db->where('id', 4)
-							->get('job')
-							->row();
-
+		$job1 = $this->db->where('id', 4)->get('job')->row();
 		$this->assertEquals('Musician', $job1->name);
 
 		// Do the update
@@ -62,10 +50,8 @@
 		$this->db->update('job', NULL, 'id = 4');
 
 		// Check updated record
-		$job1 = $this->db->where('id', 4)
-							->get('job')
-							->row();
-
+		$job1 = $this->db->where('id', 4)->get('job')->row();
 		$this->assertEquals('Vocalist', $job1->name);
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/database/query_builder/where_test.php b/tests/codeigniter/database/query_builder/where_test.php
index 607eaa0..20b7a56 100644
--- a/tests/codeigniter/database/query_builder/where_test.php
+++ b/tests/codeigniter/database/query_builder/where_test.php
@@ -22,11 +22,8 @@
 	 */
 	public function test_where_simple_key_value()
 	{
-		$job1 = $this->db->where('id', 1)
-							->get('job')
-							->row();
+		$job1 = $this->db->where('id', 1)->get('job')->row();
 
-		// Check the result
 		$this->assertEquals('1', $job1->id);
 		$this->assertEquals('Developer', $job1->name);
 	}
@@ -38,11 +35,7 @@
 	 */
 	public function test_where_custom_key_value()
 	{
-		$jobs = $this->db->where('id !=', 1)
-							->get('job')
-							->result_array();
-
-		// Check the result
+		$jobs = $this->db->where('id !=', 1)->get('job')->result_array();
 		$this->assertEquals(3, count($jobs));
 	}
 
@@ -54,16 +47,12 @@
 	public function test_where_associative_array()
 	{
 		$where = array('id >' => 2, 'name !=' => 'Accountant');
-		$jobs = $this->db->where($where)
-							->get('job')
-							->result_array();
+		$jobs = $this->db->where($where)->get('job')->result_array();
 
-		// Check the result
 		$this->assertEquals(1, count($jobs));
 
 		// Should be Musician
 		$job = current($jobs);
-
 		$this->assertEquals('Musician', $job['name']);
 	}
 
@@ -75,16 +64,12 @@
 	public function test_where_custom_string()
 	{
 		$where = "id > 2 AND name != 'Accountant'";
-		$jobs = $this->db->where($where)
-							->get('job')
-							->result_array();
+		$jobs = $this->db->where($where)->get('job')->result_array();
 
-		// Check the result
 		$this->assertEquals(1, count($jobs));
 
 		// Should be Musician
 		$job = current($jobs);
-
 		$this->assertEquals('Musician', $job['name']);
 	}
 
@@ -100,7 +85,6 @@
 							->get('job')
 							->result_array();
 
-		// Check the result
 		$this->assertEquals(3, count($jobs));
 		$this->assertEquals('Developer', $jobs[0]['name']);
 		$this->assertEquals('Politician', $jobs[1]['name']);
@@ -118,7 +102,6 @@
 							->get('job')
 							->result_array();
 
-		// Check the result
 		$this->assertEquals(2, count($jobs));
 		$this->assertEquals('Politician', $jobs[0]['name']);
 		$this->assertEquals('Accountant', $jobs[1]['name']);
@@ -135,10 +118,9 @@
 							->get('job')
 							->result_array();
 
-		// Check the result
 		$this->assertEquals(2, count($jobs));
 		$this->assertEquals('Developer', $jobs[0]['name']);
 		$this->assertEquals('Musician', $jobs[1]['name']);
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/array_helper_test.php b/tests/codeigniter/helpers/array_helper_test.php
index 9cd1596..ba46e86 100644
--- a/tests/codeigniter/helpers/array_helper_test.php
+++ b/tests/codeigniter/helpers/array_helper_test.php
@@ -1,7 +1,7 @@
 <?php
 
 class Array_helper_test extends CI_TestCase {
-	
+
 	public function set_up()
 	{
 		$this->helper('array');
@@ -13,31 +13,31 @@
 			'herb'		=> 'cook'
 		);
 	}
-	
+
 	// ------------------------------------------------------------------------
-	
+
 	public function test_element_with_existing_item()
-	{	
+	{
 		$this->assertEquals(FALSE, element('testing', $this->my_array));
-		
+
 		$this->assertEquals('not set', element('testing', $this->my_array, 'not set'));
-		
+
 		$this->assertEquals('bar', element('foo', $this->my_array));
 	}
-	
-	// ------------------------------------------------------------------------	
+
+	// ------------------------------------------------------------------------
 
 	public function test_random_element()
 	{
 		// Send a string, not an array to random_element
 		$this->assertEquals('my string', random_element('my string'));
-		
+
 		// Test sending an array
 		$this->assertEquals(TRUE, in_array(random_element($this->my_array), $this->my_array));
 	}
 
-	// ------------------------------------------------------------------------	
-	
+	// ------------------------------------------------------------------------
+
 	public function test_elements()
 	{
 		$this->assertEquals(TRUE, is_array(elements('test', $this->my_array)));
diff --git a/tests/codeigniter/helpers/date_helper_test.php b/tests/codeigniter/helpers/date_helper_test.php
index 17d1ef2..1b79b94 100644
--- a/tests/codeigniter/helpers/date_helper_test.php
+++ b/tests/codeigniter/helpers/date_helper_test.php
@@ -5,153 +5,163 @@
 	public function set_up()
 	{
 		$this->helper('date');
+
+		$this->time = time();
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_now_local()
 	{
+		/*
+
 		// This stub job, is simply to cater $config['time_reference']
 		$config = $this->getMock('CI_Config');
 		$config->expects($this->any())
 			   ->method('item')
 			   ->will($this->returnValue('local'));
-		
+
 		// Add the stub to our test instance
 		$this->ci_instance_var('config', $config);
 
-		$expected = time();
-		$test = now();
-		$this->assertEquals($expected, $test);
+		*/
+
+		$this->ci_set_config('time_reference', 'local');
+
+		$this->assertEquals(time(), now());
 	}
 
 	// ------------------------------------------------------------------------
 
-	public function test_now_gmt()
+	public function test_now_utc()
 	{
+		/*
+
 		// This stub job, is simply to cater $config['time_reference']
 		$config = $this->getMock('CI_Config');
 		$config->expects($this->any())
 			   ->method('item')
-			   ->will($this->returnValue('gmt'));
-		
+			   ->will($this->returnValue('UTC'));
+
 		// Add the stub to our stdClass
 		$this->ci_instance_var('config', $config);
 
-		$t = time();
-		$expected = mktime(gmdate("H", $t), gmdate("i", $t), gmdate("s", $t), gmdate("m", $t), gmdate("d", $t), gmdate("Y", $t));
-		$test = now();
-		$this->assertEquals($expected, $test);
+		*/
+
+		$this->assertEquals(
+			mktime(gmdate('G'), gmdate('i'), gmdate('s'), gmdate('n'), gmdate('j'), gmdate('Y')),
+			now('UTC')
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_mdate()
 	{
-		$time = time();
-		$expected = date("Y-m-d - h:i a", $time);
-		$test = mdate("%Y-%m-%d - %h:%i %a", $time);
-		$this->assertEquals($expected, $test);
+		$this->assertEquals(
+			date('Y-m-d - h:i a', $this->time),
+			mdate('%Y-%m-%d - %h:%i %a', $this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_standard_date_rfc822()
 	{
-		$time = time();
-		$format = 'DATE_RFC822';
-		$expected = date("D, d M y H:i:s O", $time);
-		$this->assertEquals($expected, standard_date($format, $time));
+		$this->assertEquals(
+			date(DATE_RFC822, $this->time),
+			standard_date('DATE_RFC822', $this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_standard_date_atom()
 	{
-		$time = time();
-		$format = 'DATE_ATOM';
-		$expected = date("Y-m-d\TH:i:sO", $time);
-		$this->assertEquals($expected, standard_date($format, $time));
+		$this->assertEquals(
+			date(DATE_ATOM, $this->time),
+			standard_date('DATE_ATOM', $this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_standard_date_cookie()
 	{
-		$time = time();
-		$format = 'DATE_COOKIE';
-		$expected = date("l, d-M-y H:i:s \U\T\C", $time);
-		$this->assertEquals($expected, standard_date($format, $time));
+		$this->assertEquals(
+			date(DATE_COOKIE, $this->time),
+			standard_date('DATE_COOKIE', $this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_standard_date_iso8601()
 	{
-		$time = time();
-		$format = 'DATE_ISO8601';
-		$expected = date("Y-m-d\TH:i:sO", $time);
-		$this->assertEquals($expected, standard_date($format, $time));
+		$this->assertEquals(
+			date(DATE_ISO8601, $this->time),
+			standard_date('DATE_ISO8601', $this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_standard_date_rfc850()
 	{
-		$time = time();
-		$format = 'DATE_RFC850';
-		$expected = date("l, d-M-y H:i:s \U\T\C", $time);
-		$this->assertEquals($expected, standard_date($format, $time));
+		$this->assertEquals(
+			date(DATE_RFC850, $this->time),
+			standard_date('DATE_RFC850', $this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_standard_date_rfc1036()
 	{
-		$time = time();
-		$format = 'DATE_RFC1036';
-		$expected = date("D, d M y H:i:s O", $time);
-		$this->assertEquals($expected, standard_date($format, $time));
+		$this->assertEquals(
+			date(DATE_RFC1036, $this->time),
+			standard_date('DATE_RFC1036', $this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_standard_date_rfc1123()
 	{
-		$time = time();
-		$format = 'DATE_RFC1123';
-		$expected = date("D, d M Y H:i:s O", $time);
-		$this->assertEquals($expected, standard_date($format, $time));
+		$this->assertEquals(
+			date(DATE_RFC1123, $this->time),
+			standard_date('DATE_RFC1123', $this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_standard_date_rfc2822()
 	{
-		$time = time();
-		$format = 'DATE_RFC2822';
-		$expected = date("D, d M Y H:i:s O", $time);
-		$this->assertEquals($expected, standard_date($format, $time));
+		$this->assertEquals(
+			date(DATE_RFC2822, $this->time),
+			standard_date('DATE_RFC2822', $this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_standard_date_rss()
 	{
-		$time = time();
-		$format = 'DATE_RSS';
-		$expected = date("D, d M Y H:i:s O", $time);
-		$this->assertEquals($expected, standard_date($format, $time));
+		$this->assertEquals(
+			date(DATE_RSS, $this->time),
+			standard_date('DATE_RSS', $this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_standard_date_w3c()
 	{
-		$time = time();
-		$format = 'DATE_W3C';
-		$expected = date("Y-m-d\TH:i:sO", $time);
-		$this->assertEquals($expected, standard_date($format, $time));
+		$this->assertEquals(
+			date(DATE_W3C, $this->time),
+			standard_date('DATE_W3C', $this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
@@ -183,39 +193,36 @@
 
 	public function test_local_to_gmt()
 	{
-		$t = time();
-		$expected = mktime(gmdate("H", $t), gmdate("i", $t), gmdate("s", $t), gmdate("m", $t), gmdate("d", $t), gmdate("Y", $t));
-		$this->assertEquals($expected, local_to_gmt($t));
+		$this->assertEquals(
+			mktime(
+				gmdate('G', $this->time), gmdate('i', $this->time), gmdate('s', $this->time),
+				gmdate('n', $this->time), gmdate('j', $this->time), gmdate('Y', $this->time)
+			),
+			local_to_gmt($this->time)
+		);
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_gmt_to_local()
 	{
-		$timestamp = '1140153693';
-		$timezone = 'UM8';
-		$daylight_saving = TRUE;
-
-		$this->assertEquals(1140128493, gmt_to_local($timestamp, $timezone, $daylight_saving));
+		$this->assertEquals(1140128493, gmt_to_local('1140153693', 'UM8', TRUE));
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_mysql_to_unix()
 	{
-		$time = time();
-		$this->assertEquals($time, 
-				mysql_to_unix(date("Y-m-d H:i:s", $time)));
+		$this->assertEquals($this->time, mysql_to_unix(date('Y-m-d H:i:s', $this->time)));
 	}
 
 	// ------------------------------------------------------------------------
 
 	public function test_unix_to_human()
 	{
-		$time = time();
-		$this->assertEquals(date("Y-m-d h:i A", $time), unix_to_human($time));
-		$this->assertEquals(date("Y-m-d h:i:s A", $time), unix_to_human($time, TRUE, 'us'));
-		$this->assertEquals(date("Y-m-d H:i:s", $time), unix_to_human($time, TRUE, 'eu'));
+		$this->assertEquals(date('Y-m-d h:i A', $this->time), unix_to_human($this->time));
+		$this->assertEquals(date('Y-m-d h:i:s A', $this->time), unix_to_human($this->time, TRUE, 'us'));
+		$this->assertEquals(date('Y-m-d H:i:s', $this->time), unix_to_human($this->time, TRUE, 'eu'));
 	}
 
 	// ------------------------------------------------------------------------
@@ -223,8 +230,7 @@
 	public function test_human_to_unix()
 	{
 		$date = '2000-12-31 10:00:00 PM';
-		$expected = strtotime($date);
-		$this->assertEquals($expected, human_to_unix($date));
+		$this->assertEquals(strtotime($date), human_to_unix($date));
 		$this->assertFalse(human_to_unix());
 	}
 
@@ -283,6 +289,7 @@
 		$this->assertArrayHasKey('UP3', timezones());
 		$this->assertEquals(0, timezones('non_existant'));
 	}
+
 }
 
 /* End of file date_helper_test.php */
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/directory_helper_test.php b/tests/codeigniter/helpers/directory_helper_test.php
index 3937d29..176ff1d 100644
--- a/tests/codeigniter/helpers/directory_helper_test.php
+++ b/tests/codeigniter/helpers/directory_helper_test.php
@@ -1,41 +1,50 @@
 <?php
 
 class Directory_helper_test extends CI_TestCase {
-	
+
 	public function set_up()
 	{
 		$this->helper('directory');
 
 		vfsStreamWrapper::register();
 		vfsStreamWrapper::setRoot(new vfsStreamDirectory('testDir'));
-		
+
 		$this->_test_dir = vfsStreamWrapper::getRoot();
-	}	
-	
+	}
+
 	public function test_directory_map()
 	{
-		$structure = array('libraries' => array('benchmark.html' => '', 'database' =>
-			array('active_record.html' => '', 'binds.html' => ''), 'email.html' => '', '.hiddenfile.txt' => ''));
-		
+		$structure = array(
+			'libraries' => array(
+				'benchmark.html' => '',
+				'database' => array('active_record.html' => '', 'binds.html' => ''),
+				'email.html' => '',
+				'.hiddenfile.txt' => ''
+			)
+		);
+
 		vfsStream::create($structure, $this->_test_dir);
 
 		// test default recursive behavior
-		$expected = array('libraries' => array('benchmark.html', 'database' =>
-			array('active_record.html', 'binds.html'), 'email.html'));
-			
+		$expected = array(
+			'libraries' => array(
+				'benchmark.html',
+				'database' => array('active_record.html', 'binds.html'),
+				'email.html'
+			)
+		);
+
 		$this->assertEquals($expected, directory_map(vfsStream::url('testDir')));
 
-		// test recursion depth behavior
-		$expected = array('libraries');
-			
-		$this->assertEquals($expected, directory_map(vfsStream::url('testDir'), 1));
-
 		// test detection of hidden files
-		$expected = array('libraries' => array('benchmark.html', 'database' =>
-			array('active_record.html', 'binds.html'), 'email.html', '.hiddenfile.txt'));
-			
+		$expected['libraries'][] = '.hiddenfile.txt';
+
 		$this->assertEquals($expected, directory_map(vfsStream::url('testDir'), FALSE, TRUE));
-  }  
+
+		// test recursion depth behavior
+		$this->assertEquals(array('libraries'), directory_map(vfsStream::url('testDir'), 1));
+	}
+
 }
 
 /* End of file directory_helper_test.php */
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/email_helper_test.php b/tests/codeigniter/helpers/email_helper_test.php
index a01f3d5..fea452f 100644
--- a/tests/codeigniter/helpers/email_helper_test.php
+++ b/tests/codeigniter/helpers/email_helper_test.php
@@ -14,5 +14,5 @@
 		$this->assertEquals(TRUE, valid_email('test@test.com'));
 		$this->assertEquals(TRUE, valid_email('my.test@test.com'));
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/file_helper_test.php b/tests/codeigniter/helpers/file_helper_test.php
index 4b9c294..9b03da9 100644
--- a/tests/codeigniter/helpers/file_helper_test.php
+++ b/tests/codeigniter/helpers/file_helper_test.php
@@ -5,24 +5,23 @@
 	public function set_up()
 	{
 		$this->helper('file');
-		
+
 		vfsStreamWrapper::register();
 		vfsStreamWrapper::setRoot(new vfsStreamDirectory('testDir'));
-		
+
 		$this->_test_dir = vfsStreamWrapper::getRoot();
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	public function test_read_file()
 	{
 		$this->assertFalse(read_file('does_not_exist'));
-		
+
 		$content = 'Jack and Jill went up the mountain to fight a billy goat.';
-		
-		$file = vfsStream::newFile('my_file.txt')->withContent($content)
-												 ->at($this->_test_dir);
-		
+
+		$file = vfsStream::newFile('my_file.txt')->withContent($content)->at($this->_test_dir);
+
 		$this->assertEquals($content, read_file(vfsStream::url('my_file.txt')));
 	}
 
@@ -31,126 +30,124 @@
 	public function test_octal_permissions()
 	{
 		$content = 'Jack and Jill went up the mountain to fight a billy goat.';
-		
+
 		$file = vfsStream::newFile('my_file.txt', 0777)->withContent($content)
-												 ->lastModified(time() - 86400)
-												 ->at($this->_test_dir);
-		
-		$this->assertEquals('777', octal_permissions($file->getPermissions()));		
+											 ->lastModified(time() - 86400)
+											 ->at($this->_test_dir);
+
+		$this->assertEquals('777', octal_permissions($file->getPermissions()));
 	}
 
-	// --------------------------------------------------------------------	
-	
+	// --------------------------------------------------------------------
+
 	/**
 	 * More tests should happen here, since I'm not hitting the whole function.
 	 */
 	public function test_symbolic_permissions()
 	{
 		$content = 'Jack and Jill went up the mountain to fight a billy goat.';
-		
+
 		$file = vfsStream::newFile('my_file.txt', 0777)->withContent($content)
-												 ->lastModified(time() - 86400)
-												 ->at($this->_test_dir);
-												
-		$this->assertEquals('urwxrwxrwx', symbolic_permissions($file->getPermissions()));		
+											 ->lastModified(time() - 86400)
+											 ->at($this->_test_dir);
+
+		$this->assertEquals('urwxrwxrwx', symbolic_permissions($file->getPermissions()));
 	}
 
-	// --------------------------------------------------------------------	
-	
+	// --------------------------------------------------------------------
+
 	public function test_get_mime_by_extension()
 	{
 		$content = 'Jack and Jill went up the mountain to fight a billy goat.';
-		
+
 		$file = vfsStream::newFile('my_file.txt', 0777)->withContent($content)
-												 ->lastModified(time() - 86400)
-												 ->at($this->_test_dir);
-												
-		$this->assertEquals('text/plain', 
-						get_mime_by_extension(vfsStream::url('my_file.txt')));
-						
-		// Test a mime with an array, such as png		
+											 ->lastModified(time() - 86400)
+											 ->at($this->_test_dir);
+
+		$this->assertEquals('text/plain', get_mime_by_extension(vfsStream::url('my_file.txt')));
+
+		// Test a mime with an array, such as png
 		$file = vfsStream::newFile('foo.png')->at($this->_test_dir);
-		
-		$this->assertEquals('image/png', get_mime_by_extension(vfsStream::url('foo.png')));			
-			
+
+		$this->assertEquals('image/png', get_mime_by_extension(vfsStream::url('foo.png')));
+
 		// Test a file not in the mimes array
 		$file = vfsStream::newFile('foo.blarfengar')->at($this->_test_dir);
-		
+
 		$this->assertFalse(get_mime_by_extension(vfsStream::url('foo.blarfengar')));
 	}
 
-	// --------------------------------------------------------------------	
+	// --------------------------------------------------------------------
 
 	public function test_get_file_info()
 	{
 		// Test Bad File
 		$this->assertFalse(get_file_info('i_am_bad_boo'));
-		
+
 		// Test the rest
-		
+
 		// First pass in an array
 		$vals = array(
 			'name', 'server_path', 'size', 'date',
-			'readable', 'writable', 'executable',  'fileperms'
+			'readable', 'writable', 'executable', 'fileperms'
 		);
-		
+
 		$this->_test_get_file_info($vals);
 
 		// Test passing in vals as a string.
-		$vals = 'name, server_path, size, date, readable, writable, executable, fileperms';
-		$this->_test_get_file_info($vals);
+		$this->_test_get_file_info(implode(', ', $vals));
 	}
-	
+
 	private function _test_get_file_info($vals)
 	{
 		$content = 'Jack and Jill went up the mountain to fight a billy goat.';
 		$last_modified = time() - 86400;
-		
+
 		$file = vfsStream::newFile('my_file.txt', 0777)->withContent($content)
-												 ->lastModified($last_modified)
-												 ->at($this->_test_dir);
-		
+											 ->lastModified($last_modified)
+											 ->at($this->_test_dir);
+
 		$ret_values = array(
-			'name'			=> 'my_file.txt', 
-			'server_path'	=> 'vfs://my_file.txt', 
-			'size'			=> 57, 
-			'date'			=> $last_modified, 
+			'name'			=> 'my_file.txt',
+			'server_path'	=> 'vfs://my_file.txt',
+			'size'			=> 57,
+			'date'			=> $last_modified,
 			'readable'		=> TRUE,
-			'writable'		=> TRUE, 
-			'executable'	=> TRUE, 
+			'writable'		=> TRUE,
+			'executable'	=> TRUE,
 			'fileperms'		=> 33279
 		);
-		
+
 		$info = get_file_info(vfsStream::url('my_file.txt'), $vals);
-		
+
 		foreach ($info as $k => $v)
 		{
 			$this->assertEquals($ret_values[$k], $v);
 		}
 	}
-	
-	// --------------------------------------------------------------------	
+
+	// --------------------------------------------------------------------
 
 	// Skipping for now, as it's not implemented in vfsStreamWrapper
 	// flock(): vfsStreamWrapper::stream_lock is not implemented!
-	
+
 	// public function test_write_file()
 	// {
-	// 	if ( ! defined('FOPEN_WRITE_CREATE_DESTRUCTIVE'))
-	// 	{
-	// 		define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb');
-	// 	}
-	// 	
-	// 	$content = 'Jack and Jill went up the mountain to fight a billy goat.';
-	// 	
-	// 	$file = vfsStream::newFile('write.txt', 0777)->withContent('')
-	// 											 	 ->lastModified(time() - 86400)
-	// 											 	 ->at($this->_test_dir);
-	// 	
-	// 	$this->assertTrue(write_file(vfsStream::url('write.txt'), $content));
-	// 		
+	//	if ( ! defined('FOPEN_WRITE_CREATE_DESTRUCTIVE'))
+	//	{
+	//		define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb');
+	//	}
+	//
+	//	$content = 'Jack and Jill went up the mountain to fight a billy goat.';
+	//
+	//	$file = vfsStream::newFile('write.txt', 0777)->withContent('')
+	//								 	 ->lastModified(time() - 86400)
+	//								 	 ->at($this->_test_dir);
+	//
+	//	$this->assertTrue(write_file(vfsStream::url('write.txt'), $content));
+	//
 	// }
 
-	// --------------------------------------------------------------------		
-	
+	// --------------------------------------------------------------------
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/form_helper_test.php b/tests/codeigniter/helpers/form_helper_test.php
index 80bace9..1a30ed9 100644
--- a/tests/codeigniter/helpers/form_helper_test.php
+++ b/tests/codeigniter/helpers/form_helper_test.php
@@ -3,26 +3,26 @@
 require BASEPATH . 'core/Common.php';
 require BASEPATH . 'helpers/form_helper.php';
 
-class Form_helper_test extends CI_TestCase 
+class Form_helper_test extends CI_TestCase
 {
 	public function test_form_hidden()
-	{				
+	{
 		$expected = <<<EOH
 
 <input type="hidden" name="username" value="johndoe" />
 
 EOH;
-	
+
 		$this->assertEquals($expected, form_hidden('username', 'johndoe'));
 	}
-	
+
 	public function test_form_input()
 	{
 		$expected = <<<EOH
 <input type="text" name="username" value="johndoe" id="username" maxlength="100" size="50" style="width:50%"  />
 
 EOH;
-	
+
 		$data = array(
 			'name'        => 'username',
 			'id'          => 'username',
@@ -34,37 +34,37 @@
 
 		$this->assertEquals($expected, form_input($data));
 	}
-	
+
 	public function test_form_password()
-	{				
+	{
 		$expected = <<<EOH
 <input type="password" name="password" value=""  />
 
 EOH;
-	
+
 		$this->assertEquals($expected, form_password('password'));
 	}
-	
+
 	public function test_form_upload()
-	{				
+	{
 		$expected = <<<EOH
 <input type="file" name="attachment" value=""  />
 
 EOH;
-	
+
 		$this->assertEquals($expected, form_upload('attachment'));
 	}
-	
+
 	public function test_form_textarea()
-	{				
+	{
 		$expected = <<<EOH
 <textarea name="notes" cols="40" rows="10" >Notes</textarea>
 
 EOH;
-	
+
 		$this->assertEquals($expected, form_textarea('notes', 'Notes'));
 	}
-	
+
 	public function test_form_dropdown()
 	{
 		$expected = <<<EOH
@@ -76,16 +76,16 @@
 </select>
 
 EOH;
-		
+
 		$options = array(
-			'small'  => 'Small Shirt',
-			'med'    => 'Medium Shirt',
-			'large'   => 'Large Shirt',
-			'xlarge' => 'Extra Large Shirt',
+			'small'		=> 'Small Shirt',
+			'med'		=> 'Medium Shirt',
+			'large'		=> 'Large Shirt',
+			'xlarge'	=> 'Extra Large Shirt',
 		);
-		
+
 		$this->assertEquals($expected, form_dropdown('shirts', $options, 'large'));
-		
+
 		$expected = <<<EOH
 <select name="shirts" multiple="multiple">
 <option value="small" selected="selected">Small Shirt</option>
@@ -95,22 +95,22 @@
 </select>
 
 EOH;
-		
+
 		$shirts_on_sale = array('small', 'large');
-		
+
 		$this->assertEquals($expected, form_dropdown('shirts', $options, $shirts_on_sale));
-		
+
 		$options = array(
 			'Swedish Cars' => array(
-				'volvo'  => 'Volvo',
-				'saab'    => 'Saab'
+				'volvo'	=> 'Volvo',
+				'saab'	=> 'Saab'
 			),
 			'German Cars' => array(
-				'mercedes'  => 'Mercedes',
-				'audi'    => 'Audi'
+				'mercedes'	=> 'Mercedes',
+				'audi'		=> 'Audi'
 			)
 		);
-		
+
 		$expected = <<<EOH
 <select name="cars" multiple="multiple">
 <optgroup label="Swedish Cars">
@@ -124,13 +124,10 @@
 </select>
 
 EOH;
-		
-		$cars_on_sale = array('volvo', 'audi');
-		
-		$this->assertEquals($expected, form_dropdown('cars', $options, $cars_on_sale));
-		
+
+		$this->assertEquals($expected, form_dropdown('cars', $options, array('volvo', 'audi')));
 	}
-	
+
 	public function test_form_multiselect()
 	{
 		$expected = <<<EOH
@@ -142,17 +139,17 @@
 </select>
 
 EOH;
-		
+
 		$options = array(
-                  'small'  => 'Small Shirt',
-                  'med'    => 'Medium Shirt',
-                  'large'   => 'Large Shirt',
-                  'xlarge' => 'Extra Large Shirt',
-                );
-		
+			'small'		=> 'Small Shirt',
+			'med'		=> 'Medium Shirt',
+			'large'		=> 'Large Shirt',
+			'xlarge'	=> 'Extra Large Shirt',
+		);
+
 		$this->assertEquals($expected, form_multiselect('shirts[]', $options, array('med', 'large')));
 	}
-	
+
 	public function test_form_fieldset()
 	{
 		$expected = <<<EOH
@@ -160,7 +157,7 @@
 <legend>Address Information</legend>
 
 EOH;
-		
+
 		$this->assertEquals($expected, form_fieldset('Address Information'));
 	}
 
@@ -169,10 +166,10 @@
 		$expected = <<<EOH
 </fieldset></div></div>
 EOH;
-		
+
 		$this->assertEquals($expected, form_fieldset_close('</div></div>'));
 	}
-	
+
 	public function test_form_checkbox()
 	{
 		$expected = <<<EOH
@@ -182,7 +179,7 @@
 
 		$this->assertEquals($expected, form_checkbox('newsletter', 'accept', TRUE));
 	}
-	
+
 	public function test_form_radio()
 	{
 		$expected = <<<EOH
@@ -192,7 +189,7 @@
 
 		$this->assertEquals($expected, form_radio('newsletter', 'accept', TRUE));
 	}
-	
+
 	public function test_form_submit()
 	{
 		$expected = <<<EOH
@@ -202,7 +199,7 @@
 
 		$this->assertEquals($expected, form_submit('mysubmit', 'Submit Post!'));
 	}
-	
+
 	public function test_form_label()
 	{
 		$expected = <<<EOH
@@ -211,7 +208,7 @@
 
 		$this->assertEquals($expected, form_label('What is your Name', 'username'));
 	}
-	
+
 	public function test_form_reset()
 	{
 		$expected = <<<EOH
@@ -221,7 +218,7 @@
 
 		$this->assertEquals($expected, form_reset('myreset', 'Reset'));
 	}
-	
+
 	public function test_form_button()
 	{
 		$expected = <<<EOH
@@ -229,9 +226,9 @@
 
 EOH;
 
-		$this->assertEquals($expected, form_button('name','content'));
+		$this->assertEquals($expected, form_button('name', 'content'));
 	}
-	
+
 	public function test_form_close()
 	{
 		$expected = <<<EOH
@@ -240,13 +237,14 @@
 
 		$this->assertEquals($expected, form_close('</div></div>'));
 	}
-	
+
 	public function test_form_prep()
 	{
-		$expected = "Here is a string containing &quot;quoted&quot; text.";
-		
+		$expected = 'Here is a string containing &quot;quoted&quot; text.';
+
 		$this->assertEquals($expected, form_prep('Here is a string containing "quoted" text.'));
 	}
+
 }
 
 /* End of file form_helper_test.php */
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/html_helper_test.php b/tests/codeigniter/helpers/html_helper_test.php
index 28974b0..9a7bb48 100644
--- a/tests/codeigniter/helpers/html_helper_test.php
+++ b/tests/codeigniter/helpers/html_helper_test.php
@@ -6,16 +6,16 @@
 	{
 		$this->helper('html');
 	}
-	
+
 	// ------------------------------------------------------------------------
-	
+
 	public function test_br()
 	{
 		$this->assertEquals('<br /><br />', br(2));
 	}
-	
+
 	// ------------------------------------------------------------------------
-	
+
 	public function test_heading()
 	{
 		$this->assertEquals('<h1>foobar</h1>', heading('foobar'));
@@ -23,7 +23,7 @@
 	}
 
 	// ------------------------------------------------------------------------
-	
+
 	public function test_Ul()
 	{
 		$expect = <<<EOH
@@ -35,11 +35,9 @@
 EOH;
 
 		$expect = ltrim($expect);
-
 		$list = array('foo', 'bar');
-		
-		$this->assertEquals($expect, ul($list));
 
+		$this->assertEquals(ltrim($expect), ul($list));
 
 		$expect = <<<EOH
 <ul class="test">
@@ -51,13 +49,11 @@
 
 		$expect = ltrim($expect);
 
-		$list = array('foo', 'bar');
-
 		$this->assertEquals($expect, ul($list, 'class="test"'));
 
 		$this->assertEquals($expect, ul($list, array('class' => 'test')));
 	}
-	
+
 	// ------------------------------------------------------------------------
 
 	public function test_NBS()
@@ -66,15 +62,15 @@
 	}
 
 	// ------------------------------------------------------------------------
-	
+
 	public function test_meta()
 	{
 		$this->assertEquals("<meta name=\"test\" content=\"foo\" />\n", meta('test', 'foo'));
-		
+
 		$expect = "<meta name=\"foo\" content=\"\" />\n";
-		
+
 		$this->assertEquals($expect, meta(array('name' => 'foo')));
-		
+
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/inflector_helper_test.php b/tests/codeigniter/helpers/inflector_helper_test.php
index 9e94787..f3b0ebb 100644
--- a/tests/codeigniter/helpers/inflector_helper_test.php
+++ b/tests/codeigniter/helpers/inflector_helper_test.php
@@ -1,12 +1,12 @@
 <?php
 
 class Inflector_helper_test extends CI_TestCase {
-	
+
 	public function set_up()
 	{
 		$this->helper('inflector');
 	}
-	
+
 	public function test_singular()
 	{
 		$strs = array(
@@ -16,15 +16,15 @@
 			'smells'		=> 'smell',
 			'equipment'		=> 'equipment'
 		);
-		
+
 		foreach ($strs as $str => $expect)
 		{
 			$this->assertEquals($expect, singular($str));
 		}
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	public function test_plural()
 	{
 		$strs = array(
@@ -35,15 +35,15 @@
 			'witch'			=> 'witches',
 			'equipment'		=> 'equipment'
 		);
-		
+
 		foreach ($strs as $str => $expect)
 		{
 			$this->assertEquals($expect, plural($str));
-		}		
-	}	
+		}
+	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_camelize()
 	{
 		$strs = array(
@@ -52,15 +52,15 @@
 			'i-am-playing-a-trick'	=> 'i-am-playing-a-trick',
 			'what_do_you_think-yo?'	=> 'whatDoYouThink-yo?',
 		);
-		
+
 		foreach ($strs as $str => $expect)
 		{
 			$this->assertEquals($expect, camelize($str));
 		}
-	}	
+	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_underscore()
 	{
 		$strs = array(
@@ -69,7 +69,7 @@
 			'i-am-playing-a-trick'	=> 'i-am-playing-a-trick',
 			'what_do_you_think-yo?'	=> 'what_do_you_think-yo?',
 		);
-		
+
 		foreach ($strs as $str => $expect)
 		{
 			$this->assertEquals($expect, underscore($str));
@@ -77,7 +77,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_humanize()
 	{
 		$strs = array(
@@ -86,10 +86,11 @@
 			'i-am-playing-a-trick'	=> 'I-am-playing-a-trick',
 			'what_do_you_think-yo?'	=> 'What Do You Think-yo?',
 		);
-		
+
 		foreach ($strs as $str => $expect)
 		{
 			$this->assertEquals($expect, humanize($str));
 		}
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/number_helper_test.php b/tests/codeigniter/helpers/number_helper_test.php
index 4bb9a91..ef6aae1 100644
--- a/tests/codeigniter/helpers/number_helper_test.php
+++ b/tests/codeigniter/helpers/number_helper_test.php
@@ -1,35 +1,35 @@
 <?php
 
 class Number_helper_test extends CI_TestCase {
-	
+
 	public function set_up()
 	{
 		$this->helper('number');
-		
+
 		// Grab the core lang class
 		$lang_cls = $this->ci_core_class('lang');
-		
+
 		// Mock away load, too much going on in there,
 		// we'll just check for the expected parameter
-		
+
 		$lang = $this->getMock($lang_cls, array('load'));
 		$lang->expects($this->once())
 			 ->method('load')
 			 ->with($this->equalTo('number'));
-		
+
 		// Assign the proper language array
-		
+
 		$lang->language = $this->_get_lang('number');
-		
+
 		// We don't have a controller, so just create
 		// a cheap class to act as our super object.
 		// Make sure it has a lang attribute.
-		
-		$obj = new StdClass;
+
+		$obj = new stdClass;
 		$obj->lang = $lang;
 		$this->ci_instance($obj);
 	}
-	
+
 	// Quick helper to actually grab the language
 	// file. Consider moving this to ci_testcase?
 	public function _get_lang($name)
@@ -37,41 +37,40 @@
 		require BASEPATH.'language/english/'.$name.'_lang.php';
 		return $lang;
 	}
-	
+
 	public function test_byte_format()
 	{
 		$this->assertEquals('456 Bytes', byte_format(456));
 	}
-	
+
 	public function test_kb_format()
 	{
 		$this->assertEquals('4.5 KB', byte_format(4567));
 	}
-	
+
 	public function test_kb_format_medium()
 	{
 		$this->assertEquals('44.6 KB', byte_format(45678));
 	}
-	
+
 	public function test_kb_format_large()
 	{
 		$this->assertEquals('446.1 KB', byte_format(456789));
 	}
-	
+
 	public function test_mb_format()
 	{
 		$this->assertEquals('3.3 MB', byte_format(3456789));
 	}
-	
+
 	public function test_gb_format()
 	{
 		$this->assertEquals('1.8 GB', byte_format(1932735283.2));
 	}
-	
+
 	public function test_tb_format()
 	{
 		$this->assertEquals('112,283.3 TB', byte_format(123456789123456789));
 	}
-}
 
-// EOF
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/path_helper_test.php b/tests/codeigniter/helpers/path_helper_test.php
index 632f575..0faf6f3 100644
--- a/tests/codeigniter/helpers/path_helper_test.php
+++ b/tests/codeigniter/helpers/path_helper_test.php
@@ -8,9 +8,8 @@
 	}
 
 	public function test_set_realpath()
-	{				
-		$expected = getcwd() . DIRECTORY_SEPARATOR;
-		$this->assertEquals($expected, set_realpath(getcwd()));		
+	{
+		$this->assertEquals(getcwd().DIRECTORY_SEPARATOR, set_realpath(getcwd()));
 	}
 
 	public function test_set_realpath_nonexistent_directory()
diff --git a/tests/codeigniter/helpers/string_helper_test.php b/tests/codeigniter/helpers/string_helper_test.php
index 29c3d65..75701ec 100644
--- a/tests/codeigniter/helpers/string_helper_test.php
+++ b/tests/codeigniter/helpers/string_helper_test.php
@@ -10,18 +10,18 @@
 	public function test_strip_slashes()
 	{
 		$expected = array(
-			"Is your name O'reilly?", 
+			"Is your name O'reilly?",
 			"No, my name is O'connor."
 		);
-		
+
 		$str = array(
 			"Is your name O\'reilly?",
 			"No, my name is O\'connor."
 		);
-		
+
 		$this->assertEquals($expected, strip_slashes($str));
 	}
-	
+
 	public function test_trim_slashes()
 	{
 		$strs = array(
@@ -144,4 +144,5 @@
 		$this->assertEquals('file-1', increment_string('file', '-', '1'));
 		$this->assertEquals(124, increment_string('123', ''));
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/text_helper_test.php b/tests/codeigniter/helpers/text_helper_test.php
index 584066b..f131469 100644
--- a/tests/codeigniter/helpers/text_helper_test.php
+++ b/tests/codeigniter/helpers/text_helper_test.php
@@ -3,16 +3,16 @@
 class Text_helper_test extends CI_TestCase {
 
 	private $_long_string;
-	
+
 	public function set_up()
 	{
 		$this->helper('text');
-		
+
 		$this->_long_string = 'Once upon a time, a framework had no tests.  It sad.  So some nice people began to write tests.  The more time that went on, the happier it became.  Everyone was happy.';
 	}
-	
+
 	// ------------------------------------------------------------------------
-	
+
 	public function test_word_limiter()
 	{
 		$this->assertEquals('Once upon a time,&#8230;', word_limiter($this->_long_string, 4));
@@ -20,8 +20,8 @@
 		$this->assertEquals('', word_limiter('', 4));
 	}
 
-	// ------------------------------------------------------------------------	
-	
+	// ------------------------------------------------------------------------
+
 	public function test_character_limiter()
 	{
 		$this->assertEquals('Once upon a time, a&#8230;', character_limiter($this->_long_string, 20));
@@ -30,22 +30,22 @@
 		$this->assertEquals('Short', character_limiter('Short', 5));
 	}
 
-	// ------------------------------------------------------------------------	
-	
+	// ------------------------------------------------------------------------
+
 	public function test_ascii_to_entities()
 	{
 		$strs = array(
 			'“‘ “test”'			=> '&#8220;&#8216; &#8220;test&#8221;',
 			'†¥¨ˆøåß∂ƒ©Ë™âˆ†Ëš¬'	=> '&#8224;&#165;&#168;&#710;&#248;&#229;&#223;&#8706;&#402;&#169;&#729;&#8710;&#730;&#172;'
 		);
-		
+
 		foreach ($strs as $str => $expect)
 		{
 			$this->assertEquals($expect, ascii_to_entities($str));
 		}
 	}
 
-	// ------------------------------------------------------------------------	
+	// ------------------------------------------------------------------------
 
 	public function test_entities_to_ascii()
 	{
@@ -53,27 +53,27 @@
 			'&#8220;&#8216; &#8220;test&#8221;' => '“‘ “test”',
 			'&#8224;&#165;&#168;&#710;&#248;&#229;&#223;&#8706;&#402;&#169;&#729;&#8710;&#730;&#172;' => '†¥¨ˆøåß∂ƒ©Ë™âˆ†Ëš¬'
 		);
-		
+
 		foreach ($strs as $str => $expect)
 		{
 			$this->assertEquals($expect, entities_to_ascii($str));
-		}		
+		}
 	}
-	
-	// ------------------------------------------------------------------------	
-	
-	function test_convert_accented_characters() 
+
+	// ------------------------------------------------------------------------
+
+	public function test_convert_accented_characters()
 	{
 		$this->assertEquals('AAAeEEEIIOOEUUUeY', convert_accented_characters('ÀÂÄÈÊËÎÏÔŒÙÛÜŸ'));
 		$this->assertEquals('a e i o u n ue', convert_accented_characters('á é í ó ú ñ ü'));
 	}
 
-	// ------------------------------------------------------------------------	
-	
+	// ------------------------------------------------------------------------
+
 	public function test_censored_words()
 	{
 		$censored = array('boob', 'nerd', 'ass', 'fart');
-		
+
 		$strs = array(
 			'Ted bobbled the ball' 			=> 'Ted bobbled the ball',
 			'Jake is a nerdo'				=> 'Jake is a nerdo',
@@ -81,28 +81,26 @@
 			'Did Mary Fart?'				=> 'Did Mary $*#?',
 			'Jake is really a boob'			=> 'Jake is really a $*#'
 		);
-		
-		
+
 		foreach ($strs as $str => $expect)
 		{
 			$this->assertEquals($expect, word_censor($str, $censored, '$*#'));
 		}
-		
+
 		// test censored words being sent as a string
 		$this->assertEquals('test', word_censor('test', 'test'));
 	}
 
-	// ------------------------------------------------------------------------	
+	// ------------------------------------------------------------------------
 
 	public function test_highlight_code()
 	{
-		$code = '<?php var_dump($this); ?>';
 		$expect = "<code><span style=\"color: #000000\">\n<span style=\"color: #0000BB\">&lt;?php&nbsp;var_dump</span><span style=\"color: #007700\">(</span><span style=\"color: #0000BB\">\$this</span><span style=\"color: #007700\">);&nbsp;</span><span style=\"color: #0000BB\">?&gt;&nbsp;</span>\n</span>\n</code>";
 
-		$this->assertEquals($expect, highlight_code($code));
+		$this->assertEquals($expect, highlight_code('<?php var_dump($this); ?>'));
 	}
 
-	// ------------------------------------------------------------------------	
+	// ------------------------------------------------------------------------
 
 	public function test_highlight_phrase()
 	{
@@ -113,16 +111,16 @@
 			'Or tell me what this is'	=> 'Or tell me what <strong>this is</strong>',
 			''							=> ''
 		);
-		
+
 		foreach ($strs as $str => $expect)
 		{
 			$this->assertEquals($expect, highlight_phrase($str, 'this is'));
 		}
 	}
 
-	// ------------------------------------------------------------------------	
+	// ------------------------------------------------------------------------
 
-	public function test_ellipsizing()
+	public function test_ellipsize()
 	{
 		$strs = array(
 			'0'		=> array(
@@ -144,16 +142,30 @@
 				'short'							=> 'short'
 			),
 		);
-		
+
 		foreach ($strs as $pos => $s)
 		{
 			foreach ($s as $str => $expect)
 			{
-				$this->assertEquals($expect, ellipsize($str, 10, $pos));				
+				$this->assertEquals($expect, ellipsize($str, 10, $pos));
 			}
 		}
 	}
 
-	// ------------------------------------------------------------------------	
+	// ------------------------------------------------------------------------
+
+	public function test_word_wrap()
+	{
+		$string = 'Here is a simple string of text that will help us demonstrate this function.';
+		$this->assertEquals(substr_count(word_wrap($string, 25), "\n"), 4);
+	}
+
+	// ------------------------------------------------------------------------
+
+	public function test_default_word_wrap_charlim()
+	{
+		$string = "Here is a longer string of text that will help us demonstrate the default charlim of this function.";
+		$this->assertEquals(strpos(word_wrap($string), "\n"), 73);
+	}
 
 }
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/url_helper_test.php b/tests/codeigniter/helpers/url_helper_test.php
index c561809..c81c5f1 100644
--- a/tests/codeigniter/helpers/url_helper_test.php
+++ b/tests/codeigniter/helpers/url_helper_test.php
@@ -72,4 +72,5 @@
 			$this->assertEquals($out, auto_link($in, 'url'));
 		}
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/helpers/xml_helper_test.php b/tests/codeigniter/helpers/xml_helper_test.php
index a83fef9..e8cf411 100644
--- a/tests/codeigniter/helpers/xml_helper_test.php
+++ b/tests/codeigniter/helpers/xml_helper_test.php
@@ -6,10 +6,10 @@
 	{
 		$this->helper('xml');
 	}
-	
+
 	public function test_xml_convert()
 	{
 		$this->assertEquals('&lt;tag&gt;my &amp; test &#45; &lt;/tag&gt;', xml_convert('<tag>my & test - </tag>'));
 	}
-	
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/libraries/Encrypt_test.php b/tests/codeigniter/libraries/Encrypt_test.php
new file mode 100644
index 0000000..153a25e
--- /dev/null
+++ b/tests/codeigniter/libraries/Encrypt_test.php
@@ -0,0 +1,72 @@
+<?php
+
+class Encrypt_test extends CI_TestCase {
+
+	public function set_up()
+	{
+		$obj = new stdClass;
+		$obj->encrypt = new Mock_Libraries_Encrypt();
+
+		$this->ci_instance($obj);
+		$this->encrypt = $obj->encrypt;
+
+		$this->ci_set_config('encryption_key', "Encryptin'glike@boss!");
+		$this->msg = 'My secret message';
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_encode()
+	{
+		$this->assertNotEquals($this->msg, $this->encrypt->encode($this->msg));
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_decode()
+	{
+		$encoded_msg = $this->encrypt->encode($this->msg);
+		$this->assertEquals($this->msg, $this->encrypt->decode($encoded_msg));
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_optional_key()
+	{
+		$key = 'Ohai!ù0129°03182%HD1892P0';
+		$encoded_msg = $this->encrypt->encode($this->msg, $key);
+		$this->assertEquals($this->msg, $this->encrypt->decode($encoded_msg, $key));
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_default_cipher()
+	{
+		$this->assertEquals('rijndael-256', $this->encrypt->get_cipher());
+	}
+
+	// --------------------------------------------------------------------
+
+
+	public function test_set_cipher()
+	{
+		$this->encrypt->set_cipher(MCRYPT_BLOWFISH);
+		$this->assertEquals('blowfish', $this->encrypt->get_cipher());
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_default_mode()
+	{
+		$this->assertEquals('cbc', $this->encrypt->get_mode());
+	}
+
+	// --------------------------------------------------------------------
+
+	public function test_set_mode()
+	{
+		$this->encrypt->set_mode(MCRYPT_MODE_CFB);
+		$this->assertEquals('cfb', $this->encrypt->get_mode());
+	}
+
+}
\ No newline at end of file
diff --git a/tests/codeigniter/libraries/Parser_test.php b/tests/codeigniter/libraries/Parser_test.php
index c3d88fa..b68f44a 100644
--- a/tests/codeigniter/libraries/Parser_test.php
+++ b/tests/codeigniter/libraries/Parser_test.php
@@ -1,73 +1,74 @@
 <?php
 
 class Parser_test extends CI_TestCase {
-	
+
 	public function set_up()
 	{
-		$obj = new StdClass;
+		$obj = new stdClass;
 		$obj->parser = new Mock_Libraries_Parser();
-		
+
 		$this->ci_instance($obj);
-		
+
 		$this->parser = $obj->parser;
 	}
+
 	// --------------------------------------------------------------------
-	
+
 	public function test_set_delimiters()
 	{
 		// Make sure default delimiters are there
 		$this->assertEquals('{', $this->parser->l_delim);
 		$this->assertEquals('}', $this->parser->r_delim);
-		
+
 		// Change them to square brackets
 		$this->parser->set_delimiters('[', ']');
-		
+
 		// Make sure they changed
 		$this->assertEquals('[', $this->parser->l_delim);
 		$this->assertEquals(']', $this->parser->r_delim);
-		
+
 		// Reset them
 		$this->parser->set_delimiters();
-		
+
 		// Make sure default delimiters are there
 		$this->assertEquals('{', $this->parser->l_delim);
 		$this->assertEquals('}', $this->parser->r_delim);
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	public function test_parse_simple_string()
 	{
 		$data = array(
 			'title' => 'Page Title',
 			'body' => 'Lorem ipsum dolor sit amet.'
 		);
-		
+
 		$template = "{title}\n{body}";
-		
+
 		$result = implode("\n", $data);
-		
+
 		$this->assertEquals($result, $this->parser->parse_string($template, $data, TRUE));
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	public function test_parse()
 	{
 		$this->_parse_no_template();
 		$this->_parse_var_pair();
 		$this->_mismatched_var_pair();
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	private function _parse_no_template()
 	{
 		$this->assertFalse($this->parser->parse_string('', '', TRUE));
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	private function _parse_var_pair()
 	{
 		$data = array(
@@ -78,16 +79,14 @@
 						'flying'		=> 'no'),
 			)
 		);
-		
+
 		$template = "{title}\n{powers}{invisibility}\n{flying}{/powers}";
-		
-		$result = "Super Heroes\nyes\nno";
-		
-		$this->assertEquals($result, $this->parser->parse_string($template, $data, TRUE));	
+
+		$this->assertEquals("Super Heroes\nyes\nno", $this->parser->parse_string($template, $data, TRUE));
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	private function _mismatched_var_pair()
 	{
 		$data = array(
@@ -98,13 +97,11 @@
 						'flying'		=> 'no'),
 			)
 		);
-		
+
 		$template = "{title}\n{powers}{invisibility}\n{flying}";
-		
 		$result = "Super Heroes\n{powers}{invisibility}\n{flying}";
-		
-		$this->assertEquals($result, $this->parser->parse_string($template, $data, TRUE));			
+
+		$this->assertEquals($result, $this->parser->parse_string($template, $data, TRUE));
 	}
 
-	// --------------------------------------------------------------------
 }
\ No newline at end of file
diff --git a/tests/codeigniter/libraries/Table_test.php b/tests/codeigniter/libraries/Table_test.php
index f5133de..edfc83d 100644
--- a/tests/codeigniter/libraries/Table_test.php
+++ b/tests/codeigniter/libraries/Table_test.php
@@ -4,43 +4,39 @@
 
 	public function set_up()
 	{
-		$obj = new StdClass;
+		$obj = new stdClass;
 		$obj->table = new Mock_Libraries_Table();
-		
+
 		$this->ci_instance($obj);
-		
+
 		$this->table = $obj->table;
 	}
 
-	
 	// Setter Methods
 	// --------------------------------------------------------------------
-	
+
 	public function test_set_template()
 	{
 		$this->assertFalse($this->table->set_template('not an array'));
-		
-		$template = array(
-			'a' => 'b'
-		);
-		
+
+		$template = array('a' => 'b');
+
 		$this->table->set_template($template);
 		$this->assertEquals($template, $this->table->template);
 	}
-	
+
 	public function test_set_empty()
 	{
 		$this->table->set_empty('nada');
 		$this->assertEquals('nada', $this->table->empty_cells);
 	}
-	
+
 	public function test_set_caption()
 	{
 		$this->table->set_caption('awesome cap');
 		$this->assertEquals('awesome cap', $this->table->caption);
 	}
-	
-	
+
 	/*
 	 * @depends testPrepArgs
 	 */
@@ -49,9 +45,9 @@
 		// uses _prep_args internally, so we'll just do a quick
 		// check to verify that func_get_args and prep_args are
 		// being called.
-		
+
 		$this->table->set_heading('name', 'color', 'size');
-		
+
 		$this->assertEquals(
 			array(
 				array('data' => 'name'),
@@ -61,8 +57,7 @@
 			$this->table->heading
 		);
 	}
-	
-	
+
 	/*
 	 * @depends testPrepArgs
 	 */
@@ -71,13 +66,13 @@
 		// uses _prep_args internally, so we'll just do a quick
 		// check to verify that func_get_args and prep_args are
 		// being called.
-		
+
 		$this->table->add_row('my', 'pony', 'sings');
 		$this->table->add_row('your', 'pony', 'stinks');
 		$this->table->add_row('my pony', '>', 'your pony');
-		
+
 		$this->assertEquals(count($this->table->rows), 3);
-		
+
 		$this->assertEquals(
 			array(
 				array('data' => 'your'),
@@ -87,11 +82,10 @@
 			$this->table->rows[1]
 		);
 	}
-	
-	
+
 	// Uility Methods
 	// --------------------------------------------------------------------
-	
+
 	public function test_prep_args()
 	{
 		$expected = array(
@@ -99,7 +93,7 @@
 			array('data' => 'color'),
 			array('data' => 'size')
 		);
-		
+
 		$this->assertEquals(
 			$expected,
 			$this->table->prep_args(array('name', 'color', 'size'))
@@ -114,7 +108,7 @@
 			$this->table->prep_args(array('name', 'color', 'size', array('data' => 'weight', 'class' => 'awesome')))
 		);
 	}
-	
+
 	public function test_default_template_keys()
 	{
 		$keys = array(
@@ -126,132 +120,124 @@
 			'row_alt_start', 'row_alt_end', 'cell_alt_start', 'cell_alt_end',
 			'table_close'
 		);
-		
+
 		foreach ($keys as $key)
 		{
 			$this->assertArrayHasKey($key, $this->table->default_template());
 		}
 	}
-	
+
 	public function test_compile_template()
 	{
 		$this->assertFalse($this->table->set_template('invalid_junk'));
-		
+
 		// non default key
 		$this->table->set_template(array('nonsense' => 'foo'));
 		$this->table->compile_template();
-		
+
 		$this->assertArrayHasKey('nonsense', $this->table->template);
 		$this->assertEquals('foo', $this->table->template['nonsense']);
-		
+
 		// override default
 		$this->table->set_template(array('table_close' => '</table junk>'));
 		$this->table->compile_template();
-		
+
 		$this->assertArrayHasKey('table_close', $this->table->template);
 		$this->assertEquals('</table junk>', $this->table->template['table_close']);
 	}
-	
+
 	public function test_make_columns()
 	{
 		// Test bogus parameters
 		$this->assertFalse($this->table->make_columns('invalid_junk'));
 		$this->assertFalse($this->table->make_columns(array()));
 		$this->assertFalse($this->table->make_columns(array('one', 'two'), '2.5'));
-		
-		
+
 		// Now on to the actual column creation
-		
+
 		$five_values = array(
 			'Laura', 'Red', '15',
 			'Katie', 'Blue'
 		);
-		
+
 		// No column count - no changes to the array
 		$this->assertEquals(
 			$five_values,
 			$this->table->make_columns($five_values)
 		);
-		
+
 		// Column count of 3 leaves us with one &nbsp;
 		$this->assertEquals(
 			array(
 				array('Laura', 'Red', '15'),
-				array('Katie', 'Blue', '&nbsp;')				
+				array('Katie', 'Blue', '&nbsp;')
 			),
 			$this->table->make_columns($five_values, 3)
 		);
 	}
-	
+
 	public function test_clear()
 	{
 		$this->table->set_heading('Name', 'Color', 'Size');
-		
+
 		// Make columns changes auto_heading
 		$rows = $this->table->make_columns(array(
 			'Laura', 'Red', '15',
 			'Katie', 'Blue'
 		), 3);
-		
+
 		foreach ($rows as $row)
 		{
 			$this->table->add_row($row);
 		}
-		
+
 		$this->assertFalse($this->table->auto_heading);
 		$this->assertEquals(count($this->table->heading), 3);
 		$this->assertEquals(count($this->table->rows), 2);
-		
+
 		$this->table->clear();
-		
+
 		$this->assertTrue($this->table->auto_heading);
 		$this->assertEmpty($this->table->heading);
 		$this->assertEmpty($this->table->rows);
 	}
-	
-	
+
 	public function test_set_from_array()
 	{
 		$this->assertFalse($this->table->set_from_array('bogus'));
 		$this->assertFalse($this->table->set_from_array(NULL));
-		
+
 		$data = array(
 			array('name', 'color', 'number'),
 			array('Laura', 'Red', '22'),
-			array('Katie', 'Blue')				
+			array('Katie', 'Blue')
 		);
-		
+
 		$this->table->set_from_array($data, FALSE);
 		$this->assertEmpty($this->table->heading);
-		
+
 		$this->table->clear();
-		
-		$expected_heading = array(
+
+		$this->table->set_from_array($data);
+		$this->assertEquals(count($this->table->rows), 2);
+
+		$expected = array(
 			array('data' => 'name'),
 			array('data' => 'color'),
 			array('data' => 'number')
 		);
-		
-		$expected_second = array(
+
+		$this->assertEquals($expected, $this->table->heading);
+
+		$expected = array(
 			array('data' => 'Katie'),
 			array('data' => 'Blue'),
 		);
-		
-		$this->table->set_from_array($data);
-		$this->assertEquals(count($this->table->rows), 2);
-		
-		$this->assertEquals(
-			$expected_heading,
-			$this->table->heading
-		);
-		
-		$this->assertEquals(
-			$expected_second,
-			$this->table->rows[1]
-		);
+
+		$this->assertEquals($expected, $this->table->rows[1]);
 	}
-	
-	function test_set_from_object()
+
+	public function test_set_from_object()
 	{
 		// Make a stub of query instance
 		$query = new CI_TestCase();
@@ -268,37 +254,31 @@
 			return 2;
 		};
 
-		$expected_heading = array(
+		$this->table->set_from_object($query);
+
+		$expected = array(
 			array('data' => 'name'),
 			array('data' => 'email')
 		);
 
-		$expected_second = array(
+		$this->assertEquals($expected, $this->table->heading);
+
+		$expected = array(
 			'name' => array('data' => 'Foo Bar'),
 			'email' => array('data' => 'foo@bar.com'),
 		);
 
-		$this->table->set_from_object($query);
-
-		$this->assertEquals(
-			$expected_heading,
-			$this->table->heading
-		);
-		
-		$this->assertEquals(
-			$expected_second,
-			$this->table->rows[1]
-		);
+		$this->assertEquals($expected, $this->table->rows[1]);
 	}
-	
-	function test_generate()
+
+	public function test_generate()
 	{
 		// Prepare the data
 		$data = array(
 			array('Name', 'Color', 'Size'),
 			array('Fred', 'Blue', 'Small'),
 			array('Mary', 'Red', 'Large'),
-			array('John', 'Green', 'Medium')	
+			array('John', 'Green', 'Medium')
 		);
 
 		$table = $this->table->generate($data);
@@ -313,4 +293,5 @@
 		$this->assertTrue(strpos($table, '<td>Blue</td>') !== FALSE);
 		$this->assertTrue(strpos($table, '<td>Small</td>') !== FALSE);
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/libraries/Typography_test.php b/tests/codeigniter/libraries/Typography_test.php
index 250aefb..eb6dacb 100644
--- a/tests/codeigniter/libraries/Typography_test.php
+++ b/tests/codeigniter/libraries/Typography_test.php
@@ -4,11 +4,11 @@
 
 	public function set_up()
 	{
-		$obj = new StdClass;
+		$obj = new stdClass;
 		$obj->type = new Mock_Libraries_Typography();
-		
+
 		$this->ci_instance($obj);
-		
+
 		$this->type = $obj->type;
 	}
 
@@ -33,18 +33,18 @@
 			'foo..'							=> 'foo..',
 			'foo...bar.'					=> 'foo&#8230;bar.',
 			'test.  new'					=> 'test.&nbsp; new',
-		);	
-		
+		);
+
 		foreach ($strs as $str => $expected)
 		{
-			$this->assertEquals($expected, $this->type->format_characters($str));		
+			$this->assertEquals($expected, $this->type->format_characters($str));
 		}
 	}
 
 	// --------------------------------------------------------------------
 
 	public function test_nl2br_except_pre()
-	{	
+	{
 		$str = <<<EOH
 Hello, I'm a happy string with some new lines.  
 
@@ -85,12 +85,11 @@
 The End.
 EOH;
 
-		$this->assertEquals($expected, 
-							$this->type->nl2br_except_pre($str));
+		$this->assertEquals($expected, $this->type->nl2br_except_pre($str));
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public function test_auto_typography()
 	{
 		$this->_blank_string();
@@ -103,7 +102,7 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	private function _blank_string()
 	{
 		// Test blank string
@@ -131,7 +130,7 @@
 	{
 		$str = "This has way too many linebreaks.\n\n\n\nSee?";
 		$expect = "<p>This has way too many linebreaks.</p>\n\n<p>See?</p>";
-		
+
 		$this->assertEquals($expect, $this->type->auto_typography($str, TRUE));
 	}
 
@@ -141,7 +140,7 @@
 	{
 		$str = '<!-- I can haz comments? -->  But no!';
 		$expect = '<p><!-- I can haz comments? -->&nbsp; But no!</p>';
-		
+
 		$this->assertEquals($expect, $this->type->auto_typography($str));
 	}
 
@@ -151,7 +150,7 @@
 	{
 		$str = '<p>My Sentence</p><pre>var_dump($this);</pre>';
 		$expect = '<p>My Sentence</p><pre>var_dump($this);</pre>';
-		
+
 		$this->assertEquals($expect, $this->type->auto_typography($str));
 	}
 
@@ -161,7 +160,7 @@
 	{
 		$str = 'My Sentence<pre>var_dump($this);</pre>';
 		$expect = '<p>My Sentence</p><pre>var_dump($this);</pre>';
-		
+
 		$this->assertEquals($expect, $this->type->auto_typography($str));
 	}
 
@@ -170,19 +169,18 @@
 	public function _protect_braced_quotes()
 	{
 		$this->type->protect_braced_quotes = TRUE;
-		
+
 		$str = 'Test {parse="foobar"}';
 		$expect = '<p>Test {parse="foobar"}</p>';
-		
+
 		$this->assertEquals($expect, $this->type->auto_typography($str));
 
 		$this->type->protect_braced_quotes = FALSE;
-		
+
 		$str = 'Test {parse="foobar"}';
 		$expect = '<p>Test {parse=&#8220;foobar&#8221;}</p>';
-		
+
 		$this->assertEquals($expect, $this->type->auto_typography($str));
-
-
 	}
+
 }
\ No newline at end of file
diff --git a/tests/codeigniter/libraries/Useragent_test.php b/tests/codeigniter/libraries/Useragent_test.php
index 7dad7ac..89383f8 100644
--- a/tests/codeigniter/libraries/Useragent_test.php
+++ b/tests/codeigniter/libraries/Useragent_test.php
@@ -1,7 +1,7 @@
 <?php
 
 class UserAgent_test extends CI_TestCase {
-	
+
 	protected $_user_agent = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27';
 	protected $_mobile_ua = 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_1 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8B117 Safari/6531.22.7';
 
@@ -10,7 +10,7 @@
 		// set a baseline user agent
 		$_SERVER['HTTP_USER_AGENT'] = $this->_user_agent;
 
-		$obj = new StdClass;
+		$obj = new stdClass;
 		$obj->agent = new Mock_Libraries_UserAgent();
 
 		$this->ci_instance($obj);
@@ -82,6 +82,4 @@
 		$this->assertFalse($this->agent->accept_charset());
 	}
 
-	// --------------------------------------------------------------------
-
 }
\ No newline at end of file
diff --git a/tests/mocks/autoloader.php b/tests/mocks/autoloader.php
index 441c889..be1c222 100644
--- a/tests/mocks/autoloader.php
+++ b/tests/mocks/autoloader.php
@@ -7,9 +7,9 @@
 // Prototype :
 //
 // $mock_table = new Mock_Libraries_Table(); 			// Will load ./mocks/libraries/table.php
-// $mock_database_driver = new Mock_Database_Driver();	// Will load ./mocks/database/driver.php 
+// $mock_database_driver = new Mock_Database_Driver();	// Will load ./mocks/database/driver.php
 // and so on...
-function autoload($class) 
+function autoload($class)
 {
 	$dir = realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR;
 
@@ -50,9 +50,9 @@
 		elseif (in_array($subclass, $ci_libraries))
 		{
 			$dir = BASEPATH.'libraries'.DIRECTORY_SEPARATOR;
-			$class = ($subclass == 'Driver_Library') ? 'Driver' : $subclass;
+			$class = ($subclass === 'Driver_Library') ? 'Driver' : $subclass;
 		}
-		elseif (preg_match('/^CI_DB_(.+)_(driver|forge|result|utility)$/', $class, $m) && count($m) == 3)
+		elseif (preg_match('/^CI_DB_(.+)_(driver|forge|result|utility)$/', $class, $m) && count($m) === 3)
 		{
 			$driver_path = BASEPATH.'database'.DIRECTORY_SEPARATOR.'drivers'.DIRECTORY_SEPARATOR;
 			$dir = $driver_path.$m[1].DIRECTORY_SEPARATOR;
@@ -75,14 +75,19 @@
 	{
 		$trace = debug_backtrace();
 
-		// If the autoload call came from `class_exists` or `file_exists`, 
-		// we skipped and return FALSE
-		if ($trace[2]['function'] == 'class_exists' OR $trace[2]['function'] == 'file_exists')
+		if ($trace[2]['function'] === 'class_exists' OR $trace[2]['function'] === 'file_exists')
 		{
+			// If the autoload call came from `class_exists` or `file_exists`,
+			// we skipped and return FALSE
 			return FALSE;
 		}
-		
-	    throw new InvalidArgumentException("Unable to load $class.");
+		elseif (($autoloader = spl_autoload_functions()) && end($autoloader) !== __FUNCTION__)
+		{
+			// If there was other custom autoloader, passed away
+			return FALSE;
+		}
+
+		throw new InvalidArgumentException("Unable to load {$class}.");
 	}
 
 	include_once($file);
diff --git a/tests/mocks/ci_testcase.php b/tests/mocks/ci_testcase.php
index f327e6b..eda9e1b 100644
--- a/tests/mocks/ci_testcase.php
+++ b/tests/mocks/ci_testcase.php
@@ -1,11 +1,11 @@
 <?php
 
 class CI_TestCase extends PHPUnit_Framework_TestCase {
-	
+
 	protected $ci_config;
 	protected $ci_instance;
 	protected static $ci_test_instance;
-		
+
 	private $global_map = array(
 		'benchmark'	=> 'bm',
 		'config'	=> 'cfg',
@@ -19,18 +19,17 @@
 		'loader'	=> 'load',
 		'model'		=> 'model'
 	);
-	
+
 	// --------------------------------------------------------------------
-	
+
 	public function __construct()
 	{
 		parent::__construct();
-		
 		$this->ci_config = array();
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	public function setUp()
 	{
 		if (method_exists($this, 'set_up'))
@@ -38,10 +37,10 @@
 			$this->set_up();
 		}
 	}
-	
+
 	// --------------------------------------------------------------------
-	
-	public function tearDown() 
+
+	public function tearDown()
 	{
 		if (method_exists($this, 'tear_down'))
 		{
@@ -50,15 +49,15 @@
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	public static function instance()
 	{
 		return self::$ci_test_instance;
 	}
-	
+
 	// --------------------------------------------------------------------
-	
-	function ci_set_config($key, $val = '')
+
+	public function ci_set_config($key, $val = '')
 	{
 		if (is_array($key))
 		{
@@ -71,36 +70,36 @@
 	}
 
 	// --------------------------------------------------------------------
-	
-	function ci_get_config()
+
+	public function ci_get_config()
 	{
 		return $this->ci_config;
 	}
-	
+
 	// --------------------------------------------------------------------
-	
-	function ci_instance($obj = FALSE)
+
+	public function ci_instance($obj = FALSE)
 	{
 		if ( ! is_object($obj))
 		{
 			return $this->ci_instance;
 		}
-		
+
 		$this->ci_instance = $obj;
 	}
-	
+
 	// --------------------------------------------------------------------
-	
-	function ci_instance_var($name, $obj = FALSE)
+
+	public function ci_instance_var($name, $obj = FALSE)
 	{
 		if ( ! is_object($obj))
 		{
 			return $this->ci_instance->$name;
 		}
-		
+
 		$this->ci_instance->$name =& $obj;
 	}
-	
+
 	// --------------------------------------------------------------------
 
 	/**
@@ -112,10 +111,10 @@
 	 * test can modify the variable it assigns to and
 	 * still maintain the global.
 	 */
-	function &ci_core_class($name)
+	public function &ci_core_class($name)
 	{
 		$name = strtolower($name);
-		
+
 		if (isset($this->global_map[$name]))
 		{
 			$class_name = ucfirst($name);
@@ -130,29 +129,29 @@
 		{
 			throw new Exception('Not a valid core class.');
 		}
-		
+
 		if ( ! class_exists('CI_'.$class_name))
 		{
 			require_once BASEPATH.'core/'.$class_name.'.php';
 		}
-		
+
 		$GLOBALS[strtoupper($global_name)] = 'CI_'.$class_name;
 		return $GLOBALS[strtoupper($global_name)];
 	}
-	
+
 	// --------------------------------------------------------------------
-	
+
 	// convenience function for global mocks
-	function ci_set_core_class($name, $obj)
+	public function ci_set_core_class($name, $obj)
 	{
 		$orig =& $this->ci_core_class($name);
 		$orig = $obj;
 	}
-	
+
 	// --------------------------------------------------------------------
 	// Internals
 	// --------------------------------------------------------------------
-	
+
 	/**
 	 * Overwrite runBare
 	 *
@@ -169,28 +168,27 @@
 	}
 
 	// --------------------------------------------------------------------
-	
-	function helper($name)
+
+	public function helper($name)
 	{
 		require_once(BASEPATH.'helpers/'.$name.'_helper.php');
 	}
 
 	// --------------------------------------------------------------------
-	
+
 	/**
 	 * This overload is useful to create a stub, that need to have a specific method.
 	 */
-	function __call($method, $args)
+	public function __call($method, $args)
 	{
-		if ($this->{$method} instanceof Closure) 
+		if ($this->{$method} instanceof Closure)
 		{
 			return call_user_func_array($this->{$method},$args);
-		} 
-		else 
+		}
+		else
 		{
 			return parent::__call($method, $args);
 		}
 	}
-}
 
-// EOF
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/tests/mocks/core/common.php b/tests/mocks/core/common.php
index e745766..a655ee1 100644
--- a/tests/mocks/core/common.php
+++ b/tests/mocks/core/common.php
@@ -4,11 +4,11 @@
 
 if ( ! function_exists('get_instance'))
 {
-	function &get_instance() 
+	function &get_instance()
 	{
 		$test = CI_TestCase::instance();
-		$instance = $test->ci_instance();
-		return $instance;
+		$test = $test->ci_instance();
+		return $test;
 	}
 }
 
@@ -16,10 +16,10 @@
 
 if ( ! function_exists('get_config'))
 {
-	function &get_config() {
+	function &get_config()
+	{
 		$test = CI_TestCase::instance();
 		$config = $test->ci_get_config();
-			
 		return $config;
 	}
 }
@@ -29,12 +29,12 @@
 	function config_item($item)
 	{
 		$config =& get_config();
-		
+
 		if ( ! isset($config[$item]))
 		{
 			return FALSE;
 		}
-		
+
 		return $config[$item];
 	}
 }
@@ -45,20 +45,20 @@
 {
 	function load_class($class, $directory = 'libraries', $prefix = 'CI_')
 	{
-		if ($directory != 'core' OR $prefix != 'CI_')
+		if ($directory !== 'core' OR $prefix !== 'CI_')
 		{
 			throw new Exception('Not Implemented: Non-core load_class()');
 		}
-		
+
 		$test = CI_TestCase::instance();
-		
+
 		$obj =& $test->ci_core_class($class);
-		
+
 		if (is_string($obj))
 		{
-			throw new Exception('Bad Isolation: Use ci_set_core_class to set '.$class.'');
+			throw new Exception('Bad Isolation: Use ci_set_core_class to set '.$class);
 		}
-		
+
 		return $obj;
 	}
 }
@@ -74,16 +74,16 @@
 	function remove_invisible_characters($str, $url_encoded = TRUE)
 	{
 		$non_displayables = array();
-		
+
 		// every control character except newline (dec 10)
 		// carriage return (dec 13), and horizontal tab (dec 09)
-		
+
 		if ($url_encoded)
 		{
 			$non_displayables[] = '/%0[0-8bcef]/';	// url encoded 00-08, 11, 12, 14, 15
 			$non_displayables[] = '/%1[0-9a-f]/';	// url encoded 16-31
 		}
-		
+
 		$non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';	// 00-08, 11, 12, 14-31, 127
 
 		do
@@ -166,6 +166,4 @@
 	{
 		return TRUE;
 	}
-}
-
-// EOF
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/tests/mocks/core/input.php b/tests/mocks/core/input.php
index 8a337d2..2a4aa49 100644
--- a/tests/mocks/core/input.php
+++ b/tests/mocks/core/input.php
@@ -1,10 +1,10 @@
 <?php
 
 class Mock_Core_Input extends CI_Input {
-	
+
 	/**
-	 * Since we use GLOBAL to fetch Security and Utf8 classes, 
-	 * we need to use inversion of control to mock up 
+	 * Since we use GLOBAL to fetch Security and Utf8 classes,
+	 * we need to use inversion of control to mock up
 	 * the same process within CI_Input class constructor.
 	 *
 	 * @covers CI_Input::__construct()
diff --git a/tests/mocks/core/loader.php b/tests/mocks/core/loader.php
index d4b29bb..53d88d5 100644
--- a/tests/mocks/core/loader.php
+++ b/tests/mocks/core/loader.php
@@ -1,7 +1,7 @@
 <?php
 
 class Mock_Core_Loader extends CI_Loader {
-	
+
 	/**
 	 * Since we use paths to load up models, views, etc, we need the ability to
 	 * mock up the file system so when core tests are run, we aren't mucking
@@ -15,16 +15,17 @@
 	{
 		vfsStreamWrapper::register();
 		vfsStreamWrapper::setRoot(new vfsStreamDirectory('application'));
-		
+
 		$this->models_dir 	= vfsStream::newDirectory('models')->at(vfsStreamWrapper::getRoot());
 		$this->libs_dir 	= vfsStream::newDirectory('libraries')->at(vfsStreamWrapper::getRoot());
 		$this->helpers_dir 	= vfsStream::newDirectory('helpers')->at(vfsStreamWrapper::getRoot());
 		$this->views_dir 	= vfsStream::newDirectory('views')->at(vfsStreamWrapper::getRoot());
-		
+
 		$this->_ci_ob_level  		= ob_get_level();
 		$this->_ci_library_paths	= array(vfsStream::url('application').'/', BASEPATH);
 		$this->_ci_helper_paths 	= array(vfsStream::url('application').'/', BASEPATH);
 		$this->_ci_model_paths 		= array(vfsStream::url('application').'/');
 		$this->_ci_view_paths 		= array(vfsStream::url('application').'/views/' => TRUE);
 	}
+
 }
\ No newline at end of file
diff --git a/tests/mocks/core/security.php b/tests/mocks/core/security.php
index d7ea0e6..e19a8b2 100644
--- a/tests/mocks/core/security.php
+++ b/tests/mocks/core/security.php
@@ -1,7 +1,7 @@
 <?php
 
 class Mock_Core_Security extends CI_Security {
-	
+
 	public function csrf_set_cookie()
 	{
 		// We cannot set cookie in CLI mode, so for csrf test, who rely on $_COOKIE,
diff --git a/tests/mocks/core/uri.php b/tests/mocks/core/uri.php
index b694609..94f75df 100644
--- a/tests/mocks/core/uri.php
+++ b/tests/mocks/core/uri.php
@@ -1,12 +1,12 @@
 <?php
 
 class Mock_Core_URI extends CI_URI {
-	
+
 	public function __construct()
 	{
 		$test = CI_TestCase::instance();
 		$cls =& $test->ci_core_class('cfg');
-		
+
 		// set predictable config values
 		$test->ci_set_config(array(
 			'index_page'		=> 'index.php',
@@ -14,12 +14,13 @@
 			'subclass_prefix'	=> 'MY_'
 		));
 
-		$this->config = new $cls;	
+		$this->config = new $cls;
 
 	}
-	
+
 	protected function _is_cli_request()
 	{
 		return FALSE;
 	}
+
 }
\ No newline at end of file
diff --git a/tests/mocks/core/utf8.php b/tests/mocks/core/utf8.php
index b77d717..068e74a 100644
--- a/tests/mocks/core/utf8.php
+++ b/tests/mocks/core/utf8.php
@@ -1,27 +1,26 @@
 <?php
 
 class Mock_Core_Utf8 extends CI_Utf8 {
-	
+
 	/**
-	 * We need to define several constants as 
+	 * We need to define several constants as
 	 * the same process within CI_Utf8 class constructor.
 	 *
 	 * @covers CI_Utf8::__construct()
 	 */
 	public function __construct()
 	{
-		defined('UTF8_ENABLED') or define('UTF8_ENABLED', TRUE);
+		defined('UTF8_ENABLED') OR define('UTF8_ENABLED', TRUE);
 
 		if (extension_loaded('mbstring'))
 		{
-			defined('MB_ENABLED') or define('MB_ENABLED', TRUE);
+			defined('MB_ENABLED') OR define('MB_ENABLED', TRUE);
 			mb_internal_encoding('UTF-8');
 		}
 		else
 		{
-			defined('MB_ENABLED') or define('MB_ENABLED', FALSE);
+			defined('MB_ENABLED') OR define('MB_ENABLED', FALSE);
 		}
-		
 	}
 
 }
\ No newline at end of file
diff --git a/tests/mocks/database/config/mysql.php b/tests/mocks/database/config/mysql.php
index ace0a31..a590b9f 100644
--- a/tests/mocks/database/config/mysql.php
+++ b/tests/mocks/database/config/mysql.php
@@ -1,7 +1,7 @@
 <?php
 
 return array(
-	
+
 	// Typical Database configuration
 	'mysql' => array(
 		'dsn' => '',
@@ -9,7 +9,7 @@
 		'username' => 'travis',
 		'password' => '',
 		'database' => 'ci_test',
-		'dbdriver' => 'mysql',
+		'dbdriver' => 'mysql'
 	),
 
 	// Database configuration with failover
@@ -28,7 +28,7 @@
 				'password' => '',
 				'database' => 'ci_test',
 				'dbdriver' => 'mysql',
-			),
-		),
-	),
+			)
+		)
+	)
 );
\ No newline at end of file
diff --git a/tests/mocks/database/config/pdo/mysql.php b/tests/mocks/database/config/pdo/mysql.php
index cefb6b0..96608f7 100644
--- a/tests/mocks/database/config/pdo/mysql.php
+++ b/tests/mocks/database/config/pdo/mysql.php
@@ -1,16 +1,16 @@
 <?php
 
 return array(
-	
+
 	// Typical Database configuration
 	'pdo/mysql' => array(
-		'dsn' => '',
+		'dsn' => 'mysql:host=localhost;dbname=ci_test',
 		'hostname' => 'localhost',
 		'username' => 'travis',
 		'password' => '',
 		'database' => 'ci_test',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'mysql',
+		'subdriver' => 'mysql'
 	),
 
 	// Database configuration with failover
@@ -21,17 +21,17 @@
 		'password' => 'wrong password',
 		'database' => 'not_ci_test',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'mysql',
+		'subdriver' => 'mysql',
 		'failover' => array(
 			array(
-				'dsn' => '',
+				'dsn' => 'mysql:host=localhost;dbname=ci_test',
 				'hostname' => 'localhost',
 				'username' => 'travis',
 				'password' => '',
 				'database' => 'ci_test',
 				'dbdriver' => 'pdo',
-				'pdodriver' => 'mysql',
-			),
-		),
-	),
+				'subdriver' => 'mysql'
+			)
+		)
+	)
 );
\ No newline at end of file
diff --git a/tests/mocks/database/config/pdo/pgsql.php b/tests/mocks/database/config/pdo/pgsql.php
index 5196e9a..e55e3ea 100644
--- a/tests/mocks/database/config/pdo/pgsql.php
+++ b/tests/mocks/database/config/pdo/pgsql.php
@@ -1,7 +1,7 @@
 <?php
 
 return array(
-	
+
 	// Typical Database configuration
 	'pdo/pgsql' => array(
 		'dsn' => 'pgsql:host=localhost;port=5432;dbname=ci_test;',
@@ -10,7 +10,7 @@
 		'password' => '',
 		'database' => 'ci_test',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'pgsql',
+		'subdriver' => 'pgsql'
 	),
 
 	// Database configuration with failover
@@ -21,7 +21,7 @@
 		'password' => 'wrong password',
 		'database' => 'not_ci_test',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'pgsql',
+		'subdriver' => 'pgsql',
 		'failover' => array(
 			array(
 				'dsn' => 'pgsql:host=localhost;port=5432;dbname=ci_test;',
@@ -30,8 +30,8 @@
 				'password' => '',
 				'database' => 'ci_test',
 				'dbdriver' => 'pdo',
-				'pdodriver' => 'pgsql',
-			),
-		),
-	),
+				'subdriver' => 'pgsql'
+			)
+		)
+	)
 );
\ No newline at end of file
diff --git a/tests/mocks/database/config/pdo/sqlite.php b/tests/mocks/database/config/pdo/sqlite.php
index c68b4b2..1bf56b3 100644
--- a/tests/mocks/database/config/pdo/sqlite.php
+++ b/tests/mocks/database/config/pdo/sqlite.php
@@ -10,7 +10,7 @@
 		'password' => 'sqlite',
 		'database' => 'sqlite',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'sqlite',
+		'subdriver' => 'sqlite'
 	),
 
 	// Database configuration with failover
@@ -21,7 +21,7 @@
 		'password' => 'sqlite',
 		'database' => 'sqlite',
 		'dbdriver' => 'pdo',
-		'pdodriver' => 'sqlite',
+		'subdriver' => 'sqlite',
 		'failover' => array(
 			array(
 				'dsn' => 'sqlite:/'.realpath(__DIR__.'/../..').'/ci_test.sqlite',
@@ -29,9 +29,9 @@
 				'username' => 'sqlite',
 				'password' => 'sqlite',
 				'database' => 'sqlite',
-				'dbdriver' => 'pdo', 
-				'pdodriver' => 'sqlite',
-			),
-		),
-	),
+				'dbdriver' => 'pdo',
+				'subdriver' => 'sqlite'
+			)
+		)
+	)
 );
\ No newline at end of file
diff --git a/tests/mocks/database/config/pgsql.php b/tests/mocks/database/config/pgsql.php
index c06af8c..1444b00 100644
--- a/tests/mocks/database/config/pgsql.php
+++ b/tests/mocks/database/config/pgsql.php
@@ -1,7 +1,7 @@
 <?php
 
 return array(
-	
+
 	// Typical Database configuration
 	'pgsql' => array(
 		'dsn' => '',
@@ -9,7 +9,7 @@
 		'username' => 'postgres',
 		'password' => '',
 		'database' => 'ci_test',
-		'dbdriver' => 'postgre',
+		'dbdriver' => 'postgre'
 	),
 
 	// Database configuration with failover
@@ -28,7 +28,7 @@
 				'password' => '',
 				'database' => 'ci_test',
 				'dbdriver' => 'postgre',
-			),
-		),
-	),
+			)
+		)
+	)
 );
\ No newline at end of file
diff --git a/tests/mocks/database/config/sqlite.php b/tests/mocks/database/config/sqlite.php
index 755ce2a..d37ee48 100644
--- a/tests/mocks/database/config/sqlite.php
+++ b/tests/mocks/database/config/sqlite.php
@@ -9,7 +9,7 @@
 		'username' => 'sqlite',
 		'password' => 'sqlite',
 		'database' => realpath(__DIR__.'/..').'/ci_test.sqlite',
-		'dbdriver' => 'sqlite3',
+		'dbdriver' => 'sqlite3'
 	),
 
 	// Database configuration with failover
@@ -27,8 +27,8 @@
 				'username' => 'sqlite',
 				'password' => 'sqlite',
 				'database' => realpath(__DIR__.'/..').'/ci_test.sqlite',
-				'dbdriver' => 'sqlite3',
-			),
-		),
-	),
+				'dbdriver' => 'sqlite3'
+			)
+		)
+	)
 );
\ No newline at end of file
diff --git a/tests/mocks/database/db.php b/tests/mocks/database/db.php
index 59028ed..7565853 100644
--- a/tests/mocks/database/db.php
+++ b/tests/mocks/database/db.php
@@ -6,7 +6,7 @@
 	 * @var array DB configuration
 	 */
 	private $config = array();
-	
+
 	/**
 	 * Prepare database configuration skeleton
 	 *
@@ -21,7 +21,7 @@
 	/**
 	 * Build DSN connection string for DB driver instantiate process
 	 *
-	 * @param 	string 	Group name 		
+	 * @param 	string 	Group name
 	 * @return 	string 	DSN Connection string
 	 */
 	public function set_dsn($group = 'default')
@@ -45,9 +45,9 @@
 		);
 
 		$config = array_merge($this->config[$group], $params);
-		$dsnstring = ( ! empty($config['dsn'])) ? $config['dsn'] : FALSE;
-		$pdodriver = ( ! empty($config['pdodriver'])) ? $config['pdodriver'] : FALSE;
-		$failover = ( ! empty($config['failover'])) ? $config['failover'] : FALSE;
+		$dsnstring = empty($config['dsn']) ? FALSE : $config['dsn'];
+		$subdriver = empty($config['subdriver']) ? FALSE: $config['subdriver'];
+		$failover = empty($config['failover']) ? FALSE : $config['failover'];
 
 		$dsn = $config['dbdriver'].'://'.$config['username'].':'.$config['password']
 			       .'@'.$config['hostname'].'/'.$config['database'];
@@ -55,7 +55,7 @@
 		// Build the parameter
 		$other_params = array_slice($config, 6);
 		if ($dsnstring) $other_params['dsn'] = $dsnstring;
-		if ($pdodriver) $other_params['pdodriver'] = $pdodriver;
+		if ($subdriver) $other_params['subdriver'] = $subdriver;
 		if ($failover) $other_params['failover'] = $failover;
 
 		return $dsn.'?'.http_build_query($other_params);
@@ -65,28 +65,27 @@
 	 * Return a database config array
 	 *
 	 * @see 	./config
-	 * @param 	string 		Driver based configuration
-	 * @return 	array 		
+	 * @param	string	Driver based configuration
+	 * @return	array
 	 */
 	public static function config($driver)
 	{
 		$dir = realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR;
-
 		return include($dir.'config'.DIRECTORY_SEPARATOR.$driver.'.php');
 	}
 
 	/**
 	 * Main DB method wrapper
 	 *
-	 * @param 	string 		Group or DSN string
-	 * @param 	bool 		
-	 * @return 	object 		
+	 * @param 	string	Group or DSN string
+	 * @param 	bool
+	 * @return 	object
 	 */
 	public static function DB($group, $query_builder = FALSE)
 	{
 		include_once(BASEPATH.'database/DB.php');
 
-		try 
+		try
 		{
 			$db = DB($group, $query_builder);
 		}
@@ -97,4 +96,5 @@
 
 		return $db;
 	}
+
 }
\ No newline at end of file
diff --git a/tests/mocks/database/db/driver.php b/tests/mocks/database/db/driver.php
index cb18202..65ac2c4 100644
--- a/tests/mocks/database/db/driver.php
+++ b/tests/mocks/database/db/driver.php
@@ -1,7 +1,7 @@
 <?php
 
 class Mock_Database_DB_Driver extends CI_DB_driver {
-	
+
 	/**
 	 * @var object The actual Driver
 	 */
@@ -16,7 +16,10 @@
 	 */
 	public function __construct($driver_class, $config = array())
 	{
-		if (is_string($driver_class)) $this->ci_db_driver = new $driver_class($config);
+		if (is_string($driver_class))
+		{
+			$this->ci_db_driver = new $driver_class($config);
+		}
 	}
 
 	/**
diff --git a/tests/mocks/database/db/querybuilder.php b/tests/mocks/database/db/querybuilder.php
index 1b95c92..3f22526 100644
--- a/tests/mocks/database/db/querybuilder.php
+++ b/tests/mocks/database/db/querybuilder.php
@@ -1,10 +1,3 @@
 <?php
 
-if ( ! class_exists('CI_DB_query_builder'))
-{
-	class Mock_Database_DB_QueryBuilder extends CI_DB_active_record {}
-}
-else
-{
-	class Mock_Database_DB_QueryBuilder extends CI_DB_query_builder {}
-}
+class Mock_Database_DB_QueryBuilder extends CI_DB_query_builder {}
\ No newline at end of file
diff --git a/tests/mocks/database/drivers/mysql.php b/tests/mocks/database/drivers/mysql.php
index 34a74e2..e0c1fb0 100644
--- a/tests/mocks/database/drivers/mysql.php
+++ b/tests/mocks/database/drivers/mysql.php
@@ -1,16 +1,17 @@
 <?php
 
 class Mock_Database_Drivers_Mysql extends Mock_Database_DB_Driver {
-	
+
 	/**
 	 * Instantiate the database driver
 	 *
-	 * @param  string 	DB Driver class name
-	 * @param  array 	DB configuration to set
-	 * @return void
+	 * @param	string	DB Driver class name
+	 * @param	array	DB configuration to set
+	 * @return	void
 	 */
 	public function __construct($config = array())
 	{
 		parent::__construct('CI_DB_mysql_driver', $config);
 	}
+
 }
\ No newline at end of file
diff --git a/tests/mocks/database/drivers/pdo.php b/tests/mocks/database/drivers/pdo.php
index 590e195..17768ee 100644
--- a/tests/mocks/database/drivers/pdo.php
+++ b/tests/mocks/database/drivers/pdo.php
@@ -1,13 +1,13 @@
 <?php
 
 class Mock_Database_Drivers_PDO extends Mock_Database_DB_Driver {
-	
+
 	/**
 	 * Instantiate the database driver
 	 *
-	 * @param  string 	DB Driver class name
-	 * @param  array 	DB configuration to set
-	 * @return void
+	 * @param	string	DB Driver class name
+	 * @param	array	DB configuration to set
+	 * @return	void
 	 */
 	public function __construct($config = array())
 	{
diff --git a/tests/mocks/database/drivers/postgre.php b/tests/mocks/database/drivers/postgre.php
index 0df9059..5a45115 100644
--- a/tests/mocks/database/drivers/postgre.php
+++ b/tests/mocks/database/drivers/postgre.php
@@ -1,16 +1,17 @@
 <?php
 
 class Mock_Database_Drivers_Postgre extends Mock_Database_DB_Driver {
-	
+
 	/**
 	 * Instantiate the database driver
 	 *
-	 * @param  string 	DB Driver class name
-	 * @param  array 	DB configuration to set
-	 * @return void
+	 * @param	string	DB Driver class name
+	 * @param	array	DB configuration to set
+	 * @return	void
 	 */
 	public function __construct($config = array())
 	{
 		parent::__construct('CI_DB_postgre_driver', $config);
 	}
+
 }
\ No newline at end of file
diff --git a/tests/mocks/database/drivers/sqlite.php b/tests/mocks/database/drivers/sqlite.php
index 76a182c..5124675 100644
--- a/tests/mocks/database/drivers/sqlite.php
+++ b/tests/mocks/database/drivers/sqlite.php
@@ -1,16 +1,17 @@
 <?php
 
 class Mock_Database_Drivers_Sqlite extends Mock_Database_DB_Driver {
-	
+
 	/**
 	 * Instantiate the database driver
 	 *
-	 * @param  string 	DB Driver class name
-	 * @param  array 	DB configuration to set
-	 * @return void
+	 * @param	string	DB Driver class name
+	 * @param	array	DB configuration to set
+	 * @return	void
 	 */
 	public function __construct($config = array())
 	{
 		parent::__construct('CI_DB_sqlite3_driver', $config);
 	}
+
 }
\ No newline at end of file
diff --git a/tests/mocks/database/schema/skeleton.php b/tests/mocks/database/schema/skeleton.php
index 05499f8..18e1ddd 100644
--- a/tests/mocks/database/schema/skeleton.php
+++ b/tests/mocks/database/schema/skeleton.php
@@ -41,8 +41,7 @@
 
 		return static::$db;
 	}
-	
-	
+
 	/**
 	 * Create the dummy tables
 	 *
@@ -54,20 +53,20 @@
 		static::$forge->add_field(array(
 			'id' => array(
 				'type' => 'INTEGER',
-				'constraint' => 3,
+				'constraint' => 3
 			),
 			'name' => array(
 				'type' => 'VARCHAR',
-				'constraint' => 40,
+				'constraint' => 40
 			),
 			'email' => array(
 				'type' => 'VARCHAR',
-				'constraint' => 100,
+				'constraint' => 100
 			),
 			'country' => array(
 				'type' => 'VARCHAR',
-				'constraint' => 40,
-			),
+				'constraint' => 40
+			)
 		));
 		static::$forge->add_key('id', TRUE);
 		static::$forge->create_table('user', (strpos(static::$driver, 'pgsql') === FALSE));
@@ -76,15 +75,15 @@
 		static::$forge->add_field(array(
 			'id' => array(
 				'type' => 'INTEGER',
-				'constraint' => 3,
+				'constraint' => 3
 			),
 			'name' => array(
 				'type' => 'VARCHAR',
-				'constraint' => 40,
+				'constraint' => 40
 			),
 			'description' => array(
-				'type' => 'TEXT',
-			),
+				'type' => 'TEXT'
+			)
 		));
 		static::$forge->add_key('id', TRUE);
 		static::$forge->create_table('job', (strpos(static::$driver, 'pgsql') === FALSE));
@@ -93,15 +92,15 @@
 		static::$forge->add_field(array(
 			'id' => array(
 				'type' => 'INTEGER',
-				'constraint' => 3,
+				'constraint' => 3
 			),
 			'key' => array(
 				'type' => 'VARCHAR',
-				'constraint' => 40,
+				'constraint' => 40
 			),
 			'value' => array(
-				'type' => 'TEXT',
-			),
+				'type' => 'TEXT'
+			)
 		));
 		static::$forge->add_key('id', TRUE);
 		static::$forge->create_table('misc', (strpos(static::$driver, 'pgsql') === FALSE));
@@ -120,28 +119,29 @@
 				array('id' => 1, 'name' => 'Derek Jones', 'email' => 'derek@world.com', 'country' => 'US'),
 				array('id' => 2, 'name' => 'Ahmadinejad', 'email' => 'ahmadinejad@world.com', 'country' => 'Iran'),
 				array('id' => 3, 'name' => 'Richard A Causey', 'email' => 'richard@world.com', 'country' => 'US'),
-				array('id' => 4, 'name' => 'Chris Martin', 'email' => 'chris@world.com', 'country' => 'UK'),
+				array('id' => 4, 'name' => 'Chris Martin', 'email' => 'chris@world.com', 'country' => 'UK')
 			),
 			'job' => array(
-				array('id' => 1, 'name' => 'Developer', 'description' => 'Awesome job, but sometimes makes you bored'), 
+				array('id' => 1, 'name' => 'Developer', 'description' => 'Awesome job, but sometimes makes you bored'),
 				array('id' => 2, 'name' => 'Politician', 'description' => 'This is not really a job'),
-    			array('id' => 3, 'name' => 'Accountant', 'description' => 'Boring job, but you will get free snack at lunch'),
-			    array('id' => 4, 'name' => 'Musician', 'description' => 'Only Coldplay can actually called Musician'),
+    				array('id' => 3, 'name' => 'Accountant', 'description' => 'Boring job, but you will get free snack at lunch'),
+				array('id' => 4, 'name' => 'Musician', 'description' => 'Only Coldplay can actually called Musician')
 			),
 			'misc' => array(
-				array('id' => 1, 'key' => '\\xxxfoo456', 'value' => 'Entry with \\xxx'), 
-				array('id' => 2, 'key' => '\\%foo456', 'value' => 'Entry with \\%'),
-			),
+				array('id' => 1, 'key' => '\\xxxfoo456', 'value' => 'Entry with \\xxx'),
+				array('id' => 2, 'key' => '\\%foo456', 'value' => 'Entry with \\%')
+			)
 		);
 
-		foreach ($data as $table => $dummy_data) 
+		foreach ($data as $table => $dummy_data)
 		{
 			static::$db->truncate($table);
 
 			foreach ($dummy_data as $single_dummy_data)
 			{
-				static::$db->insert($table, $single_dummy_data); 
+				static::$db->insert($table, $single_dummy_data);
 			}
 		}
 	}
+
 }
\ No newline at end of file
diff --git a/tests/mocks/libraries/encrypt.php b/tests/mocks/libraries/encrypt.php
new file mode 100644
index 0000000..f185939
--- /dev/null
+++ b/tests/mocks/libraries/encrypt.php
@@ -0,0 +1,16 @@
+<?php
+
+class Mock_Libraries_Encrypt extends CI_Encrypt {
+
+	// Overide inaccesible protected method
+	public function __call($method, $params)
+	{
+		if (is_callable(array($this, '_'.$method)))
+		{
+			return call_user_func_array(array($this, '_'.$method), $params);
+		}
+
+		throw new BadMethodCallException('Method '.$method.' was not found');
+	}
+
+}
\ No newline at end of file
diff --git a/tests/mocks/libraries/table.php b/tests/mocks/libraries/table.php
index 97fbb30..87c278b 100644
--- a/tests/mocks/libraries/table.php
+++ b/tests/mocks/libraries/table.php
@@ -1,7 +1,7 @@
 <?php
 
 class Mock_Libraries_Table extends CI_Table {
-	
+
 	// Overide inaccesible protected method
 	public function __call($method, $params)
 	{
@@ -12,4 +12,5 @@
 
 		throw new BadMethodCallException('Method '.$method.' was not found');
 	}
+
 }
\ No newline at end of file
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
new file mode 100644
index 0000000..56cb884
--- /dev/null
+++ b/tests/phpunit.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit 
+	bootstrap="./Bootstrap.php"
+	colors="true"
+	convertNoticesToExceptions="true"
+	convertWarningsToExceptions="true"
+	stopOnError="false"
+	stopOnFailure="false"
+	stopOnIncomplete="false"
+	stopOnSkipped="false">
+	<testsuites>
+		<testsuite name="CodeIgniter Core Test Suite">
+			<directory suffix="test.php">./codeigniter/core</directory>
+			<directory suffix="test.php">./codeigniter/helpers</directory>
+			<directory suffix="test.php">./codeigniter/libraries</directory>
+		</testsuite>
+	</testsuites>
+	<filters>
+		<blacklist>
+			<directory suffix=".php">PEAR_INSTALL_DIR</directory>
+			<directory suffix=".php">PHP_LIBDIR</directory>
+		</blacklist>
+	</filters>
+</phpunit>
\ No newline at end of file
diff --git a/user_guide_src/source/DCO.rst b/user_guide_src/source/DCO.rst
new file mode 100644
index 0000000..c8f9b49
--- /dev/null
+++ b/user_guide_src/source/DCO.rst
@@ -0,0 +1,27 @@
+#####################################
+Developer's Certificate of Origin 1.1
+#####################################
+
+By making a contribution to this project, I certify that:
+
+(1)	The contribution was created in whole or in part by me and I
+	have the right to submit it under the open source license
+	indicated in the file; or
+
+(2)	The contribution is based upon previous work that, to the best
+	of my knowledge, is covered under an appropriate open source
+	license and I have the right under that license to submit that
+	work with modifications, whether created in whole or in part
+	by me, under the same open source license (unless I am
+	permitted to submit under a different license), as indicated
+	in the file; or
+
+(3)	The contribution was provided directly to me by some other
+	person who certified (1), (2) or (3) and I have not modified
+	it.
+
+(4)	I understand and agree that this project and the contribution
+	are public and that a record of the contribution (including all
+	personal information I submit with it, including my sign-off) is
+	maintained indefinitely and may be redistributed consistent with
+	this project or the open source license(s) involved.
diff --git a/user_guide_src/source/_themes/eldocs/layout.html b/user_guide_src/source/_themes/eldocs/layout.html
index 4b1a022..01db07c 100644
--- a/user_guide_src/source/_themes/eldocs/layout.html
+++ b/user_guide_src/source/_themes/eldocs/layout.html
@@ -9,6 +9,9 @@
 {%- if project == 'ExpressionEngine' %}{% set project_abbreviation = 'ee' %}{% set project_domain = 'expressionengine.com' %}{% endif -%}
 {%- if project == 'CodeIgniter' %}{% set project_abbreviation = 'ci' %}{% set project_domain = 'codeigniter.com' %}{% endif -%}
 {%- if project == 'MojoMotor' %}{% set project_abbreviation = 'mm' %}{% set project_domain = 'mojomotor.com' %}{% endif -%}
+{%- set exclude_comments = ['index', 'license', 'changelog',
+							'development/index', 'development/extension_hooks/index',
+							'development/guidelines/template'] %}
 
 <html>
 	<head>
@@ -85,10 +88,6 @@
 		<div id="brand" class="{{ project_abbreviation }}">
 			<a href="http://{{ project_domain }}/"><img src="{{ pathto('_static/asset/img/' + project_abbreviation + '-logo.gif', 1) }}" alt="{{ project }}"></a>
 			<p>{{ release }} User Guide</p>
-			{%- if show_source and has_source and sourcename %}
-		    <p><a href="{{ pathto('_sources/' + sourcename, true)|e }}"
-			           rel="nofollow">{{ _('Show Source') }}</a></p>
-			{%- endif %}
 		</div><!-- /#brand -->
 
 		<div id="header">
@@ -101,15 +100,16 @@
 			</form>
 			<ul>
 				{%- block rootrellink %}
-				<li><a href="{{ pathto(master_doc) }}">Home</a>&nbsp;&nbsp;{{ reldelim1 }}</li>
-				<li><a id="toc-link" href="{{ pathto(master_doc) }}">Table of Contents</a>&nbsp;&nbsp;{{ reldelim1 }}</li>
+				<li><a href="{{ pathto(master_doc) }}">User Guide Home</a>{%- if pagename != 'index' %}&nbsp;&nbsp;{{ reldelim1 }}{%- endif %}</li>
 				{%- endblock %}
 				
 				{%- for parent in parents %}
 				<li><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>&nbsp;&nbsp; {{ reldelim1 }}</li>
 		        {%- endfor %}
 
+				{%- if pagename != 'index' %}
 				<li><strong>{{ title }}</strong></li>
+				{%- endif %}
 			</ul>
 		</div><!-- /#header -->
 
@@ -124,49 +124,9 @@
 	
 	{%- block footer %}			
 		<div id="footer">
-			<p class="top">
-				{% if prev %}
-				<span class="prev">Previous Topic: <a href="{{ prev.link }}">{{ prev.title }}</a></span>
-				{% endif %}
-				{% if next %}
-				<span class="next">Next Topic: <a href="{{ next.link }}">{{ next.title }}</a></span>
-				{% endif %}
-				<a href="#header" title="Return to top">Return to top</a>
-			</p>
-			<p><a href="{{ project_url }}">{{ project }}</a> &ndash; Copyright &copy; {{ copyright }}</a> &ndash; Last updated: {{ last_updated }}</p>
+			<p class="top"><a href="#header" title="Return to top">Return to top</a></p>
+			<p><a href="{{ project_url }}">{{ project }}</a> &ndash; Copyright &copy; {{ copyright }}</a></p>
 		</div><!-- /#footer -->
 	{%- endblock %}
-	
-	<script src="{{ pathto('_static/asset/js/jquery-ui-min.js', 1) }}" type="text/javascript" charset="utf-8" async></script>
-	<script type="text/javascript" charset="utf-8">
-		$('#toc-link').click(function(){
-			$('#table-contents').animate({ left: '0' },1000);
-			return false;
-		});
-		$('html').click(function(){
-			if ($('#table-contents').css("left") == '0px'){
-				$('#table-contents').animate({ left: '-520' },1000);
-			}
-		});
-		$('#table-contents').click(function(event){
-			event.stopPropagation();
-		});
-/*		$('*:not(#table-contents,#toc-link)').click(function(){
-			if ($('#table-contents').css("left") == '0px'){
-				$('#table-contents').animate({ left: '-520' },1000);
-			}
-		});
-/*		$("#toc-link").click(function () {
-			$('#table-contents').show("slide", { direction: "left" }, 100);
-			event.stopPropagation();
-			return false;
-		});
-		$('*:not(#table-contents,#toc-link)').click(function () {
-			if ($('#table-contents').is(":visible")) {
-				$('#table-contents').hide("slide", { direction: "left" }, 100);
-				event.stopPropagation();	
-			}
-		}); */
-	</script>
 	</body>
-</html>
\ No newline at end of file
+</html>
diff --git a/user_guide_src/source/_themes/eldocs/static/asset/css/common.css b/user_guide_src/source/_themes/eldocs/static/asset/css/common.css
index 66768ba..6cabda0 100644
--- a/user_guide_src/source/_themes/eldocs/static/asset/css/common.css
+++ b/user_guide_src/source/_themes/eldocs/static/asset/css/common.css
@@ -49,7 +49,9 @@
 
 h1{ font-size: 28px; }
 
-h2{ font-size: 24px; }
+h2{ font-size: 24px; font-weight: normal; }
+
+h1, h2, h3, h4, h5, h6{ margin-bottom: 20px; }
 
 h2, h3{ border-bottom: 2px solid #EEEEEE; padding: 0 0 3px; }	
 
@@ -73,6 +75,10 @@
 	li > ol{ margin: 0; margin-left: 40px; }
 
 	dl > dd{ margin-left: 20px; }
+	
+	li > p { margin: 0; }
+
+#expressionengine-user-guide li em { font-style: normal; }
 
 p, li, dd, dt, pre{ line-height: 1.5; }
 
@@ -141,39 +147,31 @@
 fieldset{ border: 0; }
 
 .top{ float: right; }
-.next{ padding: 0 20px 0 10px; }
-.prev{ padding-right: 10px; }
 
-.highlight-ci,
+.admonition,
 .highlight-ee,
+.highlight-ci,
 .highlight-rst,
 .highlight-bash,
 .highlight-perl,
+.highlight-php,
 .cp-path,
-.important,
-.note{ 
-	background-color: #F5FBFF; 
+.codeblock{ 
+	background-color: #F9FEFF; 
 	border: 1px solid #C8DEF0; 
-	margin: 20px 0 20px 20px; 
+	-moz-box-shadow: 4px 4px 0 rgba(0,0,0,0.03);
+	-webkit-box-shadow: 4px 4px 0 rgba(0,0,0,0.03);
+	box-shadow: 4px 4px 0 rgba(0,0,0,0.03);
+	margin: 20px 0; 
 	padding: 10px 10px 8px; 
 }
 
-	.highlight-ci,
-	.highlight-ee,
-	.highlight-rst,
-	.highlight-bash,
-	.highlight-perl{
-		-moz-box-shadow: 4px 4px 0 rgba(0,0,0,0.03);
-		-webkit-box-shadow: 4px 4px 0 rgba(0,0,0,0.03);
-		box-shadow: 4px 4px 0 rgba(0,0,0,0.03);
-	}
-
-	.cp-path{ background-color: #FFFDED; border-color: #D1CDB0; }
-	.important, .note{ background-color: #F2FFE8; border-color: #B9D3A6; }
-	.highlight-rst{ background-color: #F9FEFE; border-color: #AACFCF; }
+	.admonition p{ margin: 0; }
 	
-	.important p,
-	.note p{ margin: 0; }
+	.codeblock{ margin: 10px 0; }
+	
+	.cp-path{ background-color: #FAFFF6; border-color: #D1CDB0; }
+	.important, .note{ background-color: #FFFFF2; border-color: #C8C8A5; }
 	
 .admonition-title{ 
 	float: left;
@@ -295,6 +293,43 @@
 
 	#footer p{ margin: 0; }
 
+#comments,
+#feedLink{ background: #FCFCFC; padding: 1px 40px 20px; }
+
+	#comments{ border-top: 1px solid #CCCCCC; }
+	#comments h3{ margin: 20px 0; }
+
+.comments td.avatar{ min-width: 100px; }
+
+.comments td.column1,
+.comments td.post{ background-color: #FFFFFF; padding: 10px; }
+
+.comments td.staffeven{ border-left: 10px solid #C8DEF0; }
+
+#comment_form p,
+.comments p{ margin: 0; }
+
+	.comments p{ margin-bottom: 10px; }
+
+#comment_form textarea{
+	-moz-box-sizing: border-box;
+	-webkit-box-sizing: border-box;
+	box-sizing: border-box;
+	margin-bottom: 10px;
+	resize: none; 
+	width: 100%; 
+}
+
+#comment_form input[type="submit"]{ margin-top: 10px; }
+
+	#comment_form textarea:focus{ background-color: #FFFFF2; border: 1px solid #666666; outline: 0; }
+
+	#commentFormInstructions{ font-size: 12px; margin: 20px 0; }
+
+	#feedLink a{ font-size: 16px; }
+
+	#feedLink a img{ float: left; margin-right: 5px; }
+
 @media (max-width:800px){
 	#footer .top,
 	#header form{ float: none; margin-bottom: 10px; }
@@ -310,4 +345,4 @@
 
 @media screen and (-webkit-min-device-pixel-ratio:0){
 	#header input[type="submit"]{ padding-bottom: 7px; }
-}
\ No newline at end of file
+}
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index a234d69..f3c5cf7 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -19,90 +19,129 @@
 -  General Changes
 
    -  PHP 5.1.6 is no longer supported. CodeIgniter now requires PHP 5.2.4.
+   -  ``$_SERVER['CI_ENV']`` can now be set to control the ``ENVIRONMENT`` constant.
    -  Added an optional backtrace to php-error template.
    -  Added Android to the list of user agents.
    -  Added Windows 7, Android, Blackberry and iOS to the list of user platforms.
+   -  Added Fennec (Firefox for mobile) to the list of mobile user agents.
    -  Ability to log certain error types, not all under a threshold.
    -  Added support for pem, p10, p12, p7a, p7c, p7m, p7r, p7s, crt, crl, der, kdb, rsa, cer, sst, csr Certs to mimes.php.
    -  Added support for pgp and gpg to mimes.php.
    -  Added support for 3gp, 3g2, mp4, wmv, f4v, vlc Video files to mimes.php.
    -  Added support for m4a, aac, m4u, xspf, au, ac3, flac, ogg Audio files to mimes.php.
    -  Added support for kmz and kml (Google Earth) files to mimes.php.
-   -  Added Romanian and Greek characters in foreign_characters.php
+   -  Added support for ics Calendar files to mimes.php.
+   -  Added support for rar archives to mimes.php.
+   -  Updated support for xml ('application/xml') and xsl ('application/xml', 'text/xsl') files in mimes.php.
    -  Updated support for doc files in mimes.php.
-   -  Added application/xml for xml and application/xml, text/xsl for xsl in mimes.php.
+   -  Updated support for php files in mimes.php.
+   -  Updated support for zip files in mimes.php.
+   -  Added some more doctypes.
+   -  Added Romanian and Greek characters in foreign_characters.php.
    -  Changed logger to only chmod when file is first created.
    -  Removed previously deprecated SHA1 Library.
    -  Removed previously deprecated use of ``$autoload['core']`` in application/config/autoload.php.
       Only entries in ``$autoload['libraries']`` are auto-loaded now.
-   -  Added some more doctypes.
+   -  Removed previously deprecated EXT constant.
    -  Updated all classes to be written in PHP 5 style, with visibility declarations and no ``var`` usage for properties.
    -  Moved error templates to "application/views/errors"
+   -  Global config files are loaded first, then environment ones. Environment config keys overwrite base ones, allowing to only set the keys we want changed per environment.
+   -  Changed detection of ``$view_folder`` so that if it's not found in the current path, it will now also be searched for under the application folder.
+   -  Path constants BASEPATH, APPPATH and VIEWPATH are now (internally) defined as absolute paths.
+   -  Updated email validation methods to use filter_var() instead of PCRE.
 
 -  Helpers
 
-   -  url_title() will now trim extra dashes from beginning and end.
+   -  :doc:`Date Helper <helpers/date_helper>` changes include:
+	 - ``now()`` now works with all timezone strings supported by PHP.
+	 - Added an optional third parameter to ``timespan()`` that constrains the number of time units displayed.
+	 - Added an optional parameter to ``timezone_menu()`` that allows more attributes to be added to the generated select tag.
+	 - Deprecated ``standard_date()``, which now just uses the native ``date()`` with `DateTime constants <http://bg2.php.net/manual/en/class.datetime.php#datetime.constants.types>`_.
+   -  ``create_captcha()`` accepts additional colors parameter, allowing for color customization.
+   -  :doc:`URL Helper <helpers/url_helper>` changes include:
+	 - ``url_title()`` will now trim extra dashes from beginning and end.
+	 - ``anchor_popup()`` will now fill the "href" attribute with the URL and its JS code will return false instead.
+	 - Added JS window name support to ``anchor_popup()`` function.
+	 - Added support (auto-detection) for HTTP/1.1 response code 303 in ``redirect()``.
    -  Added XHTML Basic 1.1 doctype to :doc:`HTML Helper <helpers/html_helper>`.
-   -  Changed humanize() to include a second param for the separator.
+   -  Changed ``humanize()`` to include a second param for the separator.
    -  Refactored ``plural()`` and ``singular()`` to avoid double pluralization and support more words.
    -  Added an optional third parameter to ``force_download()`` that enables/disables sending the actual file MIME type in the Content-Type header (disabled by default).
-   -  Added an optional third parameter to ``timespan()`` that constrains the number of time units displayed.
-   -  Added a work-around in force_download() for a bug Android <= 2.1, where the filename extension needs to be in uppercase.
-   -  form_dropdown() will now also take an array for unity with other form helpers.
-   -  set_realpath() can now also handle file paths as opposed to just directories.
-   -  do_hash() now uses PHP's native hash() function, supporting more algorithms.
-   -  Added an optional paramater to ``delete_files()`` to enable it to skip deleting files such as .htaccess and index.html.
-   -  Removed deprecated helper function ``js_insert_smiley()`` from smiley helper.
+   -  Added a work-around in ``force_download()`` for a bug Android <= 2.1, where the filename extension needs to be in uppercase.
+   -  ``form_dropdown()`` will now also take an array for unity with other form helpers.
+   -  ``do_hash()`` now uses PHP's native ``hash()`` function (supporting more algorithms) and is deprecated.
+   -  Removed previously deprecated helper function ``js_insert_smiley()`` from smiley helper.
+   -  :doc:`File Helper <helpers/file_helper>` changes include:
+	 - ``set_realpath()`` can now also handle file paths as opposed to just directories.
+	 - Added an optional paramater to ``delete_files()`` to enable it to skip deleting files such as .htaccess and index.html.
+	 - ``read_file()`` is now a deprecated alias of ``file_get_contents()``.
+   -  :doc:`Security Helper <helpers/security_helper>` function ``strip_image_tags()`` is now an alias for the same method in the :doc:`Security Library <libraries/security>`.
 
 -  Database
 
-   -  Renamed the Active Record class to Query Builder to remove confusion with the Active Record design pattern.
-   -  Added the ability to insert objects with insert_batch() in :doc:`Query Builder <database/query_builder>`.
-   -  Added new :doc:`Query Builder <database/query_builder>` methods that return the SQL string of queries without executing them: get_compiled_select(), get_compiled_insert(), get_compiled_update(), get_compiled_delete().
-   -  Adding $escape parameter to the order_by() method, this enables ordering by custom fields.
+   -  :doc:`Query Builder <database/query_builder>` changes include:
+	 - Renamed the Active Record class to Query Builder to remove confusion with the Active Record design pattern.
+	 - Added the ability to insert objects with insert_batch().
+	 - Added new methods that return the SQL string of queries without executing them: get_compiled_select(), get_compiled_insert(), get_compiled_update(), get_compiled_delete().
+	 - 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().
+	 - Added support for join() with multiple conditions.
+	 - Added support for USING in join().
+	 - Changed limit() to ignore NULL values instead of always casting to integer.
+	 - Changed offset() to ignore empty values instead of always casting to integer.
    -  Improved support for the MySQLi driver, including:
-	 -  OOP style of the PHP extension is now used, instead of the procedural aliases.
-	 -  Server version checking is now done via ``mysqli::$server_info`` instead of running an SQL query.
-	 -  Added persistent connections support for PHP >= 5.3.
+	 - OOP style of the PHP extension is now used, instead of the procedural aliases.
+	 - Server version checking is now done via ``mysqli::$server_info`` instead of running an SQL query.
+	 - Added persistent connections support for PHP >= 5.3.
+	 - Added support for backup() in :doc:`Database Utilities <database/utilities>`.
    -  Added 'dsn' configuration setting for drivers that support DSN strings (PDO, PostgreSQL, Oracle, ODBC, CUBRID).
    -  Improved PDO database support.
-   -  Added Interbase/Firebird database support via the "interbase" driver.
+   -  Added Interbase/Firebird database support via the 'ibase' driver.
    -  Added an optional database name parameter to db_select().
    -  Replaced the _error_message() and _error_number() methods with error(), that returns an array containing the last database error code and message.
    -  Improved version() implementation so that drivers that have a native function to get the version number don't have to be defined in the core DB_driver class.
    -  Improved support of the PostgreSQL driver, including:
-	 -  pg_version() is now used to get the database version number, when possible.
-	 -  Added db_set_charset() support.
-	 -  Added _optimize_table() support for the :doc:`Database Utility Class <database/utilities>` (rebuilds table indexes).
-	 -  Added boolean data type support in escape().
-	 -  Added update_batch() support.
+	 - pg_version() is now used to get the database version number, when possible.
+	 - Added db_set_charset() support.
+	 - Added support for optimize_table() in :doc:`Database Utilities <database/utilities>` (rebuilds table indexes).
+	 - Added boolean data type support in escape().
+	 - Added update_batch() support.
+	 - Removed limit() and order_by() support for UPDATE and DELETE queries in as PostgreSQL does not support those features.
    -  Added a constructor to the DB_result class and moved all driver-specific properties and logic out of the base DB_driver class to allow better abstraction.
-   -  Removed limit() and order_by() support for UPDATE and DELETE queries in PostgreSQL driver. Postgres does not support those features.
    -  Removed protect_identifiers() and renamed internal method _protect_identifiers() to it instead - it was just an alias.
+   -  Renamed internal method _escape_identifiers() to escape_identifiers().
+   -  Updated escape_identifiers() to accept an array of fields as well as strings.
    -  MySQL and MySQLi drivers now require at least MySQL version 5.1.
    -  db_set_charset() now only requires one parameter (collation was only needed due to legacy support for MySQL versions prior to 5.1).
-   -  Added DSN string support for CUBRID.
-   -  Added persistent connections support for CUBRID.
-   -  Added random ordering support for MSSQL, SQLSRV.
    -  Added support for SQLite3 database driver.
+   -  Improved support of the CUBRID driver, including:
+	 - Added DSN string support.
+	 - Added persistent connections support.
+	 - Improved list_databases() in :doc:`Database Utility <database/utilities>` (until now only the currently used database was returned).
+   -  Improved support of the MSSQL and SQLSRV drivers, including:
+	 - Added random ordering support.
+	 - Added support for optimize_table() in :doc:`Database Utility <database/utilities>`.
+	 - Added escaping with QUOTE_IDENTIFIER setting detection.
+	 - Added port handling support for UNIX-based systems (MSSQL driver).
+	 - Added OFFSET support for SQL Server 2005 and above.
    -  Improved support of the Oracle (OCI8) driver, including:
-	 -  Added DSN string support (Easy Connect and TNS).
-	 -  Added support for dropping tables to :doc:`Database Forge <database/forge>`.
-	 -  Added support for listing database schemas to :doc:`Database Utilities <database/utilities>`.
-	 -  Generally improved for speed and cleaned up all of its components.
-	 -  *Row* result methods now really only fetch only the needed number of rows, instead of depending entirely on result().
-	 -  num_rows() is now only called explicitly by the developer and no longer re-executes statements.
-   -  Added replace() support for SQLite.
-   -  Renamed internal method _escape_identifiers() to escape_identifiers().
-   -  Added SQLite support for drop_table() in :doc:`Database Forge <database/forge>`.
+	 - Added DSN string support (Easy Connect and TNS).
+	 - Added support for drop_table() in :doc:`Database Forge <database/forge>`.
+	 - Added support for list_databases() in :doc:`Database Utilities <database/utilities>`.
+	 - Generally improved for speed and cleaned up all of its components.
+	 - num_rows() is now only called explicitly by the developer and no longer re-executes statements.
+   -  Improved support of the SQLite driver, including:
+	 - Added support for replace() in :doc:`Query Builder <database/query_builder>`.
+	 - Added support for drop_table() in :doc:`Database Forge <database/forge>`.
    -  Added ODBC support for create_database(), drop_database() and drop_table() in :doc:`Database Forge <database/forge>`.
    -  Added PDO support for create_database(), drop_database and drop_table() in :doc:`Database Forge <database/forge>`.
-   -  Added MSSQL, SQLSRV support for optimize_table() in :doc:`Database Utility <database/utilities>`.
-   -  Improved CUBRID support for list_databases() in :doc:`Database Utility <database/utilities>` (until now only the currently used database was returned).
    -  Added unbuffered_row() method for getting a row without prefetching whole result (consume less memory).
+   -  Added PDO support for ``list_fields()`` in :doc:`Database Results <database/results>`.
+   -  Added capability for packages to hold database.php config files 
+   -  Added subdrivers support (currently only used by PDO).
 
 -  Libraries
 
+   -  CI_Session now respects php.ini's session.gc_probability and session.gc_divisor
    -  Added max_filename_increment config setting for Upload library.
    -  CI_Loader::_ci_autoloader() is now a protected method.
    -  Added custom filename to Email::attach() as $this->email->attach($filename, $disposition, $newname).
@@ -110,14 +149,16 @@
    -  :doc:`Cart library <libraries/cart>` changes include:
 	 -  It now auto-increments quantity's instead of just resetting it, this is the default behaviour of large e-commerce sites.
 	 -  Product Name strictness can be disabled via the Cart Library by switching "$product_name_safe".
-	 -  Added function remove() to remove a cart item, updating with quantity of 0 seemed like a hack but has remained to retain compatability.
+	 -  Added function remove() to remove a cart item, updating with quantity of 0 seemed like a hack but has remained to retain compatibility.
    -  :doc:`Image Manipulation library <libraries/image_lib>` changes include:
 	 -  The initialize() method now only sets existing class properties.
 	 -  Added support for 3-length hex color values for wm_font_color and wm_shadow_color properties, as well as validation for them.
 	 -  Class properties wm_font_color, wm_shadow_color and wm_use_drop_shadow are now protected, to avoid breaking the text_watermark() method if they are set manually after initialization.
 	 -  If property maintain_ratio is set to TRUE, image_reproportion() now doesn't need both width and height to be specified.
+	 -  Property maintain_ratio is now taken into account when resizing images using ImageMagick library
    -  Removed SHA1 function in the :doc:`Encryption Library <libraries/encryption>`.
    -  Added $config['csrf_regeneration'] to the CSRF protection in the :doc:`Security library <libraries/security>`, which makes token regeneration optional.
+   -  Added $config['csrf_exclude_uris'] to the CSRF protection in the :doc:`Security library <libraries/security>`, which allows you list URIs which will not have the CSRF validation functions run.
    -  :doc:`Form Validation library <libraries/form_validation>` changes include:
 	 -  Added method error_array() to return all error messages as an array.
 	 -  Added method set_data() to set an alternative data array to be validated instead of the default $_POST.
@@ -126,24 +167,42 @@
 	 -  _execute() now considers input data to be invalid if a specified rule is not found.
 	 -  Removed method is_numeric() as it exists as a native PHP function and _execute() will find and use that (the 'is_numeric' rule itself is deprecated since 1.6.1).
 	 -  Native PHP functions used as rules can now accept an additional parameter, other than the data itself.
+	 -  Updated set_rules() to accept an array of rules as well as a string.
+	 -  Fields that have empty rules set no longer run through validation (and therefore are not considered erroneous).
    -  Changed the :doc:`Session Library <libraries/sessions>` to select only one row when using database sessions.
    -  Added all_flashdata() method to session class. Returns an associative array of only flashdata.
    -  Allowed for setting table class defaults in a config file.
    -  Added a Wincache driver to the :doc:`Caching Library <libraries/caching>`.
+   -  Added a Redis driver to the :doc:`Caching Library <libraries/caching>`.
    -  Added dsn (delivery status notification) option to the :doc:`Email Library <libraries/email>`.
+   -  Renamed method _set_header() to set_header() and made it public to enable adding custom headers in the :doc:`Email Library <libraries/email>`.
+   -  Added an "index" parameter to the data() method in the :doc:`Upload Library <libraries/file_uploading>`.
+   -  :doc:`Pagination Library <libraries/pagination>` changes include:
+	 -  Added support for the anchor "rel" attribute.
+	 -  Added support for setting custom attributes.
+	 -  Deprecated usage of the "anchor_class" setting (use the new "attributes" setting instead).
+	 -  Added $config['reuse_query_string'] to allow automatic repopulation of query string arguments, combined with normal URI segments.
+   -  Added the ability to use a proxy with the :doc:`XML-RPC Library <libraries/xmlrpc>`.
 
 -  Core
 
-   -  Changed private functions in CI_URI to protected so MY_URI can override them.
+   -  _exception_handler now respects php.ini's display_errors
+   -  Changed private methods in the :doc:`URI Library <libraries/uri>` to protected so MY_URI can override them.
    -  Removed CI_CORE boolean constant from CodeIgniter.php (no longer Reactor and Core versions).
-   -  Added method get_vars() to CI_Loader to retrieve all variables loaded with $this->load->vars().
+   -  Added method get_vars() to the :doc:`Loader Library <libraries/loader>` to retrieve all variables loaded with $this->load->vars().
    -  is_loaded() function from system/core/Commons.php now returns a reference.
    -  $config['rewrite_short_tags'] now has no effect when using PHP 5.4 as *<?=* will always be available.
-   -  Added method() to CI_Input to retrieve $_SERVER['REQUEST_METHOD'].
+   -  Added method() to the :doc:`Input Library <libraries/input>` to retrieve $_SERVER['REQUEST_METHOD'].
    -  Modified valid_ip() to use PHP's filter_var() in the :doc:`Input Library <libraries/input>`.
    -  Added support for HTTP-Only cookies with new config option ``cookie_httponly`` (default FALSE).
    -  Renamed method _call_hook() to call_hook() in the :doc:`Hooks Library <general/hooks>`.
    -  Added get_content_type() method to the :doc:`Output Library <libraries/output>`.
+   -  Added get_mimes() function to system/core/Commons.php to return the config/mimes.php array.
+   -  Added a second argument to set_content_type() in the :doc:`Output Library <libraries/output>` that allows setting the document charset as well.
+   -  $config['time_reference'] now supports all timezone strings supported by PHP.
+   -  Added support for HTTP code 303 ("See Other") in set_status_header().
+   -  Changed :doc:`Config Library <libraries/config>` method site_url() to accept an array as well.
+   -  Added method ``strip_image_tags()`` to the :doc:`Security Library <libraries/security>`.
 
 Bug fixes for 3.0
 ------------------
@@ -151,7 +210,7 @@
 -  Fixed a bug where ``unlink()`` raised an error if cache file did not exist when you try to delete it.
 -  Fixed a bug (#181) where a mis-spelling was in the form validation language file.
 -  Fixed a bug (#159, #163) that mishandled Query Builder nested transactions because _trans_depth was not getting incremented.
--  Fixed a bug (#737, #75) where pagination anchor class was not set properly when using initialize method.
+-  Fixed a bug (#737, #75) - :doc:`Pagination <libraries/pagination>` anchor class was not set properly when using initialize method.
 -  Fixed a bug (#419) - auto_link() now recognizes URLs that come after a word boundary.
 -  Fixed a bug (#724) - is_unique in form validation now checks that you are connected to a database.
 -  Fixed a bug (#647) - _get_mod_time() in Zip library no longer generates stat failed errors.
@@ -162,7 +221,7 @@
 -  Fixed a bug (#797) - timespan() was using incorrect seconds for year and month.
 -  Fixed a bug in CI_Cart::contents() where if called without a TRUE (or equal) parameter, it would fail due to a typo.
 -  Fixed a bug (#696) - make oci_execute() calls inside num_rows() non-committing, since they are only there to reset which row is next in line for oci_fetch calls and thus don't need to be committed.
--  Fixed a bug (#406) - sqlsrv DB driver not returning resource on ``db_pconnect()``.
+-  Fixed a bug (#406) - SQLSRV DB driver not returning resource on ``db_pconnect()``.
 -  Fixed a bug in CI_Image_lib::gd_loaded() where it was possible for the script execution to end or a PHP E_WARNING message to be emitted.
 -  Fixed a bug in the :doc:`Pagination library <libraries/pagination>` where when use_page_numbers=TRUE previous link and page 1 link did not have the same url.
 -  Fixed a bug (#561) - Errors in :doc:`XML-RPC Library <libraries/xmlrpc>` were not properly escaped.
@@ -220,32 +279,75 @@
 -  Fixed a bug (#121) - ``CI_DB_result::row()`` returned an array when there's no actual result to be returned.
 -  Fixed a bug (#319) - SQLSRV's affected_rows() method failed due to a scrollable cursor being created for write-type queries.
 -  Fixed a bug (#356) - PostgreSQL driver didn't have an _update_batch() method, which resulted in fatal error being triggered when update_batch() is used with it.
--  Fixed a bug (#862) - create_table() failed on SQLSRV/MSSQL when used with 'IF NOT EXISTS'.
+-  Fixed a bug (#784, #862) - :doc:`Database Forge <database/forge>` method ``create_table()`` failed on SQLSRV/MSSQL when used with 'IF NOT EXISTS'.
+-  Fixed a bug (#1419) - libraries/Driver.php had a static variable that was causing an error.
+-  Fixed a bug (#1411) - the :doc:`Email library <libraries/email>` used its own short list of MIMEs instead the one from config/mimes.php.
+-  Fixed a bug where the magic_quotes_runtime setting wasn't turned off for PHP 5.3 (where it is indeed deprecated, but not non-existent).
+-  Fixed a bug (#666) - :doc:`Output library <libraries/output>`'s set_content_type() method didn't set the document charset.
+-  Fixed a bug (#784, #861) - :doc:`Database Forge <database/forge>` method ``create_table()`` used to accept constraints for MSSQL/SQLSRV integer-type columns.
+-  Fixed a bug (#706) - SQLSRV/MSSSQL didn't escape field names.
+-  Fixed a bug (#1452) - protect_identifiers() didn't properly detect identifiers with spaces in their names.
+-  Fixed a bug where protect_identifiers() ignored it's extra arguments when the value passed to it is an array.
+-  Fixed a bug where _has_operator() didn't detect BETWEEN.
+-  Fixed a bug in :doc:`Query Builder <database/query_builder>`'s join() method where it failed with identifiers containing dashes.
+-  Fixed a bug (#1264) - :doc:`Database Forge <database/forge>` and :doc:`Database Utilities <database/utilities>` didn't update/reset the databases and tables list cache when a table or a database is created, dropped or renamed.
+-  Fixed a bug (#7) - :doc:`Query Builder <database/query_builder>`'s join() method only escaped one set of conditions.
+-  Fixed a bug (#1321) - Core Exceptions class couldn't find the errors/ folder in some cases.
+-  Fixed a bug in the File-based :doc:`Cache Library <libraries/caching>` driver's get_metadata() method where a non-existent array key was accessed for the TTL value.
+-  Fixed a bug (#1202) - :doc:`Encryption Library <libraries/encryption>` encode_from_legacy() didn't set back the encrypt mode on failure.
+-  Fixed a bug (#145) - compile_binds() failed when the bind marker was present in a literal string within the query.
+-  Fixed a bug in protect_identifiers() where if passed along with the field names, operators got escaped as well.
+-  Fixed a bug (#10) - :doc:`URI Library <libraries/uri>` internal method _detect_uri() failed with paths containing a colon.
+-  Fixed a bug (#1387) - :doc:`Query Builder <database/query_builder>`'s from() method didn't escape table aliases.
+-  Fixed a bug (#520) - :doc:`Date Helper <helpers/date_helper>` function nice_date() failed when the optional second parameter is not passed.
+-  Fixed a bug (#167) - ``$config['permitted_uri_chars']`` didn't affect URL-encoded characters.
+-  Fixed a bug (#318) - :doc:`Profiling <general/profiling>` setting *query_toggle_count* was not settable as described in the manual.
+-  Fixed a bug (#938) - :doc:`Config Library <libraries/config>` method site_url() added a question mark to the URL string when query strings are enabled even if it already existed.
+-  Fixed a bug (#999) - :doc:`Config Library <libraries/config>` method site_url() always appended ``$config['url_suffix']`` to the end of the URL string, regardless of wether a query string exists in it.
+-  Fixed a bug where :doc:`URL Helper <helpers/url_helper>` function anchor_popup() ignored the attributes argument if it is not an array.
+-  Fixed a bug (#1328) - :doc:`Form Validation Library <libraries/form_validation>` didn't properly check the type of the form fields before processing them.
+-  Fixed a bug (#79) - :doc:`Form Validation Library <libraries/form_validation>` didn't properly validate array fields that use associative keys or have custom indexes.
+-  Fixed a bug (#427) - :doc:`Form Validation Library <libraries/form_validation>` method ``strip_image_tags()`` was an alias to a non-existent method.
+-  Fixed a bug (#1545) - :doc:`Query Builder <database/query_builder>` method ``limit()`` wasn't executed properly under Oracle.
+-  Fixed a bug (#1551) - :doc:`Date Helper <helpers/date_helper>` function ``standard_date()`` didn't properly format *W3C* and *ATOM* standard dates.
+-  Fixed a bug in :doc:`Query Builder <database/query_builder>` method join() where literal values were escaped as if they were fields.
+
+Version 2.1.2
+=============
+
+Release Date: June 29, 2012
+
+-  General Changes
+   -  Improved security in ``xss_clean()``.
 
 Version 2.1.1
 =============
 
-Release Date: Not Released
+Release Date: June 12, 2012
 
 -  General Changes
    -  Fixed support for docx, xlsx files in mimes.php.
 
 -  Libraries
    -  Further improved MIME type detection in the :doc:`File Uploading Library <libraries/file_uploading>`.
+   -  Added support for IPv6 to the :doc:`Input Library <libraries/input>`.
+   -  Added support for the IP format parameter to the :doc:`Form Validation Library <libraries/form_validation>`.
 
 -  Helpers
-   -  url_title() performance and output improved. You can now use any string as the word delimiter, but 'dash' and 'underscore' are still supported.
+   -  ``url_title()`` performance and output improved. You can now use any string as the word delimiter, but 'dash' and 'underscore' are still supported.
 
 Bug fixes for 2.1.1
 -------------------
 
--  Fixed a bug (#697) - A wrong array key was used in the Upload library to check for mime-types.
--  Fixed a bug - form_open() compared $action against site_url() instead of base_url().
--  Fixed a bug - CI_Upload::_file_mime_type() could've failed if mime_content_type() is used for the detection and returns FALSE.
+-  Fixed a bug (#697) - A wrong array key was used in the :doc:`File Uploading Library <libraries/file_uploading>` to check for mime-types.
+-  Fixed a bug - ``form_open()`` compared $action against ``site_url()`` instead of ``base_url()``.
+-  Fixed a bug - ``CI_Upload::_file_mime_type()`` could've failed if ``mime_content_type()`` is used for the detection and returns FALSE.
 -  Fixed a bug (#538) - Windows paths were ignored when using the :doc:`Image Manipulation Library <libraries/image_lib>` to create a new file.
 -  Fixed a bug - When database caching was enabled, $this->db->query() checked the cache before binding variables which resulted in cached queries never being found.
 -  Fixed a bug - CSRF cookie value was allowed to be any (non-empty) string before being written to the output, making code injection a risk.
 -  Fixed a bug (#726) - PDO put a 'dbname' argument in it's connection string regardless of the database platform in use, which made it impossible to use SQLite.
+-  Fixed a bug - ``CI_DB_pdo_driver::num_rows()`` was not returning properly value with SELECT queries, cause it was relying on ``PDOStatement::rowCount()``.
+-  Fixed a bug (#1059) - ``CI_Image_lib::clear()`` was not correctly clearing all necessary object properties, namely width and height.
 
 Version 2.1.0
 =============
@@ -281,7 +383,7 @@
       injection.
    -  Added additional option 'none' for the optional third argument for
       $this->db->like() in the :doc:`Database
-      Driver <database/active_record>`.
+      Driver <database/query_builder>`.
    -  Added $this->db->insert_batch() support to the OCI8 (Oracle) driver.
    -  Added failover if the main connections in the config should fail
 
@@ -379,7 +481,6 @@
    -  Added insert_batch() function to the PostgreSQL database driver.
       Thanks to epallerols for the patch.
    -  Added "application/x-csv" to mimes.php.
-   -  Added CSRF protection URI whitelisting.
    -  Fixed a bug where :doc:`Email library <libraries/email>`
       attachments with a "." in the name would using invalid MIME-types.
 
@@ -1556,27 +1657,27 @@
 -  Active Record
 
    -  Added protect_identifiers() in :doc:`Active
-      Record <./database/active_record>`.
+      Record <./database/query_builder>`.
    -  All AR queries are backticked if appropriate to the database.
    -  Added where_in(), or_where_in(), where_not_in(),
       or_where_not_in(), not_like() and or_not_like() to :doc:`Active
-      Record <./database/active_record>`.
+      Record <./database/query_builder>`.
    -  Added support for limit() into update() and delete() statements in
-      :doc:`Active Record <./database/active_record>`.
+      :doc:`Active Record <./database/query_builder>`.
    -  Added empty_table() and truncate_table() to :doc:`Active
-      Record <./database/active_record>`.
+      Record <./database/query_builder>`.
    -  Added the ability to pass an array of tables to the delete()
-      statement in :doc:`Active Record <./database/active_record>`.
+      statement in :doc:`Active Record <./database/query_builder>`.
    -  Added count_all_results() function to :doc:`Active
-      Record <./database/active_record>`.
+      Record <./database/query_builder>`.
    -  Added select_max(), select_min(), select_avg() and
-      select_sum() to :doc:`Active Record <./database/active_record>`.
+      select_sum() to :doc:`Active Record <./database/query_builder>`.
    -  Added the ability to use aliases with joins in :doc:`Active
-      Record <./database/active_record>`.
+      Record <./database/query_builder>`.
    -  Added a third parameter to Active Record's like() clause to
       control where the wildcard goes.
    -  Added a third parameter to set() in :doc:`Active
-      Record <./database/active_record>` that withholds escaping
+      Record <./database/query_builder>` that withholds escaping
       data.
    -  Changed the behaviour of variables submitted to the where() clause
       with no values to auto set "IS NULL"
@@ -1684,7 +1785,7 @@
       the table of contents of the userguide.
    -  Moved part of the userguide menu javascript to an external file.
    -  Documented distinct() in :doc:`Active
-      Record <./database/active_record>`.
+      Record <./database/query_builder>`.
    -  Documented the timezones() function in the :doc:`Date
       Helper <./helpers/date_helper>`.
    -  Documented unset_userdata in the :doc:`Session
@@ -2260,9 +2361,9 @@
    function <./general/views>`: $this->load->view('my_view',
    $object);
 -  Added getwhere function to :doc:`Active Record
-   class <./database/active_record>`.
+   class <./database/query_builder>`.
 -  Added count_all function to :doc:`Active Record
-   class <./database/active_record>`.
+   class <./database/query_builder>`.
 -  Added language file for scaffolding and fixed a scaffolding bug that
    occurs when there are no rows in the specified table.
 -  Added :doc:`$this->db->last_query() <./database/queries>`, which
@@ -2287,7 +2388,7 @@
 -  Added support for :doc:`Models <general/models>`.
 -  Redesigned the database libraries to support additional RDBMs
    (Postgres, MySQLi, etc.).
--  Redesigned the :doc:`Active Record class <./database/active_record>`
+-  Redesigned the :doc:`Active Record class <./database/query_builder>`
    to enable more varied types of queries with simpler syntax, and
    advanced features like JOINs.
 -  Added a feature to the database class that lets you run :doc:`custom
@@ -2320,7 +2421,7 @@
    whether PHP 4 or 5 is being run, since PHP 5 allows a more graceful
    way to manage objects that utilizes a bit less resources.
 -  Deprecated: $this->db->use_table() has been deprecated. Please read
-   the :doc:`Active Record <./database/active_record>` page for
+   the :doc:`Active Record <./database/query_builder>` page for
    information.
 -  Deprecated: $this->db->smart_escape_str() has been deprecated.
    Please use this instead: $this->db->escape()
diff --git a/user_guide_src/source/contributing/index.rst b/user_guide_src/source/contributing/index.rst
new file mode 100644
index 0000000..2ede0e6
--- /dev/null
+++ b/user_guide_src/source/contributing/index.rst
@@ -0,0 +1,105 @@
+###########################
+Contributing to CodeIgniter
+###########################
+
+CodeIgniter is a community driven project and accepts contributions of code
+and documentation from the community. These contributions are made in the form
+of Issues or `Pull Requests <http://help.github.com/send-pull-requests/>`_ on
+the `EllisLab CodeIgniter repository
+<https://github.com/EllisLab/CodeIgniter>`_ on GitHub.
+
+Issues are a quick way to point out a bug. If you find a bug or documentation
+error in CodeIgniter then please check a few things first:
+
+- There is not already an open Issue
+- The issue has already been fixed (check the develop branch, or look for
+  closed Issues)
+- Is it something really obvious that you fix it yourself?
+
+Reporting issues is helpful but an even better approach is to send a Pull
+Request, which is done by "Forking" the main repository and committing to your
+own copy. This will require you to use the version control system called Git.
+
+**********
+Guidelines
+**********
+
+Before we look into how, here are the guidelines. If your Pull Requests fail
+to pass these guidelines it will be declined and you will need to re-submit
+when you’ve made the changes. This might sound a bit tough, but it is required
+for us to maintain quality of the code-base.
+
+PHP Style
+=========
+
+All code must meet the `Style Guide
+<http://codeigniter.com/user_guide/general/styleguide.html>`_, which is
+essentially the `Allman indent style
+<http://en.wikipedia.org/wiki/Indent_style#Allman_style>`_, underscores and
+readable operators. This makes certain that all code is the same format as the
+existing code and means it will be as readable as possible.
+
+Documentation
+=============
+
+If you change anything that requires a change to documentation then you will
+need to add it. New classes, methods, parameters, changing default values, etc
+are all things that will require a change to documentation. The change-log
+must also be updated for every change. Also PHPDoc blocks must be maintained.
+
+Compatibility
+=============
+
+CodeIgniter is compatible with PHP 5.2.4 so all code supplied must stick to
+this requirement. If PHP 5.3 or 5.4 functions or features are used then there
+must be a fallback for PHP 5.2.4.
+
+Branching
+=========
+
+CodeIgniter uses the `Git-Flow
+<http://nvie.com/posts/a-successful-git-branching-model/>`_ branching model
+which requires all pull requests to be sent to the "develop" branch. This is
+where the next planned version will be developed. The "master" branch will
+always contain the latest stable version and is kept clean so a "hotfix" (e.g:
+an emergency security patch) can be applied to master to create a new version,
+without worrying about other features holding it up. For this reason all
+commits need to be made to "develop" and any sent to "master" will be closed
+automatically. If you have multiple changes to submit, please place all
+changes into their own branch on your fork.
+
+One thing at a time: A pull request should only contain one change. That does
+not mean only one commit, but one change - however many commits it took. The
+reason for this is that if you change X and Y but send a pull request for both
+at the same time, we might really want X but disagree with Y, meaning we
+cannot merge the request. Using the Git-Flow branching model you can create
+new branches for both of these features and send two requests.
+
+Signing
+=======
+You must sign your work, certifying that you either wrote the work or
+otherwise have the right to pass it on to an open source project. git makes
+this trivial as you merely have to use `--signoff` on your commits to your
+CodeIgniter fork.
+
+.. code-block:: bash
+
+	git commit --signoff
+
+or simply
+
+.. code-block:: bash
+
+	git commit -s
+
+This will sign your commits with the information setup in your git config, e.g.
+
+	Signed-off-by: John Q Public <john.public@example.com>
+
+If you are using Tower there is a "Sign-Off" checkbox in the commit window. You 
+could even alias git commit to use the -s flag so you don’t have to think about 
+it.
+
+By signing your work in this manner, you certify to a "Developer's Certificate 
+or Origin". The current version of this certificate is in the :doc:`/DCO` file
+in the root of this documentation.
diff --git a/user_guide_src/source/database/configuration.rst b/user_guide_src/source/database/configuration.rst
index 7a19c84..c17de60 100644
--- a/user_guide_src/source/database/configuration.rst
+++ b/user_guide_src/source/database/configuration.rst
@@ -12,26 +12,44 @@
 The config settings are stored in a multi-dimensional array with this
 prototype::
 
-	$db['default']['hostname'] = "localhost";
-	$db['default']['username'] = "root";
-	$db['default']['password'] = "";
-	$db['default']['database'] = "database_name";
-	$db['default']['dbdriver'] = "mysql";
-	$db['default']['dbprefix'] = "";
-	$db['default']['pconnect'] = TRUE;
-	$db['default']['db_debug'] = FALSE;
-	$db['default']['cache_on'] = FALSE;
-	$db['default']['cachedir'] =  "";
-	$db['default']['char_set'] = "utf8";
-	$db['default']['dbcollat'] = "utf8_general_ci";
-	$db['default']['swap_pre'] = "";
-	$db['default']['autoinit'] = TRUE;
-	$db['default']['stricton'] = FALSE;
+	$db['default'] = array(
+		'dsn'	=> '',
+		'hostname' => 'localhost',
+		'username' => 'root',
+		'password' => '',
+		'database' => 'database_name',
+		'dbdriver' => 'mysqli',
+		'dbprefix' => '',
+		'pconnect' => TRUE,
+		'db_debug' => TRUE,
+		'cache_on' => FALSE,
+		'cachedir' => '',
+		'char_set' => 'utf8',
+		'dbcollat' => 'utf8_general_ci',
+		'swap_pre' => '',
+		'autoinit' => TRUE,
+		'stricton' => FALSE,
+		'failover' => array()
+	);
 
-If you use PDO as your dbdriver, you can specify the full DSN string describe a connection to the database like this::
+Some database drivers (such as PDO, PostgreSQL, Oracle, ODBC) might
+require a full DSN string to be provided. If that is the case, you
+should use the 'dsn' configuration setting, as if you're using the
+driver's underlying native PHP extension, like this::
 
+	// PDO
 	$db['default']['dsn'] = 'pgsql:host=localhost;port=5432;dbname=database_name';
 
+	// Oracle
+	$db['default']['dsn'] = '//localhost/XE';
+
+.. note:: If you do not specify a DSN string for a driver that requires it, CodeIgniter
+	will try to build it with the rest of the provided settings.
+
+.. note:: If you provide a DSN string and it is missing some valid settings (e.g. the
+	database character set), which are present in the rest of the configuration
+	fields, CodeIgniter will append them.
+
 You can also specify failovers for the situation when the main connection cannot connect for some reason.
 These failovers can be specified by setting the failover for a connection like this::
 
@@ -41,7 +59,7 @@
 				'username' => '',
 				'password' => '',
 				'database' => '',
-				'dbdriver' => 'mysql',
+				'dbdriver' => 'mysqli',
 				'dbprefix' => '',
 				'pconnect' => TRUE,
 				'db_debug' => TRUE,
@@ -58,7 +76,7 @@
 				'username' => '',
 				'password' => '',
 				'database' => '',
-				'dbdriver' => 'mysql',
+				'dbdriver' => 'mysqli',
 				'dbprefix' => '',
 				'pconnect' => TRUE,
 				'db_debug' => TRUE,
@@ -81,30 +99,34 @@
 connection group for each, then switch between groups as needed. For
 example, to set up a "test" environment you would do this::
 
-	$db['test']['hostname'] = "localhost";
-	$db['test']['username'] = "root";
-	$db['test']['password'] = "";
-	$db['test']['database'] = "database_name";
-	$db['test']['dbdriver'] = "mysql";
-	$db['test']['dbprefix'] = "";
-	$db['test']['pconnect'] = TRUE;
-	$db['test']['db_debug'] = FALSE;
-	$db['test']['cache_on'] = FALSE;
-	$db['test']['cachedir'] =  "";
-	$db['test']['char_set'] = "utf8";
-	$db['test']['dbcollat'] = "utf8_general_ci";
-	$db['test']['swap_pre'] = "";
-	$db['test']['autoinit'] = TRUE;
-	$db['test']['stricton'] = FALSE;
+	$db['test'] = array(
+		'dsn'	=> '',
+		'hostname' => 'localhost',
+		'username' => 'root',
+		'password' => '',
+		'database' => 'database_name',
+		'dbdriver' => 'mysqli',
+		'dbprefix' => '',
+		'pconnect' => TRUE,
+		'db_debug' => TRUE,
+		'cache_on' => FALSE,
+		'cachedir' => '',
+		'char_set' => 'utf8',
+		'dbcollat' => 'utf8_general_ci',
+		'swap_pre' => '',
+		'autoinit' => TRUE,
+		'stricton' => FALSE,
+		'failover' => array()
+	);
 
 Then, to globally tell the system to use that group you would set this
 variable located in the config file::
 
-	$active_group = "test";
+	$active_group = 'test';
 
-Note: The name "test" is arbitrary. It can be anything you want. By
-default we've used the word "default" for the primary connection, but it
-too can be renamed to something more relevant to your project.
+.. note:: The name 'test' is arbitrary. It can be anything you want. By
+	default we've used the word "default" for the primary connection,
+	but it too can be renamed to something more relevant to your project.
 
 Query Builder
 -------------
@@ -119,8 +141,8 @@
 
 	$query_builder = TRUE;
 
-.. note:: that some CodeIgniter classes such as Sessions require Active
-	Records be enabled to access certain functionality.
+.. note:: that some CodeIgniter classes such as Sessions require Query
+	Builder to be enabled to access certain functionality.
 
 Explanation of Values:
 ----------------------
@@ -128,11 +150,12 @@
 ======================  ==================================================================================================
  Name Config             Description
 ======================  ==================================================================================================
-**hostname** 		The hostname of your database server. Often this is "localhost".
+**dsn**			The DSN connect string (an all-in-one configuration sequence).
+**hostname** 		The hostname of your database server. Often this is 'localhost'.
 **username**		The username used to connect to the database.
 **password**		The password used to connect to the database.
 **database**		The name of the database you want to connect to.
-**dbdriver**		The database type. ie: mysql, postgre, odbc, etc. Must be specified in lower case.
+**dbdriver**		The database type. ie: mysqli, postgre, odbc, etc. Must be specified in lower case.
 **dbprefix**		An optional table prefix which will added to the table name when running :doc:
 			`Query Builder <query_builder>` queries. This permits multiple CodeIgniter installations
 			to share one database.
@@ -144,14 +167,7 @@
 **char_set**		The character set used in communicating with the database.
 **dbcollat**		The character collation used in communicating with the database
 
-			.. note:: For MySQL and MySQLi databases, this setting is only used
-				as a backup if your server is running PHP < 5.2.3 or MySQL < 5.0.7
-				(and in table creation queries made with DB Forge). There is an
-				incompatibility in PHP with mysql_real_escape_string() which can
-				make your site vulnerable to SQL injection if you are using a
-				multi-byte character set and are running versions lower than these.
-				Sites using Latin-1 or UTF-8 database character set and collation are
-				unaffected.
+			.. note:: Only used in the 'mysql' and 'mysqli' drivers.
 
 **swap_pre**		A default table prefix that should be swapped with dbprefix. This is useful for distributed
 			applications where you might run manually written queries, and need the prefix to still be
@@ -163,11 +179,11 @@
 **port**		The database port number. To use this value you have to add a line to the database config array.
 			::
 			
-				$db['default']['port'] =  5432;
+				$db['default']['port'] = 5432;
 ======================  ==================================================================================================
 
 .. note:: Depending on what database platform you are using (MySQL, PostgreSQL,
 	etc.) not all values will be needed. For example, when using SQLite you
 	will not need to supply a username or password, and the database name
 	will be the path to your database file. The information above assumes
-	you are using MySQL.
+	you are using MySQL.
\ No newline at end of file
diff --git a/user_guide_src/source/database/connecting.rst b/user_guide_src/source/database/connecting.rst
index 5822ca6..9b81170 100644
--- a/user_guide_src/source/database/connecting.rst
+++ b/user_guide_src/source/database/connecting.rst
@@ -57,25 +57,28 @@
 To connect manually to a desired database you can pass an array of
 values::
 
-	$config['hostname'] = "localhost"; 
-	$config['username'] = "myusername"; 
-	$config['password'] = "mypassword"; 
-	$config['database'] = "mydatabase"; 
-	$config['dbdriver'] = "mysql"; 
-	$config['dbprefix'] = ""; 
-	$config['pconnect'] = FALSE; 
-	$config['db_debug'] = TRUE; 
-	$config['cache_on'] = FALSE; 
-	$config['cachedir'] = ""; 
-	$config['char_set'] = "utf8"; 
-	$config['dbcollat'] = "utf8_general_ci";  
+	$config['hostname'] = 'localhost';
+	$config['username'] = 'myusername';
+	$config['password'] = 'mypassword';
+	$config['database'] = 'mydatabase';
+	$config['dbdriver'] = 'mysqli';
+	$config['dbprefix'] = '';
+	$config['pconnect'] = FALSE;
+	$config['db_debug'] = TRUE;
+	$config['cache_on'] = FALSE;
+	$config['cachedir'] = '';
+	$config['char_set'] = 'utf8';
+	$config['dbcollat'] = 'utf8_general_ci';
 	$this->load->database($config);
 
 For information on each of these values please see the :doc:`configuration
 page <configuration>`.
 
-.. note:: For the PDO driver, $config['hostname'] should look like
-	this: 'mysql:host=localhost'
+.. note:: For the PDO driver, you should use the $config['dsn'] setting
+	instead of 'hostname' and 'database':
+
+	|
+	| $config['dsn'] = 'mysql:host=localhost;dbname=mydatabase';
 
 Or you can submit your database values as a Data Source Name. DSNs must
 have this prototype::
@@ -149,5 +152,4 @@
 
 ::
 
-	$this->db->close();
-
+	$this->db->close();
\ No newline at end of file
diff --git a/user_guide_src/source/database/queries.rst b/user_guide_src/source/database/queries.rst
index d23efec..11dd783 100644
--- a/user_guide_src/source/database/queries.rst
+++ b/user_guide_src/source/database/queries.rst
@@ -21,11 +21,31 @@
 $this->db->simple_query();
 ===========================
 
-This is a simplified version of the $this->db->query() function. It ONLY
-returns TRUE/FALSE on success or failure. It DOES NOT return a database
-result set, nor does it set the query timer, or compile bind data, or
-store your query for debugging. It simply lets you submit a query. Most
-users will rarely use this function.
+This is a simplified version of the $this->db->query() method. It DOES
+NOT return a database result set, nor does it set the query timer, or
+compile bind data, or store your query for debugging. It simply lets you
+submit a query. Most users will rarely use this function.
+
+It returns whatever the database drivers' "execute" function returns.
+That typically is TRUE/FALSE on success or failure for write type queries
+such as INSERT, DELETE or UPDATE statements (which is what it really
+should be used for) and a resource/object on success for queries with
+fetchable results.
+
+::
+
+	if ($this->db->simple_query('YOUR QUERY'))
+	{
+		echo "Success!";
+	}
+	else
+	{
+		echo "Query failed!";
+	}
+
+.. note:: PostgreSQL's pg_exec() function always returns a resource on
+	success, even for write type queries. So take that in mind if
+	you're looking for a boolean value.
 
 ***************************************
 Working with Database prefixes manually
diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst
index 54e8df6..b86a0c8 100644
--- a/user_guide_src/source/database/query_builder.rst
+++ b/user_guide_src/source/database/query_builder.rst
@@ -603,9 +603,9 @@
 
 	/*
 	class Myclass {
-		var  $title = 'My Title';
-		var  $content = 'My Content';
-		var  $date = 'My Date';
+		public $title = 'My Title';
+		public $content = 'My Content';
+		public $date = 'My Date';
 	}
 	*/
 
@@ -730,9 +730,9 @@
 
 	/*
 	class Myclass {
-		var  $title = 'My Title';
-		var  $content = 'My Content';
-		var  $date = 'My Date';
+		public $title = 'My Title';
+		public $content = 'My Content';
+		public $date = 'My Date';
 	}
 	*/
 
@@ -766,9 +766,9 @@
 
 	/*
 	class Myclass {
-		var  $title = 'My Title';
-		var  $content = 'My Content';
-		var  $date = 'My Date';
+		public $title = 'My Title';
+		public $content = 'My Content';
+		public $date = 'My Date';
 	}
 	*/
 
diff --git a/user_guide_src/source/database/results.rst b/user_guide_src/source/database/results.rst
index ac4fc37..d032f73 100644
--- a/user_guide_src/source/database/results.rst
+++ b/user_guide_src/source/database/results.rst
@@ -139,13 +139,13 @@
 .. note:: all the functions above will load the whole result into memory (prefetching) use unbuffered_row() for processing large result sets.
 
 unbuffered_row($type)
-=====
+=====================
 
 This function returns a single result row without prefetching the whole result in memory as row() does.
 If your query has more than one row, it returns the current row and moves the internal data pointer ahead. 
 The result is returned as $type could be 'object' (default) or 'array' that will return an associative array.
 
-
+::
 
 	$query = $this->db->query("YOUR QUERY");
 	
diff --git a/user_guide_src/source/general/common_functions.rst b/user_guide_src/source/general/common_functions.rst
index 70563b8..99126f9 100644
--- a/user_guide_src/source/general/common_functions.rst
+++ b/user_guide_src/source/general/common_functions.rst
@@ -79,3 +79,8 @@
 This function provides short cut for htmlspecialchars() function. It
 accepts string and array. To prevent Cross Site Scripting (XSS), it is
 very useful.
+
+get_mimes()
+=============
+
+This function returns the MIMEs array from config/mimes.php.
\ No newline at end of file
diff --git a/user_guide_src/source/general/core_classes.rst b/user_guide_src/source/general/core_classes.rst
index ac41407..4aa6693 100644
--- a/user_guide_src/source/general/core_classes.rst
+++ b/user_guide_src/source/general/core_classes.rst
@@ -31,6 +31,7 @@
 -  Log
 -  Output
 -  Router
+-  Security
 -  URI
 -  Utf8
 
diff --git a/user_guide_src/source/general/environments.rst b/user_guide_src/source/general/environments.rst
index 40725fe..fa1b096 100644
--- a/user_guide_src/source/general/environments.rst
+++ b/user_guide_src/source/general/environments.rst
@@ -11,10 +11,16 @@
 The ENVIRONMENT Constant
 ========================
 
-By default, CodeIgniter comes with the environment constant set to
+By default, CodeIgniter comes with the environment constant set to use 
+the value provided in ``$_SERVER['CI_ENV']``, otherwise defaults to 
 'development'. At the top of index.php, you will see::
 
-	 define('ENVIRONMENT', 'development');
+	define('ENVIRONMENT', isset($_SERVER['CI_ENV']) ? $_SERVER['CI_ENV'] : 'development');
+
+This server variable can be set in your .htaccess file, or Apache 
+config using `SetEnv <https://httpd.apache.org/docs/2.2/mod/mod_env.html#setenv>`_. 
+Alternative methods are available for nginx and other servers, or you can 
+remove this logic entirely and set the constant based on the HTTP_HOST or IP.
 
 In addition to affecting some basic framework behavior (see the next
 section), you may use this constant in your own development to
diff --git a/user_guide_src/source/general/models.rst b/user_guide_src/source/general/models.rst
index 0156b04..4e52a96 100644
--- a/user_guide_src/source/general/models.rst
+++ b/user_guide_src/source/general/models.rst
@@ -16,11 +16,11 @@
 update, and retrieve your blog data. Here is an example of what such a
 model class might look like::
 
-	class Blogmodel extends CI_Model {
+	class Blog_model extends CI_Model {
 
-	    var $title   = '';
-	    var $content = '';
-	    var $date    = '';
+	    public $title   = '';
+	    public $content = '';
+	    public $date    = '';
 
 	    function __construct()
 	    {
@@ -72,10 +72,11 @@
 
 	class Model_name extends CI_Model {
 
-	    function __construct()
-	    {
-	        parent::__construct();
-	    }
+		public function __construct()
+		{
+			parent::__construct();
+		}
+
 	}
 
 Where **Model_name** is the name of your class. Class names **must** have
@@ -87,10 +88,11 @@
 
 	class User_model extends CI_Model {
 
-	    function __construct()
-	    {
-	        parent::__construct();
-	    }
+		public function __construct()
+		{
+			parent::__construct();
+		}
+
 	}
 
 Your file will be this::
@@ -102,9 +104,9 @@
 
 Your models will typically be loaded and called from within your
 :doc:`controller <controllers>` functions. To load a model you will use
-the following function::
+the following method::
 
-	$this->load->model('Model_name');
+	$this->load->model('model_name');
 
 If your model is located in a sub-folder, include the relative path from
 your models folder. For example, if you have a model located at
@@ -112,33 +114,34 @@
 
 	$this->load->model('blog/queries');
 
-Once loaded, you will access your model functions using an object with
-the same name as your class::
+Once loaded, you will access your model methods using an object with the
+same name as your class::
 
-	$this->load->model('Model_name');
+	$this->load->model('model_name');
 
-	$this->Model_name->function();
+	$this->model_name->method();
 
 If you would like your model assigned to a different object name you can
-specify it via the second parameter of the loading function::
+specify it via the second parameter of the loading method::
 
-	$this->load->model('Model_name', 'fubar');
+	$this->load->model('model_name', 'foobar');
 
-	$this->fubar->function();
+	$this->foobar->method();
 
 Here is an example of a controller, that loads a model, then serves a
 view::
 
 	class Blog_controller extends CI_Controller {
 
-	    function blog()
-	    {
-	        $this->load->model('Blog');
+		public function blog()
+		{
+			$this->load->model('blog');
 
-	        $data['query'] = $this->Blog->get_last_ten_entries();
+			$data['query'] = $this->Blog->get_last_ten_entries();
 
-	        $this->load->view('blog', $data);
-	    }
+			$this->load->view('blog', $data);
+		}
+
 	}
 	
 
@@ -165,20 +168,18 @@
    defined in your database config file will be used:
    ::
 
-	$this->load->model('Model_name', '', TRUE);
+	$this->load->model('model_name', '', TRUE);
 
 -  You can manually pass database connectivity settings via the third
    parameter::
 
-	$config['hostname'] = "localhost";
-	$config['username'] = "myusername";
-	$config['password'] = "mypassword";
-	$config['database'] = "mydatabase";
-	$config['dbdriver'] = "mysql";
-	$config['dbprefix'] = "";
+	$config['hostname'] = 'localhost';
+	$config['username'] = 'myusername';
+	$config['password'] = 'mypassword';
+	$config['database'] = 'mydatabase';
+	$config['dbdriver'] = 'mysqli';
+	$config['dbprefix'] = '';
 	$config['pconnect'] = FALSE;
 	$config['db_debug'] = TRUE;
 
-	$this->load->model('Model_name', '', $config);
-
-
+	$this->load->model('Model_name', '', $config);
\ No newline at end of file
diff --git a/user_guide_src/source/general/requirements.rst b/user_guide_src/source/general/requirements.rst
index d97b7b4..d9edfba 100644
--- a/user_guide_src/source/general/requirements.rst
+++ b/user_guide_src/source/general/requirements.rst
@@ -4,5 +4,6 @@
 
 -  `PHP <http://www.php.net/>`_ version 5.2.4 or newer.
 -  A Database is required for most web application programming. Current
-   supported databases are MySQL (5.1+), MySQLi, MS SQL, SQLSRV, Oracle,
-   PostgreSQL, SQLite, SQLite3, CUBRID, Interbase, ODBC and PDO.
+   supported databases are MySQL (5.1+), MySQLi, Oracle, PostgreSQL,
+   MS SQL, SQLSRV (SQL Server 2005+), SQLite, SQLite3, CUBRID, Interbase,
+   ODBC and PDO.
diff --git a/user_guide_src/source/general/welcome.rst b/user_guide_src/source/general/welcome.rst
new file mode 100644
index 0000000..b28c3bc
--- /dev/null
+++ b/user_guide_src/source/general/welcome.rst
@@ -0,0 +1,32 @@
+######################
+Welcome to CodeIgniter
+######################
+
+CodeIgniter is an Application Development Framework - a toolkit - for
+people who build web sites using PHP. Its goal is to enable you to
+develop projects much faster than you could if you were writing code
+from scratch, by providing a rich set of libraries for commonly needed
+tasks, as well as a simple interface and logical structure to access
+these libraries. CodeIgniter lets you creatively focus on your project
+by minimizing the amount of code needed for a given task.
+
+***********************
+Who is CodeIgniter For?
+***********************
+
+CodeIgniter is right for you if:
+
+-  You want a framework with a small footprint.
+-  You need exceptional performance.
+-  You need broad compatibility with standard hosting accounts that run
+   a variety of PHP versions and configurations.
+-  You want a framework that requires nearly zero configuration.
+-  You want a framework that does not require you to use the command
+   line.
+-  You want a framework that does not require you to adhere to
+   restrictive coding rules.
+-  You are not interested in large-scale monolithic libraries like PEAR.
+-  You do not want to be forced to learn a templating language (although
+   a template parser is optionally available if you desire one).
+-  You eschew complexity, favoring simple solutions.
+-  You need clear, thorough documentation.
diff --git a/user_guide_src/source/helpers/array_helper.rst b/user_guide_src/source/helpers/array_helper.rst
index 4308753..15b5e17 100644
--- a/user_guide_src/source/helpers/array_helper.rst
+++ b/user_guide_src/source/helpers/array_helper.rst
@@ -21,17 +21,17 @@
 element()
 =========
 
-.. php:method:: element($item, $array, $default = FALSE)
+.. php:method:: element($item, $array, $default = NULL)
 
 	:param string 	$item: Item to fetch from the array
 	:param array 	$array: Input array
 	:param boolean	$default: What to return if the array isn't valid
-	:returns: FALSE on failure or the array item.
+	:returns: NULL on failure or the array item.
 
 
 Lets you fetch an item from an array. The function tests whether the
 array index is set and whether it has a value. If a value exists it is
-returned. If a value does not exist it returns FALSE, or whatever you've
+returned. If a value does not exist it returns NULL, or whatever you've
 specified as the default value via the third parameter. Example
 
 ::
@@ -43,31 +43,31 @@
 	);
 
 	echo element('color', $array); // returns "red" 
-	echo element('size', $array, NULL); // returns NULL 
+	echo element('size', $array, 'foobar'); // returns "foobar" 
 
 elements()
 ==========
 
 Lets you fetch a number of items from an array. The function tests
 whether each of the array indices is set. If an index does not exist it
-is set to FALSE, or whatever you've specified as the default value via
+is set to NULL, or whatever you've specified as the default value via
 the third parameter. 
 
-.. php:method:: elements($items, $array, $default = FALSE)
+.. php:method:: elements($items, $array, $default = NULL)
 
 	:param string 	$item: Item to fetch from the array
 	:param array 	$array: Input array
 	:param boolean	$default: What to return if the array isn't valid
-	:returns: FALSE on failure or the array item.
+	:returns: NULL on failure or the array item.
 
 Example
 
 ::
 
 	$array = array(
-		'color' => 'red',  
-		'shape' => 'round',     
-		'radius' => '10',     
+		'color' => 'red',
+		'shape' => 'round',
+		'radius' => '10',
 		'diameter' => '20'
 	);
 
@@ -78,25 +78,25 @@
 ::
 
 	array(
-		'color' => 'red',     
-		'shape' => 'round',     
-		'height' => FALSE
+		'color' => 'red',
+		'shape' => 'round',
+		'height' => NULL
 	);
 
 You can set the third parameter to any default value you like
 
 ::
 
-	 $my_shape = elements(array('color', 'shape', 'height'), $array, NULL);
+	 $my_shape = elements(array('color', 'shape', 'height'), $array, 'foobar');
 
 The above will return the following array
 
 ::
 
 	array(     
-		'color' 	=> 'red',     
-		'shape' 	=> 'round',     
-		'height'	=> NULL
+		'color' 	=> 'red',
+		'shape' 	=> 'round',
+		'height'	=> 'foobar'
 	);
 
 This is useful when sending the $_POST array to one of your Models.
diff --git a/user_guide_src/source/helpers/date_helper.rst b/user_guide_src/source/helpers/date_helper.rst
index 18216c5..e332a91 100644
--- a/user_guide_src/source/helpers/date_helper.rst
+++ b/user_guide_src/source/helpers/date_helper.rst
@@ -21,18 +21,26 @@
 =====
 
 Returns the current time as a Unix timestamp, referenced either to your
-server's local time or GMT, based on the "time reference" setting in
-your config file. If you do not intend to set your master time reference
-to GMT (which you'll typically do if you run a site that lets each user
-set their own timezone settings) there is no benefit to using this
+server's local time or any PHP suported timezone, based on the "time reference"
+setting in your config file. If you do not intend to set your master time reference
+to any other PHP suported timezone (which you'll typically do if you run a site that
+lets each user set their own timezone settings) there is no benefit to using this
 function over PHP's time() function.
 
-.. php:method:: now()
+.. php:method:: now($timezone = NULL)
+
+	:param string 	$timezone: The timezone you want to be returned
+	:returns: integer
+
+::
+	echo now("Australia/Victoria");
+
+If a timezone is not provided, it will return time() based on "time_reference" setting.
 
 mdate()
 =======
 
-This function is identical to PHPs `date() <http://www.php.net/date>`_
+This function is identical to PHP's `date() <http://www.php.net/date>`_
 function, except that it lets you use MySQL style date codes, where each
 code letter is preceded with a percent sign: %Y %m %d etc.
 
@@ -77,37 +85,34 @@
 The first parameter must contain the format, the second parameter must
 contain the date as a Unix timestamp.
 
+.. note:: This function is DEPRECATED. Use the native ``date()`` combined
+	with `DateTime's format constants <http://www.php.net/manual/en/class.datetime.php#datetime.constants.types>`_
+	instead:
+
+	|
+	| echo date(DATE_RFC822, time());
+
 Supported formats:
 
-+----------------+------------------------+-----------------------------------+
-| Constant       | Description            | Example                           |
-+================+========================+===================================+
-| DATE_ATOM      | Atom                   | 2005-08-15T16:13:03+0000          |
-+----------------+------------------------+-----------------------------------+
-| DATE_COOKIE    | HTTP Cookies           | Sun, 14 Aug 2005 16:13:03 UTC     |
-+----------------+------------------------+-----------------------------------+
-| DATE_ISO8601   | ISO-8601               | 2005-08-14T16:13:03+00:00         |
-+----------------+------------------------+-----------------------------------+
-| DATE_RFC822    | RFC 822                | Sun, 14 Aug 05 16:13:03 UTC       |
-+----------------+------------------------+-----------------------------------+
-| DATE_RFC850    | RFC 850                | Sunday, 14-Aug-05 16:13:03 UTC    |
-+----------------+------------------------+-----------------------------------+
-| DATE_RFC1036   | RFC 1036               | Sunday, 14-Aug-05 16:13:03 UTC    |
-+----------------+------------------------+-----------------------------------+
-| DATE_RFC1123   | RFC 1123               | Sun, 14 Aug 2005 16:13:03 UTC     |
-+----------------+------------------------+-----------------------------------+
-| DATE_RFC2822   | RFC 2822               | Sun, 14 Aug 2005 16:13:03 +0000   |
-+----------------+------------------------+-----------------------------------+
-| DATE_RSS       | RSS                    | Sun, 14 Aug 2005 16:13:03 UTC     |
-+----------------+------------------------+-----------------------------------+
-| DATE_W3C       | W3C                    | 2005-08-14T16:13:03+0000          |
-+----------------+------------------------+-----------------------------------+
-
+===============	=======================	======================================
+Constant		Description				Example
+===============	=======================	======================================
+DATE_ATOM	Atom			2005-08-15T16:13:03+0000
+DATE_COOKIE	HTTP Cookies		Sun, 14 Aug 2005 16:13:03 UTC
+DATE_ISO8601   	ISO-8601		2005-08-14T16:13:03+00:00
+DATE_RFC822	RFC 822			Sun, 14 Aug 05 16:13:03 UTC
+DATE_RFC850	RFC 850			Sunday, 14-Aug-05 16:13:03 UTC
+DATE_RFC1036	RFC 1036		Sunday, 14-Aug-05 16:13:03 UTC
+DATE_RFC1123	RFC 1123		Sun, 14 Aug 2005 16:13:03 UTC
+DATE_RFC2822 	RFC 2822		Sun, 14 Aug 2005 16:13:03 +0000
+DATE_RSS	RSS			Sun, 14 Aug 2005 16:13:03 UTC
+DATE_W3C	W3C			2005-08-14T16:13:03+0000
+===============	=======================	======================================
 
 local_to_gmt()
 ==============
 
-Takes a Unix timestamp as input and returns it as GMT. 
+Takes a Unix timestamp as input and returns it as GMT.
 
 .. php:method:: local_to_gmt($time = '')
 
@@ -151,7 +156,7 @@
 mysql_to_unix()
 ===============
 
-Takes a MySQL Timestamp as input and returns it as Unix. 
+Takes a MySQL Timestamp as input and returns it as Unix.
 
 .. php:method:: mysql_to_unix($time = '')
 
@@ -204,7 +209,7 @@
 The opposite of the above function. Takes a "human" time as input and
 returns it as Unix. This function is useful if you accept "human"
 formatted dates submitted via a form. Returns FALSE (boolean) if the
-date string passed to it is not formatted as indicated above. 
+date string passed to it is not formatted as indicated above.
 
 .. php:method:: human_to_unix($datestr = '')
 
@@ -227,9 +232,9 @@
 
 The function will return a Unix timestamp by default. You can,
 optionally, pass a format string (the same type as the PHP date function
-accepts) as the second parameter. 
+accepts) as the second parameter.
 
-.. php:method:: nice_date($bad_date = '', $format = FALSE) 
+.. php:method:: nice_date($bad_date = '', $format = FALSE)
 
 	:param integer 	$bad_date: The terribly formatted date-like string
 	:param string 	$format: Date format to return (same as php date function)
@@ -239,26 +244,28 @@
 
 ::
 
-	$bad_time = 199605  // Should Produce: 1996-05-01
-	$better_time = nice_date($bad_time,'Y-m-d');
-	$bad_time = 9-11-2001 // Should Produce: 2001-09-11
-	$better_time = nice_date($human,'Y-m-d');
+	$bad_date = '199605';
+	// Should Produce: 1996-05-01
+	$better_date = nice_date($bad_date, 'Y-m-d');
+
+	$bad_date = '9-11-2001';
+	// Should Produce: 2001-09-11
+	$better_date = nice_date($bad_date, 'Y-m-d');
 
 timespan()
 ==========
 
 Formats a unix timestamp so that is appears similar to this
-
 ::
 
 	1 Year, 10 Months, 2 Weeks, 5 Days, 10 Hours, 16 Minutes
 
 The first parameter must contain a Unix timestamp. The second parameter
 must contain a timestamp that is greater that the first timestamp. If
-the second parameter empty, the current time will be used. The third 
-parameter is optional and limits the number of time units to display. 
-The most common purpose for this function is to show how much time has 
-elapsed from some point in time in the past to now. 
+the second parameter empty, the current time will be used. The third
+parameter is optional and limits the number of time units to display.
+The most common purpose for this function is to show how much time has
+elapsed from some point in time in the past to now.
 
 .. php:method:: timespan($seconds = 1, $time = '', $units = '')
 
@@ -283,7 +290,7 @@
 ===============
 
 Returns the number of days in a given month/year. Takes leap years into
-account. 
+account.
 
 .. php:method:: days_in_month($month = 0, $year = '')
 
@@ -380,14 +387,15 @@
 The first parameter lets you set the "selected" state of the menu. For
 example, to set Pacific time as the default you will do this
 
-.. php:method:: timezone_menu($default = 'UTC', $class = "", $name = 'timezones')
+.. php:method:: timezone_menu($default = 'UTC', $class = '', $name = 'timezones', $attributes = '')
 
 	:param string 	$default: timezone
 	:param string	$class: classname
 	:param string	$name: menu name
+	:param mixed	$attributes: attributes
 	:returns: string
 
-Example: 
+Example:
 
 ::
 
@@ -397,6 +405,8 @@
 
 The second parameter lets you set a CSS class name for the menu.
 
+The fourth parameter lets you set one or more attributes on the generated select tag.
+
 .. note:: The text contained in the menu is found in the following
 	language file: `language/<your_lang>/date_lang.php`
 
@@ -408,86 +418,47 @@
 
 Note some of the location lists have been abridged for clarity and formatting.
 
-+------------+----------------------------------------------------------------+
-| Time Zone  | Location                                                       |
-+============+================================================================+
-| UM12       | (UTC - 12:00) Baker/Howland Island	                          |
-+------------+----------------------------------------------------------------+
-| UM11       | (UTC - 11:00) Samoa Time Zone, Niue						      |
-+------------+----------------------------------------------------------------+
-| UM10       | (UTC - 10:00) Hawaii-Aleutian Standard Time, Cook Islands	  |
-+------------+----------------------------------------------------------------+
-| UM95       | (UTC - 09:30) Marquesas Islands							      |
-+------------+----------------------------------------------------------------+
-| UM9        | (UTC - 09:00) Alaska Standard Time, Gambier Islands		      |
-+------------+----------------------------------------------------------------+
-| UM8        | (UTC - 08:00) Pacific Standard Time, Clipperton Island	      |
-+------------+----------------------------------------------------------------+
-| UM7        | (UTC - 11:00) Mountain Standard Time						      |
-+------------+----------------------------------------------------------------+
-| UM6        | (UTC - 06:00) Central Standard Time						      |
-+------------+----------------------------------------------------------------+
-| UM5        | (UTC - 05:00) Eastern Standard Time, Western Caribbean		  |
-+------------+----------------------------------------------------------------+
-| UM45       | (UTC - 04:30) Venezuelan Standard Time					      |
-+------------+----------------------------------------------------------------+
-| UM4        | (UTC - 04:00) Atlantic Standard Time, Eastern Caribbean		  |
-+------------+----------------------------------------------------------------+
-| UM35       | (UTC - 03:30) Newfoundland Standard Time					      |
-+------------+----------------------------------------------------------------+
-| UM3        | (UTC - 03:00) Argentina, Brazil, French Guiana, Uruguay	      |
-+------------+----------------------------------------------------------------+
-| UM2        | (UTC - 02:00) South Georgia/South Sandwich Islands		      |
-+------------+----------------------------------------------------------------+
-| UM1        | (UTC -1:00) Azores, Cape Verde Islands						  |
-+------------+----------------------------------------------------------------+
-| UTC        | (UTC) Greenwich Mean Time, Western European Time				  |
-+------------+----------------------------------------------------------------+
-| UP1        | (UTC +1:00) Central European Time, West Africa Time			  |
-+------------+----------------------------------------------------------------+
-| UP2        | (UTC +2:00) Central Africa Time, Eastern European Time		  |
-+------------+----------------------------------------------------------------+
-| UP3        | (UTC +3:00) Moscow Time, East Africa Time			  		  |
-+------------+----------------------------------------------------------------+
-| UP35       | (UTC +3:30) Iran Standard Time								  |
-+------------+----------------------------------------------------------------+
-| UP4        | (UTC +4:00) Azerbaijan Standard Time, Samara Time			  |
-+------------+----------------------------------------------------------------+
-| UP45       | (UTC +4:30) Afghanistan										  |
-+------------+----------------------------------------------------------------+
-| UP5        | (UTC +5:00) Pakistan Standard Time, Yekaterinburg Time		  |
-+------------+----------------------------------------------------------------+
-| UP55       | (UTC +5:30) Indian Standard Time, Sri Lanka Time				  |
-+------------+----------------------------------------------------------------+
-| UP575      | (UTC +5:45) Nepal Time										  |
-+------------+----------------------------------------------------------------+
-| UP6        | (UTC +6:00) Bangladesh Standard Time, Bhutan Time, Omsk Time   |
-+------------+----------------------------------------------------------------+
-| UP65       | (UTC +6:30) Cocos Islands, Myanmar							  |
-+------------+----------------------------------------------------------------+
-| UP7        | (UTC +7:00) Krasnoyarsk Time, Cambodia, Laos, Thailand, Vietnam|
-+------------+----------------------------------------------------------------+
-| UP8        | (UTC +8:00) Australian Western Standard Time, Beijing Time	  |
-+------------+----------------------------------------------------------------+
-| UP875      | (UTC +8:45) Australian Central Western Standard Time		      |
-+------------+----------------------------------------------------------------+
-| UP9        | (UTC +9:00) Japan Standard Time, Korea Standard Time, Yakutsk  |
-+------------+----------------------------------------------------------------+
-| UP95       | (UTC +9:30) Australian Central Standard Time					  |
-+------------+----------------------------------------------------------------+
-| UP10       | (UTC +10:00) Australian Eastern Standard Time, Vladivostok Time|
-+------------+----------------------------------------------------------------+
-| UP105      | (UTC +10:30) Lord Howe Island								  |
-+------------+----------------------------------------------------------------+
-| UP11       | (UTC +11:00) Magadan Time, Solomon Islands, Vanuatu            |
-+------------+----------------------------------------------------------------+
-| UP115      | (UTC +11:30) Norfolk Island									  |
-+------------+----------------------------------------------------------------+
-| UP12       | (UTC +12:00) Fiji, Gilbert Islands, Kamchatka, New Zealand     |
-+------------+----------------------------------------------------------------+
-| UP1275     | (UTC +12:45) Chatham Islands Standard Time					  |
-+------------+----------------------------------------------------------------+
-| UP13       | (UTC +13:00) Phoenix Islands Time, Tonga						  |
-+------------+----------------------------------------------------------------+
-| UP14       | (UTC +14:00) Line Islands									  |
-+------------+----------------------------------------------------------------+
+===========	=====================================================================
+Time Zone	Location
+===========	=====================================================================
+UM2			(UTC - 12:00) Baker/Howland Island
+UM1			(UTC - 11:00) Samoa Time Zone, Niue
+UM0			(UTC - 10:00) Hawaii-Aleutian Standard Time, Cook Islands
+UM95		(UTC - 09:30) Marquesas Islands
+UM9			(UTC - 09:00) Alaska Standard Time, Gambier Islands
+UM8			(UTC - 08:00) Pacific Standard Time, Clipperton Island
+UM7			(UTC - 11:00) Mountain Standard Time
+UM6			(UTC - 06:00) Central Standard Time
+UM5			(UTC - 05:00) Eastern Standard Time, Western Caribbean
+UM45		(UTC - 04:30) Venezuelan Standard Time
+UM4			(UTC - 04:00) Atlantic Standard Time, Eastern Caribbean
+UM35		(UTC - 03:30) Newfoundland Standard Time
+UM3			(UTC - 03:00) Argentina, Brazil, French Guiana, Uruguay
+UM2			(UTC - 02:00) South Georgia/South Sandwich Islands
+UM			(UTC -1:00) Azores, Cape Verde Islands
+UTC			(UTC) Greenwich Mean Time, Western European Time
+UP1			(UTC +1:00) Central European Time, West Africa Time
+UP2			(UTC +2:00) Central Africa Time, Eastern European Time
+UP3			(UTC +3:00) Moscow Time, East Africa Time
+UP35		(UTC +3:30) Iran Standard Time
+UP4			(UTC +4:00) Azerbaijan Standard Time, Samara Time
+UP45		(UTC +4:30) Afghanistan
+UP5			(UTC +5:00) Pakistan Standard Time, Yekaterinburg Time
+UP55		(UTC +5:30) Indian Standard Time, Sri Lanka Time
+UP575		(UTC +5:45) Nepal Time
+UP6			(UTC +6:00) Bangladesh Standard Time, Bhutan Time, Omsk Time
+UP65		(UTC +6:30) Cocos Islands, Myanmar
+UP7			(UTC +7:00) Krasnoyarsk Time, Cambodia, Laos, Thailand, Vietnam
+UP8			(UTC +8:00) Australian Western Standard Time, Beijing Time
+UP875		(UTC +8:45) Australian Central Western Standard Time
+UP9			(UTC +9:00) Japan Standard Time, Korea Standard Time, Yakutsk
+UP95		(UTC +9:30) Australian Central Standard Time
+UP10		(UTC +10:00) Australian Eastern Standard Time, Vladivostok Time
+UP105		(UTC +10:30) Lord Howe Island
+UP11		(UTC +11:00) Magadan Time, Solomon Islands, Vanuatu
+UP115		(UTC +11:30) Norfolk Island
+UP12		(UTC +12:00) Fiji, Gilbert Islands, Kamchatka, New Zealand
+UP1275		(UTC +12:45) Chatham Islands Standard Time
+UP1			(UTC +13:00) Phoenix Islands Time, Tonga
+UP14		(UTC +14:00) Line Islands
+===========	=====================================================================
\ No newline at end of file
diff --git a/user_guide_src/source/helpers/file_helper.rst b/user_guide_src/source/helpers/file_helper.rst
index bfc271e..60c5aa9 100644
--- a/user_guide_src/source/helpers/file_helper.rst
+++ b/user_guide_src/source/helpers/file_helper.rst
@@ -32,6 +32,9 @@
 	controller or view files. CodeIgniter uses a front controller so paths
 	are always relative to the main site index.
 
+.. note:: This function is DEPRECATED. Use the native ``file_get_contents()``
+	instead.
+
 If your server is running an `open_basedir` restriction this function might not work if you are trying to access a file above the calling script.
 
 write_file('path', $data)
diff --git a/user_guide_src/source/helpers/form_helper.rst b/user_guide_src/source/helpers/form_helper.rst
index a110f3c..fa7b3db 100644
--- a/user_guide_src/source/helpers/form_helper.rst
+++ b/user_guide_src/source/helpers/form_helper.rst
@@ -543,3 +543,7 @@
 	<input type="radio" name="myradio" value="1" <?php echo  set_radio('myradio', '1', TRUE); ?> />
 	<input type="radio" name="myradio" value="2" <?php echo  set_radio('myradio', '2'); ?> />
 
+.. note:: If you are using the Form Validation class, you must always specify a rule for your field,
+	even if empty, in order for the set_*() functions to work. This is because if a Form Validation object
+	is defined, the control for set_*() is handed over to a method of the class instead of the generic helper
+	function.
\ No newline at end of file
diff --git a/user_guide_src/source/helpers/security_helper.rst b/user_guide_src/source/helpers/security_helper.rst
index b1bcf2b..ec0be28 100644
--- a/user_guide_src/source/helpers/security_helper.rst
+++ b/user_guide_src/source/helpers/security_helper.rst
@@ -43,8 +43,10 @@
 	$str = do_hash($str); // SHA1
 	$str = do_hash($str, 'md5'); // MD5
 
-.. note:: This function was formerly named dohash(), which has been
-	removed in favor of `do_hash()`.
+.. note:: This function was formerly named ``dohash()``, which has been
+	removed in favor of ``do_hash()``.
+
+.. note:: This function is DEPRECATED. Use the native ``hash()`` instead.
 
 strip_image_tags()
 ==================
diff --git a/user_guide_src/source/helpers/string_helper.rst b/user_guide_src/source/helpers/string_helper.rst
index 2d23fb0..19500aa 100644
--- a/user_guide_src/source/helpers/string_helper.rst
+++ b/user_guide_src/source/helpers/string_helper.rst
@@ -36,8 +36,7 @@
 -  **unique**: Encrypted with MD5 and uniqid(). Note: The length
    parameter is not available for this type. Returns a fixed length 32
    character string.
--  **sha1**: An encrypted random number based on do_hash() from the
-   :doc:`security helper <security_helper>`.
+-  **sha1**: An encrypted random number based on ``sha1()``.
 
 Usage example
 
diff --git a/user_guide_src/source/helpers/url_helper.rst b/user_guide_src/source/helpers/url_helper.rst
index e6d51b2..82db6a5 100644
--- a/user_guide_src/source/helpers/url_helper.rst
+++ b/user_guide_src/source/helpers/url_helper.rst
@@ -168,27 +168,35 @@
 
 ::
 
-	$atts = array(               
-		'width'      => '800',               
-		'height'     => '600',               
-		'scrollbars' => 'yes',               
-		'status'     => 'yes',               
-		'resizable'  => 'yes',               
-		'screenx'    => '0',               
-		'screeny'    => '0'             
+	$atts = array(
+		'width'       => '800',
+		'height'      => '600',
+		'scrollbars'  => 'yes',
+		'status'      => 'yes',
+		'resizable'   => 'yes',
+		'screenx'     => '0',
+		'screeny'     => '0',
+		'window_name' => '_blank'
 	);
 
 	echo anchor_popup('news/local/123', 'Click Me!', $atts);
 
-Note: The above attributes are the function defaults so you only need to
-set the ones that are different from what you need. If you want the
-function to use all of its defaults simply pass an empty array in the
-third parameter
+.. note:: The above attributes are the function defaults so you only need to
+	set the ones that are different from what you need. If you want the
+	function to use all of its defaults simply pass an empty array in the
+	third parameter
 
 ::
 
 	echo anchor_popup('news/local/123', 'Click Me!', array());
 
+.. note:: The 'window_name' is not really an attribute, but an argument to
+	the JavaScript `window.open() <http://www.w3schools.com/jsref/met_win_open.asp>`
+	method, which accepts either a window name or a window target.
+
+.. note:: Any other attribute than the listed above will be parsed as an
+	HTML attribute to the anchor tag.
+
 mailto()
 ========
 
diff --git a/user_guide_src/source/index.rst b/user_guide_src/source/index.rst
index 6cdeb24..e42425b 100644
--- a/user_guide_src/source/index.rst
+++ b/user_guide_src/source/index.rst
@@ -1,34 +1,106 @@
-Welcome to CodeIgniter
-======================
+######################
+CodeIgniter User Guide
+######################
 
-CodeIgniter is an Application Development Framework - a toolkit - for
-people who build web sites using PHP. Its goal is to enable you to
-develop projects much faster than you could if you were writing code
-from scratch, by providing a rich set of libraries for commonly needed
-tasks, as well as a simple interface and logical structure to access
-these libraries. CodeIgniter lets you creatively focus on your project
-by minimizing the amount of code needed for a given task.
+- :doc:`License Agreement <license>`
+- :doc:`Change Log <changelog>`
 
-Who is CodeIgniter For?
-=======================
+.. contents::
+   :local:
+   :depth: 2
 
-CodeIgniter is right for you if:
+*******
+Welcome
+*******
 
--  You want a framework with a small footprint.
--  You need exceptional performance.
--  You need broad compatibility with standard hosting accounts that run
-   a variety of PHP versions and configurations.
--  You want a framework that requires nearly zero configuration.
--  You want a framework that does not require you to use the command
-   line.
--  You want a framework that does not require you to adhere to
-   restrictive coding rules.
--  You are not interested in large-scale monolithic libraries like PEAR.
--  You do not want to be forced to learn a templating language (although
-   a template parser is optionally available if you desire one).
--  You eschew complexity, favoring simple solutions.
--  You need clear, thorough documentation.
+- :doc:`general/welcome`
 
+**********
+Basic Info
+**********
+
+- :doc:`general/requirements`
+- :doc:`general/credits`
+
+************
+Installation
+************
+
+- :doc:`installation/downloads`
+- :doc:`installation/index`
+- :doc:`installation/upgrading`
+- :doc:`installation/troubleshooting`
+
+************
+Introduction
+************
+
+- :doc:`overview/getting_started`
+- :doc:`overview/at_a_glance`
+- :doc:`overview/cheatsheets`
+- :doc:`overview/features`
+- :doc:`overview/appflow`
+- :doc:`overview/mvc`
+- :doc:`overview/goals`
+
+********
+Tutorial
+********
+
+- :doc:`tutorial/index`
+- :doc:`tutorial/static_pages`
+- :doc:`tutorial/news_section`
+- :doc:`tutorial/create_news_items`
+- :doc:`tutorial/conclusion`
+
+**************
+General Topics
+**************
+
+.. toctree::
+	:glob:
+	:titlesonly:
+	
+	general/index
+
+*****************
+Library Reference
+*****************
+
+.. toctree::
+	:glob:
+	:titlesonly:
+	
+	libraries/index
+
+****************
+Driver Reference
+****************
+
+- :doc:`libraries/caching`
+- :doc:`database/index`
+- :doc:`libraries/javascript`
+
+****************
+Helper Reference
+****************
+
+.. toctree::
+	:glob:
+	:titlesonly:
+	
+	helpers/index
+
+***************************
+Contributing to CodeIgniter
+***************************
+
+.. toctree::
+	:glob:
+	:titlesonly:
+	
+	contributing/index
+	DCO
 
 .. toctree::
 	:glob:
@@ -38,6 +110,7 @@
 	*
 	overview/index
 	general/requirements
+	general/welcome
 	installation/index
 	general/index
 	libraries/index
diff --git a/user_guide_src/source/installation/downloads.rst b/user_guide_src/source/installation/downloads.rst
index a4a6b7f..45a8f80 100644
--- a/user_guide_src/source/installation/downloads.rst
+++ b/user_guide_src/source/installation/downloads.rst
@@ -2,9 +2,15 @@
 Downloading CodeIgniter
 #######################
 
--  `CodeIgniter V 2.1.0 (Current
+-  `CodeIgniter V 3.0.0 (Current
    version) <http://codeigniter.com/downloads/>`_
 -  `CodeIgniter V
+   2.1.2 <http://codeigniter.com/download_files/reactor/CodeIgniter_2.1.2.zip>`_
+-  `CodeIgniter V
+   2.1.1 <http://codeigniter.com/download_files/reactor/CodeIgniter_2.1.1.zip>`_
+-  `CodeIgniter V
+   2.1.0 <http://codeigniter.com/download_files/reactor/CodeIgniter_2.1.0.zip>`_
+-  `CodeIgniter V
    2.0.3 <http://codeigniter.com/download_files/reactor/CodeIgniter_2.0.3.zip>`_
 -  `CodeIgniter V
    2.0.2 <http://codeigniter.com/download_files/reactor/CodeIgniter_2.0.2.zip>`_
diff --git a/user_guide_src/source/installation/upgrade_210.rst b/user_guide_src/source/installation/upgrade_210.rst
index 9d7e1a2..5874bfc 100644
--- a/user_guide_src/source/installation/upgrade_210.rst
+++ b/user_guide_src/source/installation/upgrade_210.rst
@@ -8,15 +8,19 @@
 Step 1: Update your CodeIgniter files
 =====================================
 
-Replace all files and directories in your "system" folder and replace
-your index.php file. If any modifications were made to your index.php
-they will need to be made fresh in this new one.
+Replace all files and directories in your "system" folder.
+
+.. note:: If you have any custom developed files in these folders please
+	make copies of them first.
 
 Step 2: Replace config/user_agents.php
 ======================================
 
 This config file has been updated to contain more user agent types,
-please copy it to application/config/user_agents.php.
+please copy it to _application/config/user_agents.php*.
 
-.. note:: If you have any custom developed files in these folders please
-	make copies of them first.
\ No newline at end of file
+Step 3: Update your user guide
+==============================
+
+Please also replace your local copy of the user guide with the new
+version.
\ No newline at end of file
diff --git a/user_guide_src/source/installation/upgrade_211.rst b/user_guide_src/source/installation/upgrade_211.rst
new file mode 100644
index 0000000..59faca8
--- /dev/null
+++ b/user_guide_src/source/installation/upgrade_211.rst
@@ -0,0 +1,33 @@
+#############################
+Upgrading from 2.1.0 to 2.1.1
+#############################
+
+Before performing an update you should take your site offline by
+replacing the index.php file with a static one.
+
+Step 1: Update your CodeIgniter files
+=====================================
+
+Replace all files and directories in your "system" folder and replace
+your index.php file. If any modifications were made to your index.php
+they will need to be made fresh in this new one.
+
+.. note:: If you have any custom developed files in these folders please
+	make copies of them first.
+
+Step 2: Replace config/mimes.php
+================================
+
+This config file has been updated to contain more user mime-types, please copy
+it to _application/config/mimes.php*.
+
+Step 3: Update your IP address tables
+=====================================
+
+This upgrade adds support for IPv6 IP addresses. In order to store them, you need
+to enlarge your ip_address columns to 45 characters. For example, CodeIgniter's
+session table will need to change
+
+::
+
+	ALTER TABLE ci_sessions CHANGE ip_address ip_address varchar(45) default '0' NOT NULL
\ No newline at end of file
diff --git a/user_guide_src/source/installation/upgrade_212.rst b/user_guide_src/source/installation/upgrade_212.rst
new file mode 100644
index 0000000..205ad86
--- /dev/null
+++ b/user_guide_src/source/installation/upgrade_212.rst
@@ -0,0 +1,22 @@
+#############################
+Upgrading from 2.1.1 to 2.1.2
+#############################
+
+Before performing an update you should take your site offline by
+replacing the index.php file with a static one.
+
+Step 1: Update your CodeIgniter files
+=====================================
+
+Replace all files and directories in your "system" folder and replace
+your index.php file. If any modifications were made to your index.php
+they will need to be made fresh in this new one.
+
+.. note:: If you have any custom developed files in these folders please
+	make copies of them first.
+
+Step 2: Update your user guide
+==============================
+
+Please also replace your local copy of the user guide with the new
+version.
\ No newline at end of file
diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst
index 63c4227..f304a71 100644
--- a/user_guide_src/source/installation/upgrade_300.rst
+++ b/user_guide_src/source/installation/upgrade_300.rst
@@ -1,15 +1,14 @@
 #############################
-Upgrading from 2.1.0 to 3.0.0
+Upgrading from 2.1.2 to 3.0.0
 #############################
 
 .. note:: These upgrade notes are for a version that is yet to be released.
 
+Before performing an update you should take your site offline by replacing the index.php file with a static one.
 
-Before performing an update you should take your site offline by
-replacing the index.php file with a static one.
-
+*************************************
 Step 1: Update your CodeIgniter files
-=====================================
+*************************************
 
 Replace all files and directories in your "system" folder and replace
 your index.php file. If any modifications were made to your index.php
@@ -18,31 +17,125 @@
 .. note:: If you have any custom developed files in these folders please
 	make copies of them first.
 
-Step 2: Change References to the SHA Library
-============================================
+********************************
+Step 2: Replace config/mimes.php
+********************************
 
-The previously deprecated SHA library has been removed in CodeIgniter 3.0.
-Alter your code to use the native `sha1()` PHP function to generate a sha1 hash.
+This config file has been updated to contain more user mime-types, please copy
+it to _application/config/mimes.php*.
 
-Additionally, the `sha1()` method in the :doc:`Encryption Library <../libraries/encryption>` has been removed.
-
+**************************************************************
 Step 3: Remove $autoload['core'] from your config/autoload.php
-==============================================================
+**************************************************************
 
-Use of the `$autoload['core']` config array has been deprecated as of CodeIgniter 1.4.1 and is now removed.
-Move any entries that you might have listed there to `$autoload['libraries']` instead.
+Use of the ``$autoload['core']`` config array has been deprecated as of CodeIgniter 1.4.1 and is now removed.
+Move any entries that you might have listed there to ``$autoload['libraries']`` instead.
 
+***************************************
 Step 4: Update your config/database.php
-=======================================
+***************************************
 
-Due to 3.0.0's renaming of Active Record to Query Builder, inside your _config/database.php_, you will
+Due to 3.0.0's renaming of Active Record to Query Builder, inside your `config/database.php`, you will
 need to rename the `$active_record` variable to `$query_builder`.
 
     $active_group = 'default';
     // $active_record = TRUE;
     $query_builder = TRUE;
-    
-Step 5: Move your errors folder
-===============================
 
-In version 3.0.0, the errors folder has been moved from "application/errors" to "application/views/errors".
\ No newline at end of file
+*******************************
+Step 5: Move your errors folder
+*******************************
+
+In version 3.0.0, the errors folder has been moved from _application/errors* to _application/views/errors*.
+
+****************************************************************************
+Step 6: Check the calls to Array Helper's element() and elements() functions
+****************************************************************************
+
+The default return value of these functions, when the required elements
+don't exist, has been changed from FALSE to NULL.
+
+***************************************************************
+Step 7: Remove usage of (previously) deprecated functionalities
+***************************************************************
+
+In addition to the ``$autoload['core']`` configuration setting, there's a number of other functionalities
+that have been removed in CodeIgniter 3.0.0:
+
+The SHA1 library
+================
+
+The previously deprecated SHA1 library has been removed, alter your code to use PHP's native
+``sha1()`` function to generate a SHA1 hash.
+
+Additionally, the ``sha1()`` method in the :doc:`Encryption Library <../libraries/encryption>` has been removed.
+
+The EXT constant
+================
+
+Usage of the ``EXT`` constant has been deprecated since dropping support for PHP 4. There's no
+longer a need to maintain different filename extensions and in this new CodeIgniter version,
+the ``EXT`` constant has been removed. Use just '.php' instead.
+
+Smiley helper js_insert_smiley()
+================================
+
+:doc:`Smiley Helper <../helpers/smiley_helper>` function ``js_insert_smiley()`` has been deprecated
+since CodeIgniter 1.7.2 and is now removed. You'll need to switch to ``smiley_js()`` instead.
+
+Security helper do_hash()
+=========================
+
+:doc:`Security Helper <../helpers/security_helper>` function ``do_hash()`` is now just an alias for
+PHP's native ``hash()`` function. It is deprecated and scheduled for removal in CodeIgniter 3.1+.
+
+.. note:: This function is still available, but you're strongly encouraged to remove it's usage sooner
+	rather than later.
+
+File helper read_file()
+=======================
+
+:doc:`File Helper <../helpers/file_helper>` function ``read_file()`` is now just an alias for
+PHP's native ``file_get_contents()`` function. It is deprecated and scheduled for removal in
+CodeIgniter 3.1+.
+
+.. note:: This function is still available, but you're strongly encouraged to remove it's usage sooner
+	rather than later.
+
+Date helper standard_date()
+===========================
+
+:doc:`Date Helper <../helpers/date_helper>` function ``standard_date()`` is being deprecated due
+to the availability of native PHP `constants <http://www.php.net/manual/en/class.datetime.php#datetime.constants.types>`_,
+which when combined with ``date()`` provide the same functionality. Furthermore, they have the
+exact same names as the ones supported by ``standard_date()``. Here are examples of how to replace
+it's usage:
+
+::
+
+	// Old way
+	standard_date(); // defaults to standard_date('DATE_RFC822', now());
+
+	// Replacement
+	date(DATE_RFC822, now());
+
+	// Old way
+	standard_date('DATE_ATOM', $time);
+
+	// Replacement
+	date(DATE_ATOM, $time);
+
+.. note:: This function is still available, but you're strongly encouraged to remove its' usage sooner
+	rather than later as it is scheduled for removal in CodeIgniter 3.1+.
+
+Pagination library 'anchor_class' setting
+=========================================
+
+The :doc:`Pagination Library <../libraries/pagination>` now supports adding pretty much any HTML
+attribute to your anchors via the 'attributes' configuration setting. This includes passing the
+'class' attribute and using the separate 'anchor_class' setting no longer makes sense.
+As a result of that, the 'anchor_class' setting is now deprecated and scheduled for removal in
+CodeIgniter 3.1+.
+
+.. note:: This setting is still available, but you're strongly encouraged to remove its' usage sooner
+	rather than later.
\ No newline at end of file
diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst
index 2badffc..545f344 100644
--- a/user_guide_src/source/installation/upgrading.rst
+++ b/user_guide_src/source/installation/upgrading.rst
@@ -5,6 +5,9 @@
 Please read the upgrade notes corresponding to the version you are
 upgrading from.
 
+-  :doc:`Upgrading from 2.1.2 to 3.0.0 <upgrade_300>`
+-  :doc:`Upgrading from 2.1.1 to 2.1.2 <upgrade_212>`
+-  :doc:`Upgrading from 2.1.0 to 2.1.1 <upgrade_211>`
 -  :doc:`Upgrading from 2.0.3 to 2.1.0 <upgrade_210>`
 -  :doc:`Upgrading from 2.0.2 to 2.0.3 <upgrade_203>`
 -  :doc:`Upgrading from 2.0.1 to 2.0.2 <upgrade_202>`
diff --git a/user_guide_src/source/libraries/config.rst b/user_guide_src/source/libraries/config.rst
index c81cad7..08d9c29 100644
--- a/user_guide_src/source/libraries/config.rst
+++ b/user_guide_src/source/libraries/config.rst
@@ -149,11 +149,13 @@
 -  Your own custom configuration files
 
 .. note::
-	CodeIgniter always tries to load the configuration files for
-	the current environment first. If the file does not exist, the global
-	config file (i.e., the one in application/config/) is loaded. This means
-	you are not obligated to place **all** of your configuration files in an
-	environment folder − only the files that change per environment.
+	CodeIgniter always loads the global config file first (i.e., the one in application/config/),
+	then tries to load the configuration files for the current environment.
+	This means you are not obligated to place **all** of your configuration files in an
+	environment folder. Only the files that change per environment. Additionally you don't
+	have to copy **all** the config items in the environment config file. Only the config items
+	that you wish to change for your environment. The config items declared in your environment
+	folders always overwrite those in your global config files.
 
 Helper Functions
 ================
diff --git a/user_guide_src/source/libraries/email.rst b/user_guide_src/source/libraries/email.rst
index daf0009..c5fa680 100644
--- a/user_guide_src/source/libraries/email.rst
+++ b/user_guide_src/source/libraries/email.rst
@@ -182,6 +182,14 @@
 accept HTML email. If you do not set your own message CodeIgniter will
 extract the message from your HTML email and strip the tags.
 
+$this->email->set_header()
+--------------------------
+
+Appends additional headers to the e-mail::
+
+	$this->email->set_header('Header1', 'Value1');
+	$this->email->set_header('Header2', 'Value2');
+
 $this->email->clear()
 ---------------------
 
diff --git a/user_guide_src/source/libraries/encryption.rst b/user_guide_src/source/libraries/encryption.rst
index 28bdca2..a381222 100644
--- a/user_guide_src/source/libraries/encryption.rst
+++ b/user_guide_src/source/libraries/encryption.rst
@@ -26,7 +26,7 @@
 anything that requires high security, like storing credit card numbers.
 
 To take maximum advantage of the encryption algorithm, your key should
-be 32 characters in length (128 bits). The key should be as random a
+be 32 characters in length (256 bits). The key should be as random a
 string as you can concoct, with numbers and uppercase and lowercase
 letters. Your key should **not** be a simple text string. In order to be
 cryptographically secure it needs to be as random as possible.
diff --git a/user_guide_src/source/libraries/file_uploading.rst b/user_guide_src/source/libraries/file_uploading.rst
index d573fc7..65cd5c7 100644
--- a/user_guide_src/source/libraries/file_uploading.rst
+++ b/user_guide_src/source/libraries/file_uploading.rst
@@ -215,6 +215,9 @@
                                                                        that can not be discerned by the person uploading it.
 **remove_spaces**            TRUE              TRUE/FALSE (boolean)    If set to TRUE, any spaces in the file name will be converted to
                                                                        underscores. This is recommended.
+**detect_mime**              TRUE              TRUE/FALSE (boolean)    If set to TRUE, a server side detection of the file type will be
+                                                                       performed to avoid code injection attacks. DO NOT disable this option
+                                                                       unless you have no other option as that would cause a security risk.
 ============================ ================= ======================= ======================================================================
 
 Setting preferences in a config file
@@ -287,6 +290,10 @@
 	    [image_size_str] => width="800" height="200"
 	)
 
+To return one element from the array::
+
+	$this->upload->data('file_name');	// Returns: mypic.jpg
+
 Explanation
 ***********
 
diff --git a/user_guide_src/source/libraries/form_validation.rst b/user_guide_src/source/libraries/form_validation.rst
index 028b61c..3bcad7b 100644
--- a/user_guide_src/source/libraries/form_validation.rst
+++ b/user_guide_src/source/libraries/form_validation.rst
@@ -304,6 +304,10 @@
 new error messages that correspond to your new rules. There are numerous
 rules available which you can read about in the validation reference.
 
+.. note:: You can also pass an array of rules to set_rules(), instead of a string. Example::
+
+	$this->form_validation->set_rules('username', 'Username', array('required', 'min_length[5]'));
+
 Prepping Data
 =============
 
@@ -884,6 +888,7 @@
 **valid_email**           No         Returns FALSE if the form element does not contain a valid email address.
 **valid_emails**          No         Returns FALSE if any value provided in a comma separated list is not a valid email.
 **valid_ip**              No         Returns FALSE if the supplied IP is not valid.
+                                     Accepts an optional parameter of 'ipv4' or 'ipv6' to specify an IP format.
 **valid_base64**          No         Returns FALSE if the supplied string contains anything other than valid Base64 characters.
 ========================= ========== ============================================================================================= =======================
 
@@ -934,7 +939,7 @@
 
 		:param string $field: The field name
 		:param string $label: The field label
-		:param string $rules: The rules, seperated by a pipe "|"
+		:param mixed $rules: The rules, as a string with rules separated by a pipe "|", or an array or rules.
 		:rtype: Object
 	
 		Permits you to set validation rules, as described in the tutorial
diff --git a/user_guide_src/source/libraries/ftp.rst b/user_guide_src/source/libraries/ftp.rst
index 20b11a5..05a3fdc 100644
--- a/user_guide_src/source/libraries/ftp.rst
+++ b/user_guide_src/source/libraries/ftp.rst
@@ -26,7 +26,7 @@
 
 In this example a connection is opened to the FTP server, and a local
 file is read and uploaded in ASCII mode. The file permissions are set to
-755. Note: Setting permissions requires PHP 5.
+755.
 
 ::
 
@@ -136,8 +136,7 @@
 **Mode options are:** ascii, binary, and auto (the default). If auto is
 used it will base the mode on the file extension of the source file.
 
-Permissions are available if you are running PHP 5 and can be passed as
-an octal value in the fourth parameter.
+If set, permissions have to be passed as an octal value.
 
 $this->ftp->download()
 ======================
diff --git a/user_guide_src/source/libraries/input.rst b/user_guide_src/source/libraries/input.rst
index 432bac3..c0b9c65 100644
--- a/user_guide_src/source/libraries/input.rst
+++ b/user_guide_src/source/libraries/input.rst
@@ -42,14 +42,14 @@
 Please refer to the :doc:`Security class <security>` documentation for
 information on using XSS Filtering in your application.
 
-Using POST, COOKIE, or SERVER Data
-==================================
+Using POST, GET, COOKIE, or SERVER Data
+=======================================
 
-CodeIgniter comes with three helper functions that let you fetch POST,
+CodeIgniter comes with four helper methods that let you fetch POST, GET,
 COOKIE or SERVER items. The main advantage of using the provided
 functions rather than fetching an item directly ($_POST['something'])
-is that the functions will check to see if the item is set and return
-false (boolean) if not. This lets you conveniently use data without
+is that the methods will check to see if the item is set and return
+NULL if not. This lets you conveniently use data without
 having to test whether an item exists first. In other words, normally
 you might do something like this::
 
@@ -59,9 +59,10 @@
 
 	$something = $this->input->post('something');
 
-The three functions are:
+The four methods are:
 
 -  $this->input->post()
+-  $this->input->get()
 -  $this->input->cookie()
 -  $this->input->server()
 
@@ -73,8 +74,8 @@
 
 	$this->input->post('some_data');
 
-The function returns FALSE (boolean) if the item you are attempting to
-retrieve does not exist.
+The function returns NULL if the item you are attempting to retrieve
+does not exist.
 
 The second optional parameter lets you run the data through the XSS
 filter. It's enabled by setting the second parameter to boolean TRUE;
@@ -130,7 +131,9 @@
 This function is identical to the post function, only it fetches cookie
 data::
 
-	$this->input->cookie('some_data', TRUE);
+	$this->input->cookie('some_cookie');
+	$this->input->cookie('some_cookie, TRUE); // with XSS filter
+
 
 $this->input->server()
 ======================
@@ -195,25 +198,6 @@
 
 	$this->input->set_cookie($name, $value, $expire, $domain, $path, $prefix, $secure);
 
-$this->input->cookie()
-======================
-
-Lets you fetch a cookie. The first parameter will contain the name of
-the cookie you are looking for (including any prefixes)::
-
-	cookie('some_cookie');
-
-The function returns NULL if the item you are attempting to
-retrieve does not exist.
-
-The second optional parameter lets you run the data through the XSS
-filter. It's enabled by setting the second parameter to boolean TRUE;
-
-::
-
-	cookie('some_cookie', TRUE);
-
-
 $this->input->ip_address()
 ===========================
 
@@ -242,6 +226,9 @@
 	     echo 'Valid';
 	}
 
+Accepts an optional second string parameter of 'ipv4' or 'ipv6' to specify
+an IP format. The default checks for both formats.
+
 $this->input->user_agent()
 ===========================
 
diff --git a/user_guide_src/source/libraries/loader.rst b/user_guide_src/source/libraries/loader.rst
index 2090404..aadf974 100644
--- a/user_guide_src/source/libraries/loader.rst
+++ b/user_guide_src/source/libraries/loader.rst
@@ -116,12 +116,12 @@
 
 	$string = $this->load->view('myfile', '', true);
 
-$this->load->model('Model_name');
+$this->load->model('model_name');
 ==================================
 
 ::
 
-	$this->load->model('Model_name');
+	$this->load->model('model_name');
 
 
 If your model is located in a sub-folder, include the relative path from
@@ -134,7 +134,7 @@
 If you would like your model assigned to a different object name you can
 specify it via the second parameter of the loading function::
 
-	$this->load->model('Model_name', 'fubar');
+	$this->load->model('model_name', 'fubar');
 
 	$this->fubar->function();
 
diff --git a/user_guide_src/source/libraries/output.rst b/user_guide_src/source/libraries/output.rst
index baceaae..0472d14 100644
--- a/user_guide_src/source/libraries/output.rst
+++ b/user_guide_src/source/libraries/output.rst
@@ -49,6 +49,10 @@
 .. important:: Make sure any non-mime string you pass to this method
 	exists in config/mimes.php or it will have no effect.
 
+You can also set the character set of the document, by passing a second argument::
+
+	$this->output->set_content_type('css', 'utf-8');
+
 $this->output->get_content_type();
 ==========================================
 
diff --git a/user_guide_src/source/libraries/pagination.rst b/user_guide_src/source/libraries/pagination.rst
index f1653c9..7d750bd 100644
--- a/user_guide_src/source/libraries/pagination.rst
+++ b/user_guide_src/source/libraries/pagination.rst
@@ -21,9 +21,9 @@
 
 	$config['base_url'] = 'http://example.com/index.php/test/page/';
 	$config['total_rows'] = 200;
-	$config['per_page'] = 20; 
+	$config['per_page'] = 20;
 
-	$this->pagination->initialize($config); 
+	$this->pagination->initialize($config);
 
 	echo $this->pagination->create_links();
 
@@ -112,6 +112,33 @@
 Note that "per_page" is the default query string passed, however can be
 configured using $config['query_string_segment'] = 'your_string'
 
+$config['reuse_query_string'] = FALSE;
+======================================
+
+By default your Query String arguments (nothing to do with other
+query string options) will be ignored. Setting this config to
+TRUE will add existing query string arguments back into the
+URL after the URI segment and before the suffix
+
+::
+
+	http://example.com/index.php/test/page/20?query=search%term
+
+This helps you mix together normal :doc:`URI Segments <../general/urls>`
+as well as query string arguments, which until 3.0 was not possible.
+
+$config['prefix'] = '';
+==================================
+
+A custom prefix added to the path. The prefix value will be right before
+the offset segment.
+
+$config['suffix'] = '';
+==================================
+
+A custom suffix added to the path. The sufix value will be right after
+the offset segment.
+
 ***********************
 Adding Enclosing Markup
 ***********************
@@ -247,10 +274,30 @@
 
 	 $config['display_pages'] = FALSE;
 
-******************************
-Adding a class to every anchor
-******************************
+****************************
+Adding attributes to anchors
+****************************
 
-If you want to add a class attribute to every link rendered by the
-pagination class, you can set the config "anchor_class" equal to the
-classname you want.
+If you want to add an extra attribute to be added to every link rendered
+by the pagination class, you can set them as key/value pairs in the
+"attributes" config
+
+::
+
+	// Produces: class="myclass"
+	$config['attributes'] = array('class' => 'myclass');
+
+.. note:: Usage of the old method of setting classes via "anchor_class"
+	is deprecated.
+
+*****************************
+Disabling the "rel" attribute
+*****************************
+
+By default the rel attribute is dynamically generated and appended to
+the appropriate anchors. If for some reason you want to turn it off,
+you can pass boolean FALSE as a regular attribute
+
+::
+
+	$config['attributes']['rel'] = FALSE;
\ No newline at end of file
diff --git a/user_guide_src/source/libraries/sessions.rst b/user_guide_src/source/libraries/sessions.rst
index e8332ee..5400524 100644
--- a/user_guide_src/source/libraries/sessions.rst
+++ b/user_guide_src/source/libraries/sessions.rst
@@ -245,7 +245,7 @@
 
 	CREATE TABLE IF NOT EXISTS  `ci_sessions` (
 		session_id varchar(40) DEFAULT '0' NOT NULL,
-		ip_address varchar(16) DEFAULT '0' NOT NULL,
+		ip_address varchar(45) DEFAULT '0' NOT NULL,
 		user_agent varchar(120) NOT NULL,
 		last_activity int(10) unsigned DEFAULT 0 NOT NULL,
 		user_data text NOT NULL,
diff --git a/user_guide_src/source/libraries/uri.rst b/user_guide_src/source/libraries/uri.rst
index cdd76e3..bb959b0 100644
--- a/user_guide_src/source/libraries/uri.rst
+++ b/user_guide_src/source/libraries/uri.rst
@@ -146,7 +146,7 @@
 
 The function would return this::
 
-	/news/local/345
+	news/local/345
 
 $this->uri->ruri_string()
 ==========================
diff --git a/license_afl.rst b/user_guide_src/source/license_afl.rst
similarity index 100%
rename from license_afl.rst
rename to user_guide_src/source/license_afl.rst