summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--application/config/config.php5
-rw-r--r--system/core/Common.php91
-rw-r--r--system/core/Config.php12
-rw-r--r--system/core/Input.php46
-rw-r--r--system/core/Log.php9
-rw-r--r--system/libraries/Cache/drivers/Cache_redis.php8
-rw-r--r--system/libraries/Cache/drivers/Cache_wincache.php2
-rw-r--r--system/libraries/Session/drivers/Session_database_driver.php2
-rw-r--r--system/libraries/Session/drivers/Session_files_driver.php4
-rw-r--r--system/libraries/Session/drivers/Session_memcached_driver.php30
-rw-r--r--system/libraries/Session/drivers/Session_redis_driver.php42
-rw-r--r--tests/codeigniter/core/Security_test.php31
-rw-r--r--tests/codeigniter/database/DB_driver_test.php2
-rw-r--r--tests/codeigniter/database/DB_test.php8
-rw-r--r--tests/mocks/core/input.php8
-rw-r--r--user_guide_src/source/changelog.rst4
-rw-r--r--user_guide_src/source/database/query_builder.rst8
-rw-r--r--user_guide_src/source/database/results.rst4
-rw-r--r--user_guide_src/source/general/security.rst4
-rw-r--r--user_guide_src/source/installation/upgrade_300.rst20
-rw-r--r--user_guide_src/source/installation/upgrading.rst2
-rw-r--r--user_guide_src/source/libraries/input.rst20
-rw-r--r--user_guide_src/source/libraries/sessions.rst6
-rw-r--r--user_guide_src/source/tutorial/news_section.rst2
-rw-r--r--user_guide_src/source/tutorial/static_pages.rst105
26 files changed, 285 insertions, 194 deletions
diff --git a/.travis.yml b/.travis.yml
index 4f560442b..258ad76f1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,7 @@ php:
- 5.4
- 5.5
- 5.6
+ - 7
- hhvm
env:
@@ -31,11 +32,14 @@ matrix:
allow_failures:
- php: 5.2
- php: hhvm
+ - php: 7
exclude:
- php: hhvm
env: DB=pgsql
- php: hhvm
env: DB=pdo/pgsql
+ - php: 7
+ env: DB=mysql
- php: 5.2
env: DB=sqlite
- php: 5.2
diff --git a/application/config/config.php b/application/config/config.php
index 7d5c24c84..cc1307ca9 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -203,7 +203,7 @@ $config['directory_trigger'] = 'd';
| 3 = Informational Messages
| 4 = All Messages
|
-| You can also pass in a array with threshold levels to show individual error types
+| You can also pass an array with threshold levels to show individual error types
|
| array(2) = Debug Messages, without Error Messages
|
@@ -404,6 +404,9 @@ $config['standardize_newlines'] = FALSE;
| Determines whether the XSS filter is always active when GET, POST or
| COOKIE data is encountered
|
+| WARNING: This feature is DEPRECATED and currently available only
+| for backwards compatibility purposes!
+|
*/
$config['global_xss_filtering'] = FALSE;
diff --git a/system/core/Common.php b/system/core/Common.php
index 7035c18ff..ee5a705b2 100644
--- a/system/core/Common.php
+++ b/system/core/Common.php
@@ -497,59 +497,58 @@ if ( ! function_exists('set_status_header'))
return;
}
- $stati = array(
- 200 => 'OK',
- 201 => 'Created',
- 202 => 'Accepted',
- 203 => 'Non-Authoritative Information',
- 204 => 'No Content',
- 205 => 'Reset Content',
- 206 => 'Partial Content',
-
- 300 => 'Multiple Choices',
- 301 => 'Moved Permanently',
- 302 => 'Found',
- 303 => 'See Other',
- 304 => 'Not Modified',
- 305 => 'Use Proxy',
- 307 => 'Temporary Redirect',
-
- 400 => 'Bad Request',
- 401 => 'Unauthorized',
- 403 => 'Forbidden',
- 404 => 'Not Found',
- 405 => 'Method Not Allowed',
- 406 => 'Not Acceptable',
- 407 => 'Proxy Authentication Required',
- 408 => 'Request Timeout',
- 409 => 'Conflict',
- 410 => 'Gone',
- 411 => 'Length Required',
- 412 => 'Precondition Failed',
- 413 => 'Request Entity Too Large',
- 414 => 'Request-URI Too Long',
- 415 => 'Unsupported Media Type',
- 416 => 'Requested Range Not Satisfiable',
- 417 => 'Expectation Failed',
- 422 => 'Unprocessable Entity',
-
- 500 => 'Internal Server Error',
- 501 => 'Not Implemented',
- 502 => 'Bad Gateway',
- 503 => 'Service Unavailable',
- 504 => 'Gateway Timeout',
- 505 => 'HTTP Version Not Supported'
- );
-
if (empty($code) OR ! is_numeric($code))
{
show_error('Status codes must be numeric', 500);
}
- is_int($code) OR $code = (int) $code;
-
if (empty($text))
{
+ is_int($code) OR $code = (int) $code;
+ $stati = array(
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 307 => 'Temporary Redirect',
+
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 422 => 'Unprocessable Entity',
+
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported'
+ );
+
if (isset($stati[$code]))
{
$text = $stati[$code];
diff --git a/system/core/Config.php b/system/core/Config.php
index a191a7727..b9af8e3b2 100644
--- a/system/core/Config.php
+++ b/system/core/Config.php
@@ -126,7 +126,6 @@ class CI_Config {
foreach (array($file, ENVIRONMENT.'/'.$file) as $location)
{
$file_path = $path.'config/'.$location.'.php';
-
if (in_array($file_path, $this->is_loaded, TRUE))
{
return TRUE;
@@ -165,14 +164,13 @@ class CI_Config {
$loaded = TRUE;
log_message('debug', 'Config file loaded: '.$file_path);
}
-
- if ($loaded === TRUE)
- {
- return TRUE;
- }
}
- if ($fail_gracefully === TRUE)
+ if ($loaded === TRUE)
+ {
+ return TRUE;
+ }
+ elseif ($fail_gracefully === TRUE)
{
return FALSE;
}
diff --git a/system/core/Input.php b/system/core/Input.php
index fae3b6c08..6be4b9a6c 100644
--- a/system/core/Input.php
+++ b/system/core/Input.php
@@ -55,7 +55,7 @@ class CI_Input {
*
* @var string
*/
- public $ip_address = FALSE;
+ protected $ip_address = FALSE;
/**
* Allow GET array flag
@@ -104,14 +104,28 @@ class CI_Input {
protected $headers = array();
/**
- * Input stream data
+ * Raw input stream data
+ *
+ * Holds a cache of php://input contents
+ *
+ * @var string
+ */
+ protected $_raw_input_stream;
+
+ /**
+ * Parsed input stream data
*
* Parsed from php://input at runtime
*
* @see CI_Input::input_stream()
* @var array
*/
- protected $_input_stream = NULL;
+ protected $_input_stream;
+
+ protected $security;
+ protected $uni;
+
+ // --------------------------------------------------------------------
/**
* Class constructor
@@ -313,7 +327,8 @@ class CI_Input {
// so we'll need to check if we have already done that first.
if ( ! is_array($this->_input_stream))
{
- parse_str(file_get_contents('php://input'), $this->_input_stream);
+ // $this->raw_input_stream will trigger __get().
+ parse_str($this->raw_input_stream, $this->_input_stream);
is_array($this->_input_stream) OR $this->_input_stream = array();
}
@@ -846,4 +861,27 @@ class CI_Input {
: strtolower($this->server('REQUEST_METHOD'));
}
+ // ------------------------------------------------------------------------
+
+ /**
+ * Magic __get()
+ *
+ * Allows read access to protected properties
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ if ($name === 'raw_input_stream')
+ {
+ isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input');
+ return $this->_raw_input_stream;
+ }
+ elseif ($name === 'ip_address')
+ {
+ return $this->ip_address;
+ }
+ }
+
}
diff --git a/system/core/Log.php b/system/core/Log.php
index 833316273..e8cb401f5 100644
--- a/system/core/Log.php
+++ b/system/core/Log.php
@@ -70,13 +70,6 @@ class CI_Log {
protected $_threshold = 1;
/**
- * Highest level of logging
- *
- * @var int
- */
- protected $_threshold_max = 0;
-
- /**
* Array of threshold levels to log
*
* @var array
@@ -139,7 +132,7 @@ class CI_Log {
}
elseif (is_array($config['log_threshold']))
{
- $this->_threshold = $this->_threshold_max;
+ $this->_threshold = 0;
$this->_threshold_array = array_flip($config['log_threshold']);
}
diff --git a/system/libraries/Cache/drivers/Cache_redis.php b/system/libraries/Cache/drivers/Cache_redis.php
index f2a41cc67..5236556d9 100644
--- a/system/libraries/Cache/drivers/Cache_redis.php
+++ b/system/libraries/Cache/drivers/Cache_redis.php
@@ -243,15 +243,13 @@ class CI_Cache_redis extends CI_Driver
*/
public function is_supported()
{
- if (extension_loaded('redis'))
- {
- return $this->_setup_redis();
- }
- else
+ if ( ! extension_loaded('redis'))
{
log_message('debug', 'The Redis extension must be loaded to use Redis cache.');
return FALSE;
}
+
+ return $this->_setup_redis();
}
// ------------------------------------------------------------------------
diff --git a/system/libraries/Cache/drivers/Cache_wincache.php b/system/libraries/Cache/drivers/Cache_wincache.php
index 528b2b9bf..9cc6ff016 100644
--- a/system/libraries/Cache/drivers/Cache_wincache.php
+++ b/system/libraries/Cache/drivers/Cache_wincache.php
@@ -194,7 +194,7 @@ class CI_Cache_wincache extends CI_Driver {
*/
public function is_supported()
{
- if ( ! extension_loaded('wincache'))
+ if ( ! extension_loaded('wincache') OR ! ini_get('wincache.ucenabled'))
{
log_message('debug', 'The Wincache PHP extension must be loaded to use Wincache Cache.');
return FALSE;
diff --git a/system/libraries/Session/drivers/Session_database_driver.php b/system/libraries/Session/drivers/Session_database_driver.php
index f496b4fe0..76c1cf34e 100644
--- a/system/libraries/Session/drivers/Session_database_driver.php
+++ b/system/libraries/Session/drivers/Session_database_driver.php
@@ -319,7 +319,7 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan
if ($this->_platform === 'mysql')
{
$arg = $session_id.($this->_config['match_ip'] ? '_'.$_SERVER['REMOTE_ADDR'] : '');
- if ($this->_db->query("SELECT GET_LOCK('".$arg."', 10) AS ci_session_lock")->row()->ci_session_lock)
+ if ($this->_db->query("SELECT GET_LOCK('".$arg."', 300) AS ci_session_lock")->row()->ci_session_lock)
{
$this->_lock = $arg;
return TRUE;
diff --git a/system/libraries/Session/drivers/Session_files_driver.php b/system/libraries/Session/drivers/Session_files_driver.php
index 5852277e8..74528e9d2 100644
--- a/system/libraries/Session/drivers/Session_files_driver.php
+++ b/system/libraries/Session/drivers/Session_files_driver.php
@@ -299,7 +299,9 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle
{
if ($this->close())
{
- return unlink($this->_file_path.$session_id) && $this->_cookie_destroy();
+ return file_exists($this->_file_path.$session_id)
+ ? (unlink($this->_file_path.$session_id) && $this->_cookie_destroy())
+ : TRUE;
}
elseif ($this->_file_path !== NULL)
{
diff --git a/system/libraries/Session/drivers/Session_memcached_driver.php b/system/libraries/Session/drivers/Session_memcached_driver.php
index f1a6e2400..938a612d9 100644
--- a/system/libraries/Session/drivers/Session_memcached_driver.php
+++ b/system/libraries/Session/drivers/Session_memcached_driver.php
@@ -204,7 +204,7 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa
if (isset($this->_lock_key))
{
- $this->_memcached->replace($this->_lock_key, time(), 5);
+ $this->_memcached->replace($this->_lock_key, time(), 300);
if ($this->_fingerprint !== ($fingerprint = md5($session_data)))
{
if ($this->_memcached->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration']))
@@ -299,34 +299,21 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa
{
if (isset($this->_lock_key))
{
- return $this->_memcached->replace($this->_lock_key, time(), 5);
+ return $this->_memcached->replace($this->_lock_key, time(), 300);
}
+ // 30 attempts to obtain a lock, in case another request already has it
$lock_key = $this->_key_prefix.$session_id.':lock';
- if ( ! ($ts = $this->_memcached->get($lock_key)))
- {
- if ( ! $this->_memcached->set($lock_key, TRUE, 5))
- {
- log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
- return FALSE;
- }
-
- $this->_lock_key = $lock_key;
- $this->_lock = TRUE;
- return TRUE;
- }
-
- // Another process has the lock, we'll try to wait for it to free itself ...
$attempt = 0;
- while ($attempt++ < 5)
+ do
{
- usleep(((time() - $ts) * 1000000) - 20000);
- if (($ts = $this->_memcached->get($lock_key)) < time())
+ if ($this->_memcached->get($lock_key))
{
+ sleep(1);
continue;
}
- if ( ! $this->_memcached->set($lock_key, time(), 5))
+ if ( ! $this->_memcached->set($lock_key, time(), 300))
{
log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
return FALSE;
@@ -335,8 +322,9 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa
$this->_lock_key = $lock_key;
break;
}
+ while ($attempt++ < 30);
- if ($attempt === 5)
+ if ($attempt === 30)
{
log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 5 attempts, aborting.');
return FALSE;
diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php
index 1cc4d75d7..1ce101daf 100644
--- a/system/libraries/Session/drivers/Session_redis_driver.php
+++ b/system/libraries/Session/drivers/Session_redis_driver.php
@@ -205,7 +205,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle
if (isset($this->_lock_key))
{
- $this->_redis->setTimeout($this->_lock_key, 5);
+ $this->_redis->setTimeout($this->_lock_key, 300);
if ($this->_fingerprint !== ($fingerprint = md5($session_data)))
{
if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration']))
@@ -272,7 +272,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle
{
if (isset($this->_redis, $this->_lock_key))
{
- if ($this->_redis->delete($this->_key_prefix.$session_id) !== 1)
+ if (($result = $this->_redis->delete($this->_key_prefix.$session_id)) !== 1)
{
log_message('debug', 'Session: Redis::delete() expected to return 1, got '.var_export($result, TRUE).' instead.');
}
@@ -313,40 +313,21 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle
{
if (isset($this->_lock_key))
{
- return $this->_redis->setTimeout($this->_lock_key, 5);
+ return $this->_redis->setTimeout($this->_lock_key, 300);
}
+ // 30 attempts to obtain a lock, in case another request already has it
$lock_key = $this->_key_prefix.$session_id.':lock';
- if (($ttl = $this->_redis->ttl($lock_key)) < 1)
- {
- if ( ! $this->_redis->setex($lock_key, 5, time()))
- {
- log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
- return FALSE;
- }
-
- $this->_lock_key = $lock_key;
-
- if ($ttl === -1)
- {
- log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.');
- }
-
- $this->_lock = TRUE;
- return TRUE;
- }
-
- // Another process has the lock, we'll try to wait for it to free itself ...
$attempt = 0;
- while ($attempt++ < 5)
+ do
{
- usleep(($ttl * 1000000) - 20000);
if (($ttl = $this->_redis->ttl($lock_key)) > 0)
{
+ sleep(1);
continue;
}
- if ( ! $this->_redis->setex($lock_key, 5, time()))
+ if ( ! $this->_redis->setex($lock_key, 300, time()))
{
log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
return FALSE;
@@ -355,12 +336,17 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle
$this->_lock_key = $lock_key;
break;
}
+ while ($attempt++ < 30);
- if ($attempt === 5)
+ if ($attempt === 30)
{
- log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 5 attempts, aborting.');
+ log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.');
return FALSE;
}
+ elseif ($ttl === -1)
+ {
+ log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.');
+ }
$this->_lock = TRUE;
return TRUE;
diff --git a/tests/codeigniter/core/Security_test.php b/tests/codeigniter/core/Security_test.php
index d967613b5..c96eecf02 100644
--- a/tests/codeigniter/core/Security_test.php
+++ b/tests/codeigniter/core/Security_test.php
@@ -126,5 +126,36 @@ class Security_test extends CI_TestCase {
$this->assertEquals('foo', $safe_filename);
}
+
+ // --------------------------------------------------------------------
+ public function test_strip_image_tags()
+ {
+ $imgtags = Array(
+ '<img src="smiley.gif" alt="Smiley face" height="42" width="42">',
+ '<img alt="Smiley face" height="42" width="42" src="smiley.gif">',
+ '<img src="http://www.w3schools.com/images/w3schools_green.jpg">',
+ '<img src="/img/sunset.gif" height="100%" width="100%">',
+ '<img src="mdn-logo-sm.png" alt="MD Logo" srcset="mdn-logo-HD.png 2x, mdn-logo-small.png 15w, mdn-banner-HD.png 100w 2x" />',
+ '<img sqrc="/img/sunset.gif" height="100%" width="100%">',
+ '<img srqc="/img/sunset.gif" height="100%" width="100%">',
+ '<img srcq="/img/sunset.gif" height="100%" width="100%">'
+ );
+
+ $urls = Array(
+ 'smiley.gif',
+ 'smiley.gif',
+ 'http://www.w3schools.com/images/w3schools_green.jpg',
+ '/img/sunset.gif',
+ 'mdn-logo-sm.png',
+ '<img sqrc="/img/sunset.gif" height="100%" width="100%">',
+ '<img srqc="/img/sunset.gif" height="100%" width="100%">',
+ '<img srcq="/img/sunset.gif" height="100%" width="100%">'
+ );
+
+ for($i = 0; $i < count($imgtags); $i++)
+ {
+ $this->assertEquals($urls[$i], $this->security->strip_image_tags($imgtags[$i]));
+ }
+ }
} \ No newline at end of file
diff --git a/tests/codeigniter/database/DB_driver_test.php b/tests/codeigniter/database/DB_driver_test.php
index c04c42b09..26416d3fc 100644
--- a/tests/codeigniter/database/DB_driver_test.php
+++ b/tests/codeigniter/database/DB_driver_test.php
@@ -6,7 +6,7 @@ class DB_driver_test extends CI_TestCase {
{
$config = Mock_Database_DB::config(DB_DRIVER);
sscanf(DB_DRIVER, '%[^/]/', $driver_name);
- $driver = $this->$driver_name($config[DB_DRIVER]);
+ $driver = $this->{$driver_name}($config[DB_DRIVER]);
$this->assertTrue($driver->initialize());
}
diff --git a/tests/codeigniter/database/DB_test.php b/tests/codeigniter/database/DB_test.php
index d5a9369e6..dc4fae986 100644
--- a/tests/codeigniter/database/DB_test.php
+++ b/tests/codeigniter/database/DB_test.php
@@ -26,6 +26,14 @@ class DB_test extends CI_TestCase {
{
$config = Mock_Database_DB::config(DB_DRIVER);
$connection = new Mock_Database_DB($config);
+
+ // E_DEPRECATED notices thrown by mysql_connect(), mysql_pconnect()
+ // on PHP 5.5+ cause the tests to fail
+ if (DB_DRIVER === 'mysql' && version_compare(PHP_VERSION, '5.5', '>='))
+ {
+ error_reporting(E_ALL & ~E_DEPRECATED);
+ }
+
$db = Mock_Database_DB::DB($connection->set_dsn(DB_DRIVER), TRUE);
$this->assertTrue($db instanceof CI_DB);
diff --git a/tests/mocks/core/input.php b/tests/mocks/core/input.php
index 0d1873849..40e27441f 100644
--- a/tests/mocks/core/input.php
+++ b/tests/mocks/core/input.php
@@ -38,4 +38,12 @@ class Mock_Core_Input extends CI_Input {
return FALSE;
}
+ public function __set($name, $value)
+ {
+ if ($name === 'ip_address')
+ {
+ $this->ip_address = $value;
+ }
+ }
+
} \ No newline at end of file
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 8f77f368f..ef3d2af39 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -480,6 +480,7 @@ Release Date: Not Released
- :doc:`Input Library <libraries/input>` changes include:
+ - Deprecated the ``$config['global_xss_filtering']`` setting.
- Added ``method()`` to retrieve ``$_SERVER['REQUEST_METHOD']``.
- Added support for arrays and network addresses (e.g. 192.168.1.1/24) for use with the *proxy_ips* setting.
- Added method ``input_stream()`` to aid in using **php://input** stream data such as one passed via PUT, DELETE and PATCH requests.
@@ -493,6 +494,7 @@ Release Date: Not Released
- Added an option for ``_clean_input_keys()`` to return FALSE instead of terminating the whole script.
- Deprecated the ``is_cli_request()`` method, it is now an alias for the new :php:func:`is_cli()` common function.
- Added an ``$xss_clean`` parameter to method ``user_agent()`` and removed the ``$user_agent`` property.
+ - Added property ``$raw_input_stream`` to access **php://input** data.
- :doc:`Common functions <general/common_functions>` changes include:
@@ -768,7 +770,7 @@ Bug fixes for 3.0
- Fixed a bug (#3161) - :doc:`Cache Library <libraries/caching>` methods `increment()`, `decrement()` didn't auto-create non-existent items when using redis and/or file storage.
- Fixed a bug (#3189) - :doc:`Parser Library <libraries/parser>` used double replacement on ``key->value`` pairs, exposing a potential template injection vulnerability.
- Fixed a bug (#3573) - :doc:`Email Library <libraries/email>` violated `RFC5321 <https://tools.ietf.org/rfc/rfc5321.txt>`_ by sending 'localhost.localdomain' as a hostname.
-- Fixed a bug (#3572) - :doc:`CI_Security::_remove_evil_attributes()` failed for large-sized inputs due to *pcre.backtrack_limit* and didn't properly match HTML tags.
+- Fixed a bug (#3572) - ``CI_Security::_remove_evil_attributes()`` failed for large-sized inputs due to *pcre.backtrack_limit* and didn't properly match HTML tags.
Version 2.2.1
=============
diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst
index fa1e90353..9b4694710 100644
--- a/user_guide_src/source/database/query_builder.rst
+++ b/user_guide_src/source/database/query_builder.rst
@@ -1221,7 +1221,7 @@ Class Reference
:param string $key: The field to search
:param array $values: The values searched on
- :param boolean $escape: Whether to escape values and identifiers
+ :param boolean $escape: Whether to escape identifiers
:returns: DB_query_builder instance
:rtype: object
@@ -1232,7 +1232,7 @@ Class Reference
:param string $key: The field to search
:param array $values: The values searched on
- :param boolean $escape: Whether to escape values and identifiers
+ :param boolean $escape: Whether to escape identifiers
:returns: DB_query_builder instance
:rtype: object
@@ -1243,7 +1243,7 @@ Class Reference
:param string $key: Name of field to examine
:param array $values: Array of target values
- :param boolean $escape: Whether to escape values and identifiers
+ :param boolean $escape: Whether to escape identifiers
:returns: DB_query_builder instance
:rtype: object
@@ -1254,7 +1254,7 @@ Class Reference
:param string $key: Name of field to examine
:param array $values: Array of target values
- :param boolean $escape: Whether to escape values and identifiers
+ :param boolean $escape: Whether to escape identifiers
:returns: DB_query_builder instance
:rtype: object
diff --git a/user_guide_src/source/database/results.rst b/user_guide_src/source/database/results.rst
index a22c2e8c3..ac44566d3 100644
--- a/user_guide_src/source/database/results.rst
+++ b/user_guide_src/source/database/results.rst
@@ -102,7 +102,7 @@ You can also add a second String parameter, which is the name of a class
to instantiate the row with::
$query = $this->db->query("SELECT * FROM users LIMIT 1;");
- $query->row(0, 'User');
+ $row = $query->row(0, 'User');
echo $row->name; // access attributes
echo $row->reverse_name(); // or methods defined on the 'User' class
@@ -431,4 +431,4 @@ Class Reference
:rtype: array
Returns an array containing the field names in the
- result set. \ No newline at end of file
+ result set.
diff --git a/user_guide_src/source/general/security.rst b/user_guide_src/source/general/security.rst
index 0c58f96b4..efc821f2b 100644
--- a/user_guide_src/source/general/security.rst
+++ b/user_guide_src/source/general/security.rst
@@ -133,6 +133,10 @@ with that. Please read below.
provides them for you as long as you're running at least PHP version
5.3.7 (and if you don't meet that requirement - please, upgrade).
+ If you're one of the really unlucky people who can't even upgrade to a
+ more recent PHP version, use `hash_pbkdf() <http://php.net/hash_pbkdf2>`,
+ which we also provide in our compatibility layer.
+
- DO NOT ever display or send a password in plain-text format!
Even to the password's owner, if you need a "Forgotten password"
diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst
index 73ed0f4c3..2f806cccf 100644
--- a/user_guide_src/source/installation/upgrade_300.rst
+++ b/user_guide_src/source/installation/upgrade_300.rst
@@ -1,5 +1,5 @@
#############################
-Upgrading from 2.2.1 to 3.0.0
+Upgrading from 2.2.x to 3.0.0
#############################
.. note:: These upgrade notes are for a version that is yet to be released.
@@ -551,6 +551,22 @@ PHP's native ``hash()`` function. It is deprecated and scheduled for removal in
.. note:: This function is still available, but you're strongly encouraged to remove its usage sooner
rather than later.
+The $config['global_xss_filtering'] setting
+===========================================
+
+As already explained above, XSS filtering should not be done on input data,
+but on output instead. Therefore, the ``$config['global_xss_filtering']``,
+which automatically filters *input* data, is considered a bad practice and
+is now deprecated.
+
+Instead, you should manually escape any user-provided data via the
+:php:func:`xss_clean()` function when you need to output it, or use a
+library like `HTML Purifier <http://htmlpurifier.org/>`_ that does that
+for you.
+
+.. note:: The setting is still available, but you're strongly encouraged to
+ remove its usage sooner rather than later.
+
File helper read_file()
=======================
@@ -795,7 +811,7 @@ It is now deprecated and scheduled for removal in CodeIgniter 3.1+.
sooner rather than later.
***********************************************************
-Step 18: Check your usage of Text helper highlight_phrase()
+Step 20: Check your usage of Text helper highlight_phrase()
***********************************************************
The default HTML tag used by :doc:`Text Helper <../helpers/text_helper>` function
diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst
index ab36e9bfd..89e90e714 100644
--- a/user_guide_src/source/installation/upgrading.rst
+++ b/user_guide_src/source/installation/upgrading.rst
@@ -8,7 +8,7 @@ upgrading from.
.. toctree::
:titlesonly:
- Upgrading from 2.2.1 to 3.0.0 <upgrade_300>
+ Upgrading from 2.2.x to 3.0.0 <upgrade_300>
Upgrading from 2.2.0 to 2.2.1 <upgrade_221>
Upgrading from 2.1.4 to 2.2.0 <upgrade_220>
Upgrading from 2.1.3 to 2.1.4 <upgrade_214>
diff --git a/user_guide_src/source/libraries/input.rst b/user_guide_src/source/libraries/input.rst
index 967f69d13..d9c6c2dd1 100644
--- a/user_guide_src/source/libraries/input.rst
+++ b/user_guide_src/source/libraries/input.rst
@@ -53,6 +53,10 @@ this::
Please refer to the :doc:`Security class <security>` documentation for
information on using XSS Filtering in your application.
+.. important:: The 'global_xss_filtering' setting is DEPRECATED and kept
+ solely for backwards-compatibility purposes. XSS escaping should
+ be performed on *output*, not *input*!
+
*******************
Accessing form data
*******************
@@ -91,8 +95,14 @@ the ``$_POST`` array, because it will always exist and you can try
and access multiple variables without caring that you might only have
one shot at all of the POST data.
-CodeIgniter will take care of that for you, and you can access data
-from the **php://input** stream at any time, just by calling the
+CodeIgniter will take care of that for you, and you can read the data
+from the **php://input** stream at any time, just by using the
+``$raw_input_stream`` property::
+
+ $this->input->raw_input_stream;
+
+Additionally if the input stream is form-encoded like $_POST you can
+access its values by calling the
``input_stream()`` method::
$this->input->input_stream('key');
@@ -114,6 +124,12 @@ Class Reference
.. php:class:: CI_Input
+ .. attribute:: $raw_input_stream
+
+ Read only property that will return php://input data as is.
+
+ The property can be read multiple times.
+
.. php:method:: post([$index = NULL[, $xss_clean = NULL]])
:param mixed $index: POST parameter name
diff --git a/user_guide_src/source/libraries/sessions.rst b/user_guide_src/source/libraries/sessions.rst
index 5a1b90537..51ecc03bd 100644
--- a/user_guide_src/source/libraries/sessions.rst
+++ b/user_guide_src/source/libraries/sessions.rst
@@ -632,8 +632,7 @@ Redis Driver
.. note:: Since Redis doesn't have a locking mechanism exposed, locks for
this driver are emulated by a separate value that is kept for up
- to 5 seconds. You may experience issues if your page loads take
- longer than that!
+ to 300 seconds.
Redis is a storage engine typically used for caching and popular because
of its high performance, which is also probably your reason to use the
@@ -670,8 +669,7 @@ Memcached Driver
.. note:: Since Memcache doesn't have a locking mechanism exposed, locks
for this driver are emulated by a separate value that is kept for
- up to 5 seconds. You may experience issues if your page loads take
- longer than that!
+ up to 300 seconds.
The 'memcached' driver is very similar to the 'redis' one in all of its
properties, except perhaps for availability, because PHP's `Memcached
diff --git a/user_guide_src/source/tutorial/news_section.rst b/user_guide_src/source/tutorial/news_section.rst
index 80938de32..f436b2510 100644
--- a/user_guide_src/source/tutorial/news_section.rst
+++ b/user_guide_src/source/tutorial/news_section.rst
@@ -151,7 +151,7 @@ and add the next piece of code.
<div class="main">
<?php echo $news_item['text'] ?>
</div>
- <p><a href="news/<?php echo $news_item['slug'] ?>">View article</a></p>
+ <p><a href="<?php echo $news_item['slug'] ?>">View article</a></p>
<?php endforeach ?>
diff --git a/user_guide_src/source/tutorial/static_pages.rst b/user_guide_src/source/tutorial/static_pages.rst
index 8ba0486c1..53f286473 100644
--- a/user_guide_src/source/tutorial/static_pages.rst
+++ b/user_guide_src/source/tutorial/static_pages.rst
@@ -11,12 +11,16 @@ static pages. A controller is simply a class that helps delegate work.
It is the glue of your web application.
For example, when a call is made to:
-``http://example.com/news/latest/10`` We might imagine that there is a
-controller named "news". The method being called on news would be
-"latest". The news method's job could be to grab 10 news items, and
-render them on the page. Very often in MVC, you'll see URL patterns that
-match:
-``http://example.com/[controller-class]/[controller-method]/[arguments]``
+
+ http://example.com/news/latest/10
+
+We might imagine that there is a controller named "news". The method
+being called on news would be "latest". The news method's job could be to
+grab 10 news items, and render them on the page. Very often in MVC,
+you'll see URL patterns that match:
+
+ http://example.com/[controller-class]/[controller-method]/[arguments]
+
As URL schemes become more complex, this may change. But for now, this
is all we will need to know.
@@ -25,15 +29,13 @@ code.
::
- <?php
- class Pages extends CI_Controller {
+ <?php
+ class Pages extends CI_Controller {
- public function view($page = 'home')
- {
-
- }
-
- }
+ public function view($page = 'home')
+ {
+ }
+ }
You have created a class named "pages", with a view method that accepts
one argument named $page. The pages class is extending the
@@ -56,13 +58,13 @@ following code.
::
- <html>
- <head>
- <title>CodeIgniter Tutorial</title>
- </head>
- <body>
+ <html>
+ <head>
+ <title>CodeIgniter Tutorial</title>
+ </head>
+ <body>
- <h1>CodeIgniter Tutorial</h1>
+ <h1>CodeIgniter Tutorial</h1>
The header contains the basic HTML code that you'll want to display
before loading the main view, together with a heading. It will also
@@ -72,16 +74,16 @@ includes the following code:
::
- <em>&copy; 2014</em>
- </body>
- </html>
+ <em>&copy; 2014</em>
+ </body>
+ </html>
Adding logic to the controller
------------------------------
-Earlier you set up a controller with a view() method. The method accepts
-one parameter, which is the name of the page to be loaded. The static
-page templates will be located in the application/views/pages/
+Earlier you set up a controller with a ``view()`` method. The method
+accepts one parameter, which is the name of the page to be loaded. The
+static page templates will be located in the application/views/pages/
directory.
In that directory, create two files named home.php and about.php. Within
@@ -93,43 +95,40 @@ page actually exists:
::
- <?php
- public function view($page = 'home')
- {
-
- if ( ! file_exists(APPPATH.'/views/pages/'.$page.'.php'))
- {
- // Whoops, we don't have a page for that!
- show_404();
- }
-
- $data['title'] = ucfirst($page); // Capitalize the first letter
-
- $this->load->view('templates/header', $data);
- $this->load->view('pages/'.$page, $data);
- $this->load->view('templates/footer', $data);
-
- }
+ public function view($page = 'home')
+ {
+ if ( ! file_exists(APPPATH.'/views/pages/'.$page.'.php'))
+ {
+ // Whoops, we don't have a page for that!
+ show_404();
+ }
+
+ $data['title'] = ucfirst($page); // Capitalize the first letter
+
+ $this->load->view('templates/header', $data);
+ $this->load->view('pages/'.$page, $data);
+ $this->load->view('templates/footer', $data);
+ }
Now, when the page does exist, it is loaded, including the header and
footer, and displayed to the user. If the page doesn't exist, a "404
Page not found" error is shown.
The first line in this method checks whether the page actually exists.
-PHP's native file\_exists() function is used to check whether the file
-is where it's expected to be. show\_404() is a built-in CodeIgniter
+PHP's native ``file_exists()`` function is used to check whether the file
+is where it's expected to be. ``show_404()`` is a built-in CodeIgniter
function that renders the default error page.
-In the header template, the $title variable was used to customize the
+In the header template, the ``$title`` variable was used to customize the
page title. The value of title is defined in this method, but instead of
assigning the value to a variable, it is assigned to the title element
in the $data array.
The last thing that has to be done is loading the views in the order
-they should be displayed. The second parameter in the view() method is
-used to pass values to the view. Each value in the $data array is
+they should be displayed. The second parameter in the ``view()`` method is
+used to pass values to the view. Each value in the ``$data`` array is
assigned to a variable with the name of its key. So the value of
-$data['title'] in the controller is equivalent to $title in the view.
+``$data['title']`` in the controller is equivalent to $title in the view.
Routing
-------
@@ -149,8 +148,8 @@ all other code that sets any element in the $route array.
::
- $route['default_controller'] = 'pages/view';
- $route['(:any)'] = 'pages/view/$1';
+ $route['default_controller'] = 'pages/view';
+ $route['(:any)'] = 'pages/view/$1';
CodeIgniter reads its routing rules from top to bottom and routes the
request to the first matching rule. Each rule is a regular expression
@@ -163,8 +162,8 @@ More information about routing can be found in the URI Routing
`documentation <../general/routing.html>`_.
Here, the second rule in the $routes array matches **any** request using
-the wildcard string (:any). and passes the parameter to the view()
+the wildcard string (:any). and passes the parameter to the ``view()``
method of the pages class.
-Now visit index.php/about. Did it get routed correctly to the view()
+Now visit index.php/about. Did it get routed correctly to the ``view()``
method in the pages controller? Awesome!