Merge pull request #2566 from vlakoff/develop

Complete 3.0 upgrade notes about NULL returned on missing items
diff --git a/system/core/Security.php b/system/core/Security.php
index 196d611..70cf3e0 100644
--- a/system/core/Security.php
+++ b/system/core/Security.php
@@ -38,6 +38,30 @@
 class CI_Security {
 
 	/**
+	 * List of sanitize filename strings
+	 *
+	 * @var	array
+	 */
+	public $filename_bad_chars =	array(
+		'../', '<!--', '-->', '<', '>',
+		"'", '"', '&', '$', '#',
+		'{', '}', '[', ']', '=',
+		';', '?', '%20', '%22',
+		'%3c',		// <
+		'%253c',	// <
+		'%3e',		// >
+		'%0e',		// >
+		'%28',		// (
+		'%29',		// )
+		'%2528',	// (
+		'%26',		// &
+		'%24',		// $
+		'%3f',		// ?
+		'%3b',		// ;
+		'%3d'		// =
+	);
+
+	/**
 	 * XSS Hash
 	 *
 	 * Random Hash for protecting URLs.
@@ -549,24 +573,7 @@
 	 */
 	public function sanitize_filename($str, $relative_path = FALSE)
 	{
-		$bad = array(
-			'../', '<!--', '-->', '<', '>',
-			"'", '"', '&', '$', '#',
-			'{', '}', '[', ']', '=',
-			';', '?', '%20', '%22',
-			'%3c',		// <
-			'%253c',	// <
-			'%3e',		// >
-			'%0e',		// >
-			'%28',		// (
-			'%29',		// )
-			'%2528',	// (
-			'%26',		// &
-			'%24',		// $
-			'%3f',		// ?
-			'%3b',		// ;
-			'%3d'		// =
-		);
+		$bad = $this->filename_bad_chars;
 
 		if ( ! $relative_path)
 		{
diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php
index fa89661..184a8df 100644
--- a/system/database/drivers/pdo/pdo_driver.php
+++ b/system/database/drivers/pdo/pdo_driver.php
@@ -69,7 +69,7 @@
 	{
 		parent::__construct($params);
 
-		if (preg_match('/([^;]+):/', $this->dsn, $match) && count($match) === 2)
+		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.
@@ -77,7 +77,7 @@
 			return;
 		}
 		// Legacy support for DSN specified in the hostname field
-		elseif (preg_match('/([^;]+):/', $this->hostname, $match) && count($match) === 2)
+		elseif (preg_match('/([^:]+):/', $this->hostname, $match) && count($match) === 2)
 		{
 			$this->dsn = $this->hostname;
 			$this->hostname = NULL;
diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php
index 6fca73f..146c0f5 100644
--- a/system/helpers/form_helper.php
+++ b/system/helpers/form_helper.php
@@ -931,9 +931,9 @@
 	 */
 	function _attributes_to_string($attributes)
 	{
-		if (is_string($attributes))
+		if (empty($attributes))
 		{
-			return ($attributes === '' ? '' : ' '.$attributes);
+			return '';
 		}
 
 		if (is_object($attributes))
@@ -953,6 +953,11 @@
 			return $atts;
 		}
 
+		if (is_string($attributes))
+		{
+			return ' '.$attributes;
+		}
+
 		return FALSE;
 	}
 }
diff --git a/system/libraries/Cache/drivers/Cache_apc.php b/system/libraries/Cache/drivers/Cache_apc.php
index 127a220..a84e7d2 100644
--- a/system/libraries/Cache/drivers/Cache_apc.php
+++ b/system/libraries/Cache/drivers/Cache_apc.php
@@ -150,7 +150,7 @@
 	{
 		if ( ! extension_loaded('apc') OR ! (bool) @ini_get('apc.enabled'))
 		{
-			log_message('error', 'The APC PHP extension must be loaded to use APC Cache.');
+			log_message('debug', 'The APC PHP extension must be loaded to use APC Cache.');
 			return FALSE;
 		}
 
diff --git a/system/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php
index 35d9104..d2a3a48 100644
--- a/system/libraries/Cache/drivers/Cache_memcached.php
+++ b/system/libraries/Cache/drivers/Cache_memcached.php
@@ -240,7 +240,7 @@
 	{
 		if ( ! extension_loaded('memcached') && ! extension_loaded('memcache'))
 		{
-			log_message('error', 'The Memcached Extension must be loaded to use Memcached Cache.');
+			log_message('debug', 'The Memcached Extension must be loaded to use Memcached Cache.');
 			return FALSE;
 		}
 
diff --git a/system/libraries/Cache/drivers/Cache_redis.php b/system/libraries/Cache/drivers/Cache_redis.php
index 484f284..40823fc 100644
--- a/system/libraries/Cache/drivers/Cache_redis.php
+++ b/system/libraries/Cache/drivers/Cache_redis.php
@@ -168,7 +168,7 @@
 		}
 		else
 		{
-			log_message('error', 'The Redis extension must be loaded to use Redis cache.');
+			log_message('debug', 'The Redis extension must be loaded to use Redis cache.');
 			return FALSE;
 		}
 	}
diff --git a/system/libraries/Cache/drivers/Cache_wincache.php b/system/libraries/Cache/drivers/Cache_wincache.php
index d749978..80d3ac1 100644
--- a/system/libraries/Cache/drivers/Cache_wincache.php
+++ b/system/libraries/Cache/drivers/Cache_wincache.php
@@ -150,7 +150,7 @@
 	{
 		if ( ! extension_loaded('wincache'))
 		{
-			log_message('error', 'The Wincache PHP extension must be loaded to use Wincache Cache.');
+			log_message('debug', 'The Wincache PHP extension must be loaded to use Wincache Cache.');
 			return FALSE;
 		}
 
diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php
index c7f6f82..659a026 100644
--- a/system/libraries/Session/Session.php
+++ b/system/libraries/Session/Session.php
@@ -60,11 +60,18 @@
 	public $params = array();
 
 	/**
+	 * Valid drivers list
+	 *
+	 * @var	array
+	 */
+	public $valid_drivers = array('native',	'cookie');
+
+	/**
 	 * Current driver in use
 	 *
 	 * @var	string
 	 */
-	protected $current = NULL;
+	public $current = NULL;
 
 	/**
 	 * User data
@@ -105,36 +112,26 @@
 
 		log_message('debug', 'CI_Session Class Initialized');
 
-		// Get valid drivers list
-		$this->valid_drivers = array(
-			'native',
-			'cookie'
-		);
-		$key = 'sess_valid_drivers';
-		$drivers = isset($params[$key]) ? $params[$key] : $CI->config->item($key);
-		if ($drivers)
+		// Add possible extra entries to our valid drivers list
+		$drivers = isset($params['sess_valid_drivers']) ? $params['sess_valid_drivers'] : $CI->config->item('sess_valid_drivers');
+		if ( ! empty($drivers))
 		{
-			// Add driver names to valid list
-			foreach ((array) $drivers as $driver)
-			{
-				if ( ! in_array(strtolower($driver), array_map('strtolower', $this->valid_drivers)))
-				{
-					$this->valid_drivers[] = $driver;
-				}
-			}
+			$drivers = array_map('strtolower', (array) $drivers);
+			$this->valid_drivers = array_merge($this->valid_drivers, array_diff($drivers, $this->valid_drivers));
 		}
 
 		// Get driver to load
-		$key = 'sess_driver';
-		$driver = isset($params[$key]) ? $params[$key] : $CI->config->item($key);
+		$driver = isset($params['sess_driver']) ? $params['sess_driver'] : $CI->config->item('sess_driver');
 		if ( ! $driver)
 		{
+			log_message('debug', "Session: No driver name is configured, defaulting to 'cookie'.");
 			$driver = 'cookie';
 		}
 
-		if ( ! in_array(strtolower($driver), array_map('strtolower', $this->valid_drivers)))
+		if ( ! in_array($driver, $this->valid_drivers))
 		{
-			$this->valid_drivers[] = $driver;
+			log_message('error', 'Session: Configured driver name is not valid, aborting.');
+			return;
 		}
 
 		// Save a copy of parameters in case drivers need access
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 1e463db..81d92f7 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -311,6 +311,7 @@
       -  Added Wincache driver.
       -  Added Redis driver.
       -  Added a *key_prefix* option for cache IDs.
+      -  Updated driver ``is_supported()`` methods to log at the "debug" level.
 
    -  :doc:`Email library <libraries/email>` changes include:
 
@@ -415,6 +416,7 @@
       -  Added method ``strip_image_tags()``.
       -  Added ``$config['csrf_regeneration']``, which makes token regeneration optional.
       -  Added ``$config['csrf_exclude_uris']``, which allows you list URIs which will not have the CSRF validation methods run.
+      -  Modified method ``sanitize_filename()`` to read a public ``$filename_bad_chars`` property for getting the invalid characters list.
 
    -  :doc:`URI Routing <general/routing>` changes include:
 
diff --git a/user_guide_src/source/helpers/form_helper.rst b/user_guide_src/source/helpers/form_helper.rst
index b2a9b6f..f49027b 100644
--- a/user_guide_src/source/helpers/form_helper.rst
+++ b/user_guide_src/source/helpers/form_helper.rst
@@ -22,7 +22,7 @@
 .. php:function:: form_open($action = '', $attributes = '', $hidden = array())
 
 	:param	string	$action: Form action/target URI string
-	:param	string	$attributes: HTML attributes
+	:param	array	$attributes: HTML attributes
 	:param	array	$hidden: An array of hidden fields' definitions
 	:returns:	string
 
@@ -41,7 +41,7 @@
 The above example would create a form that points to your base URL plus the
 "email/send" URI segments, like this::
 
-	<form method="post" accept-charset="utf-8" action="http://example.com/index.php/email/send" />
+	<form method="post" accept-charset="utf-8" action="http://example.com/index.php/email/send">
 
 Adding Attributes
 ^^^^^^^^^^^^^^^^^
@@ -52,9 +52,13 @@
 	$attributes = array('class' => 'email', 'id' => 'myform');
 	echo form_open('email/send', $attributes);
 
-The above example would create a form similar to this::
+Alternatively, you can specify the second parameter as a string::
 
-	<form method="post" accept-charset="utf-8" action="http://example.com/index.php/email/send" class="email" id="myform" />
+	echo form_open('email/send', 'class="email" id="myform"');
+
+The above examples would create a form similar to this::
+
+	<form method="post" accept-charset="utf-8" action="http://example.com/index.php/email/send" class="email" id="myform">
 
 Adding Hidden Input Fields
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -65,6 +69,8 @@
 	$hidden = array('username' => 'Joe', 'member_id' => '234');
 	echo form_open('email/send', '', $hidden);
 
+You can skip the second parameter by passing any falsy value to it.
+
 The above example would create a form similar to this::
 
 	<form method="post" accept-charset="utf-8" action="http://example.com/index.php/email/send">
@@ -77,7 +83,7 @@
 .. php:function:: form_open_multipart($action = '', $attributes = array(), $hidden = array())
 
 	:param	string	$action: Form action/target URI string
-	:param	string	$attributes: HTML attributes
+	:param	array	$attributes: HTML attributes
 	:param	array	$hidden: An array of hidden fields' definitions
 	:returns:	string
 
@@ -258,10 +264,10 @@
 Example::
 
 	$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',
 	);
 
 	$shirts_on_sale = array('small', 'large');
@@ -407,16 +413,14 @@
 the box should be checked or not.
 
 Similar to the other form functions in this helper, you can also pass an
-array of attributes to the function
-
-::
+array of attributes to the function::
 
 	$data = array(
-		'name'    => 'newsletter',
-		'id'      => 'newsletter',
-		'value'   => 'accept',
-		'checked' => TRUE,
-		'style'   => 'margin:10px'
+		'name'		=> 'newsletter',
+		'id'		=> 'newsletter',
+		'value'		=> 'accept',
+		'checked'	=> TRUE,
+		'style'		=> 'margin:10px'
 	);
 
 	echo form_checkbox($data);
@@ -523,11 +527,11 @@
 form to contain::
 
 	$data = array(
-		'name'    => 'button',
-		'id'      => 'button',
-		'value'   => 'true',
-		'type'    => 'reset',
-		'content' => 'Reset'
+		'name'		=> 'button',
+		'id'		=> 'button',
+		'value'		=> 'true',
+		'type'		=> 'reset',
+		'content'	=> 'Reset'
 	);
 
 	echo form_button($data);