diff options
227 files changed, 4177 insertions, 2132 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..51fea4174 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# This file tells which files and directories should be ignored and +# NOT downloaded when using composer to pull down a project with +# the --prefer-dist option selected. Used to remove development +# specific files so user has a clean download. + +# git files +.gitattributes export-ignore +# .gitignore + +# helper config files +.travis.yml export-ignore +phpdoc.dist.xml export-ignore + +# Misc other files +readme.rst + +# They don't want all of our tests... +tests/codeigniter/ export-ignore +tests/travis/ export-ignore + +# User Guide Source Files +user_guide_src diff --git a/.gitignore b/.gitignore index 9edcfca6d..5982f9bad 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,15 @@ user_guide_src/cilexer/build/* user_guide_src/cilexer/dist/* user_guide_src/cilexer/pycilexer.egg-info/* /vendor/ -/nbproject/
\ No newline at end of file + +# IDE Files +#------------------------- +/nbproject/ +.idea/* + +## Sublime Text cache files +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache +*.sublime-workspace +*.sublime-project diff --git a/.travis.yml b/.travis.yml index 3a823f7b0..adc60d759 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ php: - 5.4 - 5.5 - 5.6 + - 7 - hhvm env: @@ -29,12 +30,16 @@ script: phpunit -d zend.enable_gc=0 -d date.timezone=UTC --coverage-text --confi 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 @@ -43,4 +48,5 @@ matrix: branches: only: - develop + - 3.0-stable - /^feature\/.+$/ diff --git a/application/cache/index.html b/application/cache/index.html index c942a79ce..b702fbc39 100644 --- a/application/cache/index.html +++ b/application/cache/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/config/autoload.php b/application/config/autoload.php index 72f855c8b..4bc6bf0ad 100644 --- a/application/config/autoload.php +++ b/application/config/autoload.php @@ -39,16 +39,15 @@ defined('BASEPATH') OR exit('No direct script access allowed'); | $autoload['packages'] = array(APPPATH.'third_party', '/usr/local/shared'); | */ - $autoload['packages'] = array(); - /* | ------------------------------------------------------------------- | Auto-load Libraries | ------------------------------------------------------------------- -| These are the classes located in the system/libraries folder -| or in your application/libraries folder. +| These are the classes located in system/libraries/ or your +| application/libraries/ directory, with the addition of the +| 'database' library, which is somewhat of a special case. | | Prototype: | @@ -59,26 +58,23 @@ $autoload['packages'] = array(); | | $autoload['libraries'] = array('user_agent' => 'ua'); */ - $autoload['libraries'] = array(); - /* | ------------------------------------------------------------------- | Auto-load Drivers | ------------------------------------------------------------------- -| These classes are located in the system/libraries folder or in your -| application/libraries folder within their own subdirectory. They +| These classes are located in system/libraries/ or in your +| application/libraries/ directory, but are also placed inside their +| own subdirectory and they extend the CI_Driver_Library class. They | offer multiple interchangeable driver options. | | Prototype: | | $autoload['drivers'] = array('cache'); */ - $autoload['drivers'] = array(); - /* | ------------------------------------------------------------------- | Auto-load Helper Files @@ -87,10 +83,8 @@ $autoload['drivers'] = array(); | | $autoload['helper'] = array('url', 'file'); */ - $autoload['helper'] = array(); - /* | ------------------------------------------------------------------- | Auto-load Config files @@ -103,10 +97,8 @@ $autoload['helper'] = array(); | config files. Otherwise, leave it blank. | */ - $autoload['config'] = array(); - /* | ------------------------------------------------------------------- | Auto-load Language files @@ -119,10 +111,8 @@ $autoload['config'] = array(); | "codeigniter_lang.php" would be referenced as array('codeigniter'); | */ - $autoload['language'] = array(); - /* | ------------------------------------------------------------------- | Auto-load Models @@ -136,5 +126,4 @@ $autoload['language'] = array(); | | $autoload['model'] = array('first_model' => 'first'); */ - $autoload['model'] = array(); diff --git a/application/config/config.php b/application/config/config.php index 1e399590b..4f8f81406 100644 --- a/application/config/config.php +++ b/application/config/config.php @@ -11,10 +11,16 @@ defined('BASEPATH') OR exit('No direct script access allowed'); | | http://example.com/ | -| If this is not set then CodeIgniter will try guess the protocol, domain -| and path to your installation. However, you should always configure this -| explicitly and never rely on auto-guessing, especially in production -| environments. +| WARNING: You MUST set this value! +| +| If it is not set, then CodeIgniter will try guess the protocol and path +| your installation, but due to security concerns the hostname will be set +| to $_SERVER['SERVER_ADDR'] if available, or localhost otherwise. +| The auto-detection mechanism exists only for convenience during +| development and MUST NOT be used in production! +| +| If you need to allow multiple domains, remember that this file is still +| a PHP script and you can easily do that on your own. | */ $config['base_url'] = ''; @@ -37,17 +43,16 @@ $config['index_page'] = 'index.php'; |-------------------------------------------------------------------------- | | This item determines which server global should be used to retrieve the -| URI string. The default setting of 'AUTO' works for most servers. +| URI string. The default setting of 'REQUEST_URI' works for most servers. | If your links do not seem to work, try one of the other delicious flavors: | -| 'AUTO' Default - auto detects -| 'CLI' or 'argv' Uses $_SERVER['argv'] (for php-cli only) -| 'PATH_INFO' Uses $_SERVER['PATH_INFO'] -| 'REQUEST_URI' Uses $_SERVER['REQUEST_URI'] -| 'QUERY_STRING' Uses $_SERVER['QUERY_STRING'] +| 'REQUEST_URI' Uses $_SERVER['REQUEST_URI'] +| 'QUERY_STRING' Uses $_SERVER['QUERY_STRING'] +| 'PATH_INFO' Uses $_SERVER['PATH_INFO'] | +| WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded! */ -$config['uri_protocol'] = 'AUTO'; +$config['uri_protocol'] = 'REQUEST_URI'; /* |-------------------------------------------------------------------------- @@ -59,7 +64,6 @@ $config['uri_protocol'] = 'AUTO'; | | http://codeigniter.com/user_guide/general/urls.html */ - $config['url_suffix'] = ''; /* @@ -156,7 +160,6 @@ $config['composer_autoload'] = FALSE; */ $config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-'; - /* |-------------------------------------------------------------------------- | Enable Query Strings @@ -193,8 +196,6 @@ $config['directory_trigger'] = 'd'; | Error Logging Threshold |-------------------------------------------------------------------------- | -| If you have enabled error logging, you can set an error threshold to -| determine what gets logged. Threshold options are: | You can enable error logging by setting a threshold over zero. The | threshold determines what gets logged. Threshold options are: | @@ -204,7 +205,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 | @@ -289,8 +290,15 @@ $config['cache_path'] = ''; | Cache Include Query String |-------------------------------------------------------------------------- | -| Set this to TRUE if you want to use different cache files depending on the -| URL query string. Please be aware this might result in numerous cache files. +| Whether to take the URL query string into consideration when generating +| output cache files. Valid options are: +| +| FALSE = Disabled +| TRUE = Enabled, take all query parameters into account. +| Please be aware that this may result in numerous cache +| files generated for the same page over and over again. +| array('q') = Enabled, but only take into account the specified list +| of query parameters. | */ $config['cache_query_string'] = FALSE; @@ -328,9 +336,11 @@ $config['encryption_key'] = ''; | | 'sess_save_path' | -| The location to save sessions to, driver dependant. +| The location to save sessions to, driver dependent. | | For the 'files' driver, it's a path to a writable directory. +| WARNING: Only absolute paths are supported! +| | For the 'database' driver, it's a table name. | Please read up the manual for the format with other session drivers. | @@ -340,10 +350,19 @@ $config['encryption_key'] = ''; | | Whether to match the user's IP address when reading the session data. | +| WARNING: If you're using the database driver, don't forget to update +| your session table's PRIMARY KEY when changing this setting. +| | 'sess_time_to_update' | | How many seconds between CI regenerating the session ID. | +| 'sess_regenerate_destroy' +| +| Whether to destroy session data associated with the old session ID +| when auto-regenerating the session ID. When set to FALSE, the data +| will be later deleted by the garbage collector. +| | Other session cookie settings are shared with the rest of the application, | except for 'cookie_prefix' and 'cookie_httponly', which are ignored here. | @@ -354,6 +373,7 @@ $config['sess_expiration'] = 7200; $config['sess_save_path'] = NULL; $config['sess_match_ip'] = FALSE; $config['sess_time_to_update'] = 300; +$config['sess_regenerate_destroy'] = FALSE; /* |-------------------------------------------------------------------------- @@ -382,7 +402,7 @@ $config['cookie_httponly'] = FALSE; |-------------------------------------------------------------------------- | | Determines whether to standardize newline characters in input data, -| meaning to replace \r\n, \r, \n occurences with the PHP_EOL value. +| meaning to replace \r\n, \r, \n occurrences with the PHP_EOL value. | | This is particularly useful for portability between UNIX-based OSes, | (usually \n) and Windows (\r\n). @@ -398,6 +418,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; @@ -466,10 +489,11 @@ $config['time_reference'] = 'local'; | can rewrite the tags on-the-fly, enabling you to utilize that syntax | in your view files. Options are TRUE or FALSE (boolean) | +| Note: You need to have eval() enabled for this to work. +| */ $config['rewrite_short_tags'] = FALSE; - /* |-------------------------------------------------------------------------- | Reverse Proxy IPs diff --git a/application/config/constants.php b/application/config/constants.php index 01096c74a..e8d2c00ea 100644 --- a/application/config/constants.php +++ b/application/config/constants.php @@ -3,6 +3,18 @@ defined('BASEPATH') OR exit('No direct script access allowed'); /* |-------------------------------------------------------------------------- +| Display Debug backtrace +|-------------------------------------------------------------------------- +| +| If set to TRUE, a backtrace will be displayed along with php errors. If +| error_reporting is disabled, the backtrace will not display, regardless +| of this setting +| +*/ +defined('SHOW_DEBUG_BACKTRACE') OR define('SHOW_DEBUG_BACKTRACE', TRUE); + +/* +|-------------------------------------------------------------------------- | File and Directory Modes |-------------------------------------------------------------------------- | @@ -14,10 +26,10 @@ defined('BASEPATH') OR exit('No direct script access allowed'); | always be used to set the mode correctly. | */ -define('FILE_READ_MODE', 0644); -define('FILE_WRITE_MODE', 0666); -define('DIR_READ_MODE', 0755); -define('DIR_WRITE_MODE', 0755); +defined('FILE_READ_MODE') OR define('FILE_READ_MODE', 0644); +defined('FILE_WRITE_MODE') OR define('FILE_WRITE_MODE', 0666); +defined('DIR_READ_MODE') OR define('DIR_READ_MODE', 0755); +defined('DIR_WRITE_MODE') OR define('DIR_WRITE_MODE', 0755); /* |-------------------------------------------------------------------------- @@ -27,27 +39,14 @@ define('DIR_WRITE_MODE', 0755); | These modes are used when working with fopen()/popen() | */ - -define('FOPEN_READ', 'rb'); -define('FOPEN_READ_WRITE', 'r+b'); -define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care -define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care -define('FOPEN_WRITE_CREATE', 'ab'); -define('FOPEN_READ_WRITE_CREATE', 'a+b'); -define('FOPEN_WRITE_CREATE_STRICT', 'xb'); -define('FOPEN_READ_WRITE_CREATE_STRICT', 'x+b'); - -/* -|-------------------------------------------------------------------------- -| Display Debug backtrace -|-------------------------------------------------------------------------- -| -| If set to TRUE, a backtrace will be displayed along with php errors. If -| error_reporting is disabled, the backtrace will not display, regardless -| of this setting -| -*/ -define('SHOW_DEBUG_BACKTRACE', TRUE); +defined('FOPEN_READ') OR define('FOPEN_READ', 'rb'); +defined('FOPEN_READ_WRITE') OR define('FOPEN_READ_WRITE', 'r+b'); +defined('FOPEN_WRITE_CREATE_DESTRUCTIVE') OR define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care +defined('FOPEN_READ_WRITE_CREATE_DESCTRUCTIVE') OR define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care +defined('FOPEN_WRITE_CREATE') OR define('FOPEN_WRITE_CREATE', 'ab'); +defined('FOPEN_READ_WRITE_CREATE') OR define('FOPEN_READ_WRITE_CREATE', 'a+b'); +defined('FOPEN_WRITE_CREATE_STRICT') OR define('FOPEN_WRITE_CREATE_STRICT', 'xb'); +defined('FOPEN_READ_WRITE_CREATE_STRICT') OR define('FOPEN_READ_WRITE_CREATE_STRICT', 'x+b'); /* |-------------------------------------------------------------------------- @@ -74,13 +73,13 @@ define('SHOW_DEBUG_BACKTRACE', TRUE); | http://tldp.org/LDP/abs/html/exitcodes.html | */ -define('EXIT_SUCCESS', 0); // no errors -define('EXIT_ERROR', 1); // generic error -define('EXIT_CONFIG', 3); // configuration error -define('EXIT_UNKNOWN_FILE', 4); // file not found -define('EXIT_UNKNOWN_CLASS', 5); // unknown class -define('EXIT_UNKNOWN_METHOD', 6); // unknown class member -define('EXIT_USER_INPUT', 7); // invalid user input -define('EXIT_DATABASE', 8); // database error -define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code -define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code +defined('EXIT_SUCCESS') OR define('EXIT_SUCCESS', 0); // no errors +defined('EXIT_ERROR') OR define('EXIT_ERROR', 1); // generic error +defined('EXIT_CONFIG') OR define('EXIT_CONFIG', 3); // configuration error +defined('EXIT_UNKNOWN_FILE') OR define('EXIT_UNKNOWN_FILE', 4); // file not found +defined('EXIT_UNKNOWN_CLASS') OR define('EXIT_UNKNOWN_CLASS', 5); // unknown class +defined('EXIT_UNKNOWN_METHOD') OR define('EXIT_UNKNOWN_METHOD', 6); // unknown class member +defined('EXIT_USER_INPUT') OR define('EXIT_USER_INPUT', 7); // invalid user input +defined('EXIT_DATABASE') OR define('EXIT_DATABASE', 8); // database error +defined('EXIT__AUTO_MIN') OR define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code +defined('EXIT__AUTO_MAX') OR define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code diff --git a/application/config/database.php b/application/config/database.php index 5ee2af438..bf9857fff 100644 --- a/application/config/database.php +++ b/application/config/database.php @@ -39,11 +39,22 @@ defined('BASEPATH') OR exit('No direct script access allowed'); | 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. | ['swap_pre'] A default table prefix that should be swapped with the dbprefix -| ['autoinit'] Whether or not to automatically initialize the database. | ['encrypt'] Whether or not to use an encrypted connection. +| +| 'mysql' (deprecated), 'sqlsrv' and 'pdo/sqlsrv' drivers accept TRUE/FALSE +| 'mysqli' and 'pdo/mysql' drivers accept an array with the following options: +| +| 'ssl_key' - Path to the private key file +| 'ssl_cert' - Path to the public key certificate file +| 'ssl_ca' - Path to the certificate authority file +| 'ssl_capath' - Path to a directory containing trusted CA certificats in PEM format +| 'ssl_cipher' - List of *allowed* ciphers to be used for the encryption, separated by colons (':') +| 'ssl_verify' - TRUE/FALSE; Whether verify the server certificate or not ('mysqli' only) +| | ['compress'] Whether or not to use client compression (MySQL only) | ['stricton'] TRUE/FALSE - forces 'Strict Mode' connections | - good for ensuring strict SQL while developing +| ['ssl_options'] Used to set various SSL options that can be used when making SSL connections. | ['failover'] array - A array with 0 or more data for connections if the main should fail. | ['save_queries'] TRUE/FALSE - Whether to "save" all executed queries. | NOTE: Disabling this will also effectively disable both @@ -59,7 +70,6 @@ defined('BASEPATH') OR exit('No direct script access allowed'); | The $query_builder variables lets you determine whether or not to load | the query builder class. */ - $active_group = 'default'; $query_builder = TRUE; @@ -72,13 +82,12 @@ $db['default'] = array( 'dbdriver' => 'mysqli', 'dbprefix' => '', 'pconnect' => FALSE, - 'db_debug' => TRUE, + 'db_debug' => (ENVIRONMENT !== 'production'), 'cache_on' => FALSE, 'cachedir' => '', 'char_set' => 'utf8', 'dbcollat' => 'utf8_general_ci', 'swap_pre' => '', - 'autoinit' => TRUE, 'encrypt' => FALSE, 'compress' => FALSE, 'stricton' => FALSE, diff --git a/application/config/foreign_chars.php b/application/config/foreign_chars.php index d02dea958..ac406e3d4 100644 --- a/application/config/foreign_chars.php +++ b/application/config/foreign_chars.php @@ -56,6 +56,7 @@ $foreign_characters = array( '/ś|ŝ|ş|ș|š|ſ|σ|ς|с/' => 's', '/Ț|Ţ|Ť|Ŧ|τ|Т/' => 'T', '/ț|ţ|ť|ŧ|т/' => 't', + '/Þ|þ/' => 'th', '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|Ũ|Ủ|Ụ|Ừ|Ứ|Ữ|Ử|Ự|У/' => 'U', '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|υ|ύ|ϋ|ủ|ụ|ừ|ứ|ữ|ử|ự|у/' => 'u', '/Ý|Ÿ|Ŷ|Υ|Ύ|Ϋ|Ỳ|Ỹ|Ỷ|Ỵ|Й/' => 'Y', diff --git a/application/config/index.html b/application/config/index.html index c942a79ce..b702fbc39 100644 --- a/application/config/index.html +++ b/application/config/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/config/migration.php b/application/config/migration.php index 083bf287c..4b585a65c 100644 --- a/application/config/migration.php +++ b/application/config/migration.php @@ -21,12 +21,12 @@ $config['migration_enabled'] = FALSE; | Migration file names may be based on a sequential identifier or on | a timestamp. Options are: | -| 'sequential' = Default migration naming (001_add_blog.php) +| 'sequential' = Sequential migration naming (001_add_blog.php) | 'timestamp' = Timestamp migration naming (20121031104401_add_blog.php) | Use timestamp format YYYYMMDDHHIISS. | -| If this configuration value is missing the Migration library defaults -| to 'sequential' for backward compatibility. +| Note: If this configuration value is missing the Migration library +| defaults to 'sequential' for backward compatibility with CI2. | */ $config['migration_type'] = 'timestamp'; diff --git a/application/config/mimes.php b/application/config/mimes.php index 5e43773a8..aa3b1836a 100644 --- a/application/config/mimes.php +++ b/application/config/mimes.php @@ -9,7 +9,6 @@ defined('BASEPATH') OR exit('No direct script access allowed'); | Upload class to help identify allowed file types. | */ - return array( 'hqx' => array('application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'), 'cpt' => 'application/mac-compactpro', @@ -135,7 +134,7 @@ return array( 'rsa' => 'application/x-pkcs7', 'cer' => array('application/pkix-cert', 'application/x-x509-ca-cert'), '3g2' => 'video/3gpp2', - '3gp' => 'video/3gp', + '3gp' => array('video/3gp', 'video/3gpp'), 'mp4' => 'video/mp4', 'm4a' => 'audio/x-m4a', 'f4v' => 'video/mp4', @@ -160,5 +159,8 @@ return array( 'wma' => array('audio/x-ms-wma', 'video/x-ms-asf'), 'jar' => array('application/java-archive', 'application/x-java-application', 'application/x-jar', 'application/x-compressed'), 'svg' => array('image/svg+xml', 'application/xml', 'text/xml'), - 'vcf' => 'text/x-vcard' + 'vcf' => 'text/x-vcard', + 'srt' => array('text/srt', 'text/plain'), + 'vtt' => array('text/vtt', 'text/plain'), + 'ico' => array('image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon') ); diff --git a/application/config/routes.php b/application/config/routes.php index a98c6d122..599c79b0e 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -43,8 +43,8 @@ defined('BASEPATH') OR exit('No direct script access allowed'); | This is not exactly a route, but allows you to automatically route | controller and method names that contain dashes. '-' isn't a valid | class or method name character, so it requires translation. -| When you set this option to TRUE, it will replace ALL dashes in the -| controller and method URI segments. +| When you set this option to TRUE, it will replace ALL dashes with +| underscores in the controller and method URI segments. | | Examples: my-controller/index -> my_controller/index | my-controller/my-method -> my_controller/my_method diff --git a/application/config/smileys.php b/application/config/smileys.php index 1428d68bc..1eeba4776 100644 --- a/application/config/smileys.php +++ b/application/config/smileys.php @@ -13,7 +13,6 @@ defined('BASEPATH') OR exit('No direct script access allowed'); | http://codeigniter.com/user_guide/helpers/smiley_helper.html | */ - $smileys = array( // smiley image name width height alt diff --git a/application/config/user_agents.php b/application/config/user_agents.php index 68d785365..1129dbacd 100644 --- a/application/config/user_agents.php +++ b/application/config/user_agents.php @@ -10,8 +10,8 @@ defined('BASEPATH') OR exit('No direct script access allowed'); | 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( + 'windows nt 10.0' => 'Windows 10', 'windows nt 6.3' => 'Windows 8.1', 'windows nt 6.2' => 'Windows 8', 'windows nt 6.1' => 'Windows 7', @@ -61,6 +61,7 @@ $platforms = array( $browsers = array( 'OPR' => 'Opera', 'Flock' => 'Flock', + 'Edge' => 'Spartan', 'Chrome' => 'Chrome', // Opera 10+ always reports Opera/9.80 and appends Version/<real version> to the user agent string 'Opera.*?Version' => 'Opera', @@ -197,7 +198,7 @@ $robots = array( 'bingbot' => 'Bing', 'slurp' => 'Inktomi Slurp', 'yahoo' => 'Yahoo', - 'askjeeves' => 'AskJeeves', + 'ask jeeves' => 'Ask Jeeves', 'fastcrawler' => 'FastCrawler', 'infoseek' => 'InfoSeek Robot 1.0', 'lycos' => 'Lycos', diff --git a/application/controllers/index.html b/application/controllers/index.html index c942a79ce..b702fbc39 100644 --- a/application/controllers/index.html +++ b/application/controllers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/core/index.html b/application/core/index.html index c942a79ce..b702fbc39 100644 --- a/application/core/index.html +++ b/application/core/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/helpers/index.html b/application/helpers/index.html index c942a79ce..b702fbc39 100644 --- a/application/helpers/index.html +++ b/application/helpers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/hooks/index.html b/application/hooks/index.html index c942a79ce..b702fbc39 100644 --- a/application/hooks/index.html +++ b/application/hooks/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/index.html b/application/index.html index c942a79ce..b702fbc39 100644 --- a/application/index.html +++ b/application/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/language/english/index.html b/application/language/english/index.html index c942a79ce..b702fbc39 100644 --- a/application/language/english/index.html +++ b/application/language/english/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/language/index.html b/application/language/index.html index c942a79ce..b702fbc39 100644 --- a/application/language/index.html +++ b/application/language/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/libraries/index.html b/application/libraries/index.html index c942a79ce..b702fbc39 100644 --- a/application/libraries/index.html +++ b/application/libraries/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/logs/index.html b/application/logs/index.html index c942a79ce..b702fbc39 100644 --- a/application/logs/index.html +++ b/application/logs/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/models/index.html b/application/models/index.html index c942a79ce..b702fbc39 100644 --- a/application/models/index.html +++ b/application/models/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/third_party/index.html b/application/third_party/index.html index c942a79ce..b702fbc39 100644 --- a/application/third_party/index.html +++ b/application/third_party/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/views/errors/cli/error_exception.php b/application/views/errors/cli/error_exception.php index 75d7f0fad..efa6a66d1 100644 --- a/application/views/errors/cli/error_exception.php +++ b/application/views/errors/cli/error_exception.php @@ -1,25 +1,21 @@ -<?php -defined('BASEPATH') OR exit('No direct script access allowed'); -?> +<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?> An uncaught Exception was encountered -Type: <?php echo get_class($exception); ?> -Message: <?php echo $message; ?> -Filename: <?php echo $exception->getFile(); ?> +Type: <?php echo get_class($exception), "\n"; ?> +Message: <?php echo $message, "\n"; ?> +Filename: <?php echo $exception->getFile(), "\n"; ?> Line Number: <?php echo $exception->getLine(); ?> <?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?> Backtrace: - <?php foreach ($exception->getTrace() as $error): ?> - <?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?> +<?php foreach ($exception->getTrace() as $error): ?> +<?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?> + File: <?php echo $error['file'], "\n"; ?> + Line: <?php echo $error['line'], "\n"; ?> + Function: <?php echo $error['function'], "\n\n"; ?> +<?php endif ?> +<?php endforeach ?> - File: <?php echo $error['file']; ?> - Line: <?php echo $error['line']; ?> - Function: <?php echo $error['function']; ?> - - <?php endif ?> - - <?php endforeach ?> -<?php endif ?>
\ No newline at end of file +<?php endif ?> diff --git a/application/views/errors/cli/error_php.php b/application/views/errors/cli/error_php.php index fec91e54f..8a24b6491 100644 --- a/application/views/errors/cli/error_php.php +++ b/application/views/errors/cli/error_php.php @@ -1,25 +1,21 @@ -<?php -defined('BASEPATH') OR exit('No direct script access allowed'); -?> +<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?> A PHP Error was encountered -Severity: <?php echo $severity;?> -Message: <?php echo $message;?> -Filename: <?php echo $filepath;?> -Line Number: <?php echo $line;?> +Severity: <?php echo $severity, "\n"; ?> +Message: <?php echo $message, "\n"; ?> +Filename: <?php echo $filepath, "\n"; ?> +Line Number: <?php echo $line; ?> <?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?> Backtrace: - <?php foreach (debug_backtrace() as $error): ?> - <?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?> +<?php foreach (debug_backtrace() as $error): ?> +<?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?> + File: <?php echo $error['file'], "\n"; ?> + Line: <?php echo $error['line'], "\n"; ?> + Function: <?php echo $error['function'], "\n\n"; ?> +<?php endif ?> +<?php endforeach ?> - File: <?php echo $error['file'];?> - Line: <?php echo $error['line'];?> - Function: <?php echo $error['function'];?> - - <?php endif ?> - - <?php endforeach ?> -<?php endif ?>
\ No newline at end of file +<?php endif ?> diff --git a/application/views/errors/cli/index.html b/application/views/errors/cli/index.html index c942a79ce..b702fbc39 100644 --- a/application/views/errors/cli/index.html +++ b/application/views/errors/cli/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/views/errors/html/index.html b/application/views/errors/html/index.html index c942a79ce..b702fbc39 100644 --- a/application/views/errors/html/index.html +++ b/application/views/errors/html/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/views/errors/index.html b/application/views/errors/index.html index c942a79ce..b702fbc39 100644 --- a/application/views/errors/index.html +++ b/application/views/errors/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/application/views/index.html b/application/views/index.html index c942a79ce..b702fbc39 100644 --- a/application/views/index.html +++ b/application/views/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/composer.json b/composer.json index 4ff60c57e..0653a7885 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,15 @@ { - "description" : "The CodeIgniter framework", - "name" : "codeigniter/framework", + "description": "The CodeIgniter framework", + "name": "codeigniter/framework", + "type": "project", + "homepage": "http://codeigniter.com", "license": "MIT", + "support": { + "forum": "http://forum.codeigniter.com/", + "wiki": "https://github.com/bcit-ci/CodeIgniter/wiki", + "irc": "irc://irc.freenode.net/codeigniter", + "source": "https://github.com/bcit-ci/CodeIgniter" + }, "require": { "php": ">=5.2.4" }, diff --git a/readme.rst b/readme.rst index dd59fd8c1..2e35d7223 100644 --- a/readme.rst +++ b/readme.rst @@ -54,13 +54,17 @@ Resources ********* - `User Guide <http://www.codeigniter.com/docs>`_ +- `Language File Translations <https://github.com/bcit-ci/codeigniter3-translations>`_ - `Community Forums <http://forum.codeigniter.com/>`_ - `Community Wiki <https://github.com/bcit-ci/CodeIgniter/wiki>`_ - `Community IRC <http://www.codeigniter.com/irc>`_ +Report security issues to our `Security Panel <mailto:security@codeigniter.com>`_ +or via our `page on HackerOne <https://hackerone.com/codeigniter>`_, thank you. + *************** Acknowledgement *************** -The EllisLab team and The Reactor Engineers would like to thank all the +The CodeIgniter team would like to thank EllisLab, all the contributors to the CodeIgniter project and you, the CodeIgniter user.
\ No newline at end of file diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php index d830c1829..fece65bde 100644 --- a/system/core/CodeIgniter.php +++ b/system/core/CodeIgniter.php @@ -55,7 +55,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @var string * */ - define('CI_VERSION', '3.0-dev'); + define('CI_VERSION', '3.1.0-dev'); /* * ------------------------------------------------------ diff --git a/system/core/Common.php b/system/core/Common.php index 9f509745f..3ab98cf6d 100644 --- a/system/core/Common.php +++ b/system/core/Common.php @@ -181,7 +181,7 @@ if ( ! function_exists('load_class')) // Did we find the class? if ($name === FALSE) { - // Note: We use exit() rather then show_error() in order to avoid a + // Note: We use exit() rather than show_error() in order to avoid a // self-referencing loop with the Exceptions class set_status_header(503); echo 'Unable to locate the specified class: '.$class.'.php'; @@ -492,59 +492,67 @@ if ( ! function_exists('set_status_header')) */ function set_status_header($code = 200, $text = '') { - $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 (is_cli()) + { + return; + } 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( + 100 => 'Continue', + 101 => 'Switching Protocols', + + 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', + 402 => 'Payment Required', + 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]; @@ -669,7 +677,7 @@ if ( ! function_exists('_shutdown_handler')) * of CodeIgniter.php. The main reason we use this is to simulate * a complete custom exception handler. * - * E_STRICT is purposivly neglected because such events may have + * E_STRICT is purposively neglected because such events may have * been caught. Duplication or none? None is preferred for now. * * @link http://insomanic.me.uk/post/229851073/php-trick-catching-fatal-errors-e-error-with-a @@ -737,9 +745,19 @@ if ( ! function_exists('html_escape')) */ function html_escape($var, $double_encode = TRUE) { + if (empty($var)) + { + return $var; + } + if (is_array($var)) { - return array_map('html_escape', $var, array_fill(0, count($var), $double_encode)); + foreach (array_keys($var) as $key) + { + $var[$key] = html_escape($var[$key], $double_encode); + } + + return $var; } return htmlspecialchars($var, ENT_QUOTES, config_item('charset'), $double_encode); @@ -820,19 +838,9 @@ if ( ! function_exists('function_usable')) { if ( ! isset($_suhosin_func_blacklist)) { - if (extension_loaded('suhosin')) - { - $_suhosin_func_blacklist = explode(',', trim(ini_get('suhosin.executor.func.blacklist'))); - - if ( ! in_array('eval', $_suhosin_func_blacklist, TRUE) && ini_get('suhosin.executor.disable_eval')) - { - $_suhosin_func_blacklist[] = 'eval'; - } - } - else - { - $_suhosin_func_blacklist = array(); - } + $_suhosin_func_blacklist = extension_loaded('suhosin') + ? explode(',', trim(ini_get('suhosin.executor.func.blacklist'))) + : array(); } return ! in_array($function_name, $_suhosin_func_blacklist, TRUE); diff --git a/system/core/Config.php b/system/core/Config.php index a191a7727..0264776f9 100644 --- a/system/core/Config.php +++ b/system/core/Config.php @@ -88,11 +88,9 @@ class CI_Config { // Set the base_url automatically if none was provided if (empty($this->config['base_url'])) { - // The regular expression is only a basic validation for a valid "Host" header. - // It's not exhaustive, only checks for valid characters. - if (isset($_SERVER['HTTP_HOST']) && preg_match('/^((\[[0-9a-f:]+\])|(\d{1,3}(\.\d{1,3}){3})|[a-z0-9\-\.]+)(:\d+)?$/i', $_SERVER['HTTP_HOST'])) + if (isset($_SERVER['SERVER_ADDR'])) { - $base_url = (is_https() ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'] + $base_url = (is_https() ? 'https' : 'http').'://'.$_SERVER['SERVER_ADDR'] .substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], basename($_SERVER['SCRIPT_FILENAME']))); } else @@ -123,10 +121,9 @@ class CI_Config { foreach ($this->_config_paths as $path) { - foreach (array($file, ENVIRONMENT.'/'.$file) as $location) + foreach (array($file, ENVIRONMENT.DIRECTORY_SEPARATOR.$file) as $location) { $file_path = $path.'config/'.$location.'.php'; - if (in_array($file_path, $this->is_loaded, TRUE)) { return TRUE; @@ -165,14 +162,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; } @@ -240,7 +236,15 @@ class CI_Config { if (isset($protocol)) { - $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + // For protocol-relative links + if ($protocol === '') + { + $base_url = substr($base_url, strpos($base_url, '//')); + } + else + { + $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + } } if (empty($uri)) @@ -295,7 +299,15 @@ class CI_Config { if (isset($protocol)) { - $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + // For protocol-relative links + if ($protocol === '') + { + $base_url = substr($base_url, strpos($base_url, '//')); + } + else + { + $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + } } return $base_url.ltrim($this->_uri_string($uri), '/'); diff --git a/system/core/Exceptions.php b/system/core/Exceptions.php index fc25f57e6..d8f62c0fe 100644 --- a/system/core/Exceptions.php +++ b/system/core/Exceptions.php @@ -187,7 +187,7 @@ class CI_Exceptions { // -------------------------------------------------------------------- - public function show_exception(Exception $exception) + public function show_exception($exception) { $templates_path = config_item('error_views_path'); if (empty($templates_path)) diff --git a/system/core/Hooks.php b/system/core/Hooks.php index 08479b133..3b4fb2250 100644 --- a/system/core/Hooks.php +++ b/system/core/Hooks.php @@ -46,7 +46,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Libraries * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/encryption.html + * @link http://codeigniter.com/user_guide/general/hooks.html */ class CI_Hooks { diff --git a/system/core/Input.php b/system/core/Input.php index 72425c1c1..4e7a4e95e 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 @@ -139,6 +153,12 @@ class CI_Input { // Sanitize global arrays $this->_sanitize_globals(); + // CSRF Protection check + if ($this->_enable_csrf === TRUE && ! is_cli()) + { + $this->security->csrf_verify(); + } + log_message('info', 'Input Class Initialized'); } @@ -313,7 +333,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(); } @@ -475,9 +496,9 @@ class CI_Input { ) ); - for ($i = 0; $i < 8; $i++) + for ($j = 0; $j < 8; $j++) { - $ip[$i] = intval($ip[$i], 16); + $ip[$j] = intval($ip[$j], 16); } $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b'; @@ -585,7 +606,7 @@ class CI_Input { { $_GET = array(); } - elseif (is_array($_GET) && count($_GET) > 0) + elseif (is_array($_GET)) { foreach ($_GET as $key => $val) { @@ -594,7 +615,7 @@ class CI_Input { } // Clean $_POST Data - if (is_array($_POST) && count($_POST) > 0) + if (is_array($_POST)) { foreach ($_POST as $key => $val) { @@ -603,7 +624,7 @@ class CI_Input { } // Clean $_COOKIE Data - if (is_array($_COOKIE) && count($_COOKIE) > 0) + if (is_array($_COOKIE)) { // Also get rid of specially treated cookies that might be set by a server // or silly application, that are of no use to a CI application anyway @@ -632,12 +653,6 @@ class CI_Input { // Sanitize PHP_SELF $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']); - // CSRF Protection check - if ($this->_enable_csrf === TRUE && ! is_cli()) - { - $this->security->csrf_verify(); - } - log_message('debug', 'Global POST, GET and COOKIE data sanitized'); } @@ -667,7 +682,7 @@ class CI_Input { /* We strip slashes if magic quotes is on to keep things consistent NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and - it will probably not exist in future versions at all. + it will probably not exist in future versions at all. */ if ( ! is_php('5.4') && get_magic_quotes_gpc()) { @@ -702,7 +717,7 @@ class CI_Input { * only named with alpha-numeric text and a few other items. * * @param string $str Input string - * @param string $fatal Whether to terminate script exection + * @param bool $fatal Whether to terminate script exection * or to return FALSE if an invalid * key is encountered * @return string|bool @@ -784,19 +799,27 @@ class CI_Input { */ public function get_request_header($index, $xss_clean = FALSE) { - if (empty($this->headers)) + static $headers; + + if ( ! isset($headers)) { - $this->request_headers(); + empty($this->headers) && $this->request_headers(); + foreach ($this->headers as $key => $value) + { + $headers[strtolower($key)] = $value; + } } - if ( ! isset($this->headers[$index])) + $index = strtolower($index); + + if ( ! isset($headers[$index])) { return NULL; } return ($xss_clean === TRUE) - ? $this->security->xss_clean($this->headers[$index]) - : $this->headers[$index]; + ? $this->security->xss_clean($headers[$index]) + : $headers[$index]; } // -------------------------------------------------------------------- @@ -821,7 +844,7 @@ class CI_Input { * Test to see if a request was made from the command line. * * @deprecated 3.0.0 Use is_cli() instead - * @return bool + * @return bool */ public function is_cli_request() { @@ -846,4 +869,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/Loader.php b/system/core/Loader.php index b2eeb3b1d..18e4c5287 100644 --- a/system/core/Loader.php +++ b/system/core/Loader.php @@ -272,7 +272,7 @@ class CI_Loader { $CI =& get_instance(); if (isset($CI->$name)) { - show_error('The model name you are loading is the name of a resource that is already being used: '.$name); + throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name); } if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE)) @@ -290,24 +290,38 @@ class CI_Loader { load_class('Model', 'core'); } - $model = ucfirst(strtolower($model)); - - foreach ($this->_ci_model_paths as $mod_path) + $model = ucfirst($model); + if ( ! class_exists($model)) { - if ( ! file_exists($mod_path.'models/'.$path.$model.'.php')) + foreach ($this->_ci_model_paths as $mod_path) { - continue; - } + if ( ! file_exists($mod_path.'models/'.$path.$model.'.php')) + { + continue; + } - require_once($mod_path.'models/'.$path.$model.'.php'); + require_once($mod_path.'models/'.$path.$model.'.php'); + if ( ! class_exists($model, FALSE)) + { + throw new RuntimeException($mod_path."models/".$path.$model.".php exists, but doesn't declare class ".$model); + } - $this->_ci_models[] = $name; - $CI->$name = new $model(); - return $this; + break; + } + + if ( ! class_exists($model, FALSE)) + { + throw new RuntimeException('Unable to locate the model you have specified: '.$model); + } + } + elseif ( ! is_subclass_of($model, 'CI_Model')) + { + throw new RuntimeException("Class ".$model." already exists and doesn't extend CI_Model"); } - // couldn't find the model - show_error('Unable to locate the model you have specified: '.$model); + $this->_ci_models[] = $name; + $CI->$name = new $model(); + return $this; } // -------------------------------------------------------------------- @@ -500,7 +514,7 @@ class CI_Loader { * * Clears the cached variables. * - * @return object + * @return CI_Loader */ public function clear_vars() { @@ -905,7 +919,7 @@ class CI_Loader { // 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') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE && function_usable('eval')) + if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE) { echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace('<?=', '<?php echo ', file_get_contents($_ci_path)))); } @@ -1079,17 +1093,26 @@ class CI_Loader { log_message('debug', $library_name.' class already loaded. Second attempt ignored.'); return; } - elseif (file_exists(APPPATH.'libraries/'.$file_path.$library_name.'.php')) + + $paths = $this->_ci_library_paths; + array_pop($paths); // BASEPATH + array_pop($paths); // APPPATH (needs to be the first path checked) + array_unshift($paths, APPPATH); + + foreach ($paths as $path) { - // Override - include_once(APPPATH.'libraries/'.$file_path.$library_name.'.php'); - if (class_exists($prefix.$library_name, FALSE)) - { - return $this->_ci_init_library($library_name, $prefix, $params, $object_name); - } - else + if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php')) { - log_message('debug', APPPATH.'libraries/'.$file_path.$library_name.'.php exists, but does not declare '.$prefix.$library_name); + // Override + include_once($path); + if (class_exists($prefix.$library_name, FALSE)) + { + return $this->_ci_init_library($library_name, $prefix, $params, $object_name); + } + else + { + log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name); + } } } @@ -1097,16 +1120,20 @@ class CI_Loader { // Check for extensions $subclass = config_item('subclass_prefix').$library_name; - if (file_exists(APPPATH.'libraries/'.$file_path.$subclass.'.php')) + foreach ($paths as $path) { - include_once(APPPATH.'libraries/'.$file_path.$subclass.'.php'); - if (class_exists($subclass, FALSE)) - { - $prefix = config_item('subclass_prefix'); - } - else + if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php')) { - log_message('debug', APPPATH.'libraries/'.$file_path.$subclass.'.php exists, but does not declare '.$subclass); + include_once($path); + if (class_exists($subclass, FALSE)) + { + $prefix = config_item('subclass_prefix'); + break; + } + else + { + log_message('debug', $path.' exists, but does not declare '.$subclass); + } } } @@ -1294,10 +1321,7 @@ class CI_Loader { } // Load all other libraries - foreach ($autoload['libraries'] as $item) - { - $this->library($item); - } + $this->library($autoload['libraries']); } // Autoload models 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/core/Output.php b/system/core/Output.php index 02f66936c..76c1329d2 100644 --- a/system/core/Output.php +++ b/system/core/Output.php @@ -556,9 +556,16 @@ class CI_Output { .$CI->config->item('index_page') .$CI->uri->uri_string(); - if ($CI->config->item('cache_query_string') && ! empty($_SERVER['QUERY_STRING'])) + if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) { - $uri .= '?'.$_SERVER['QUERY_STRING']; + if (is_array($cache_query_string)) + { + $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); + } + else + { + $uri .= '?'.$_SERVER['QUERY_STRING']; + } } $cache_path .= md5($uri); @@ -646,9 +653,16 @@ class CI_Output { // 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; - if ($CFG->item('cache_query_string') && ! empty($_SERVER['QUERY_STRING'])) + if (($cache_query_string = $CFG->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) { - $uri .= '?'.$_SERVER['QUERY_STRING']; + if (is_array($cache_query_string)) + { + $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); + } + else + { + $uri .= '?'.$_SERVER['QUERY_STRING']; + } } $filepath = $cache_path.md5($uri); @@ -674,7 +688,7 @@ class CI_Output { $cache_info = unserialize($match[1]); $expire = $cache_info['expire']; - $last_modified = filemtime($cache_path); + $last_modified = filemtime($filepath); // Has the file expired? if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path)) @@ -729,13 +743,20 @@ class CI_Output { { $uri = $CI->uri->uri_string(); - if ($CI->config->item('cache_query_string') && ! empty($_SERVER['QUERY_STRING'])) + if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) { - $uri .= '?'.$_SERVER['QUERY_STRING']; + if (is_array($cache_query_string)) + { + $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); + } + else + { + $uri .= '?'.$_SERVER['QUERY_STRING']; + } } } - $cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').$uri); + $cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').ltrim($uri, '/')); if ( ! @unlink($cache_path)) { diff --git a/system/core/Router.php b/system/core/Router.php index eb3da2285..a84be1f1d 100644 --- a/system/core/Router.php +++ b/system/core/Router.php @@ -83,7 +83,7 @@ class CI_Router { * * @var string */ - public $directory = ''; + public $directory; /** * Default controller (and method if specific) @@ -105,7 +105,7 @@ class CI_Router { /** * Enable query strings flag * - * Determines wether to use GET parameters or segment URIs + * Determines whether to use GET parameters or segment URIs * * @var bool */ @@ -126,25 +126,16 @@ class CI_Router { $this->uri =& load_class('URI', 'core'); $this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE); + + // If a directory override is configured, it has to be set before any dynamic routing logic + is_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']); $this->_set_routing(); // Set any routing overrides that may exist in the main index file if (is_array($routing)) { - if (isset($routing['directory'])) - { - $this->set_directory($routing['directory']); - } - - if ( ! empty($routing['controller'])) - { - $this->set_class($routing['controller']); - } - - if ( ! empty($routing['function'])) - { - $this->set_method($routing['function']); - } + empty($routing['controller']) OR $this->set_class($routing['controller']); + empty($routing['function']) OR $this->set_method($routing['function']); } log_message('info', 'Router Class Initialized'); @@ -162,17 +153,44 @@ class CI_Router { */ protected function _set_routing() { + // Load the routes.php file. It would be great if we could + // skip this for enable_query_strings = TRUE, but then + // default_controller would be empty ... + if (file_exists(APPPATH.'config/routes.php')) + { + include(APPPATH.'config/routes.php'); + } + + if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/routes.php'); + } + + // Validate & get reserved routes + if (isset($route) && is_array($route)) + { + isset($route['default_controller']) && $this->default_controller = $route['default_controller']; + isset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes']; + unset($route['default_controller'], $route['translate_uri_dashes']); + $this->routes = $route; + } + // Are query strings enabled in the config file? Normally CI doesn't utilize query strings // since URI segments are more search-engine friendly, but they can optionally be used. // If this feature is enabled, we will gather the directory/class/method a little differently if ($this->enable_query_strings) { - $_d = $this->config->item('directory_trigger'); - $_d = isset($_GET[$_d]) ? trim($_GET[$_d], " \t\n\r\0\x0B/") : ''; - if ($_d !== '') + // If the directory is set at this time, it means an override exists, so skip the checks + if ( ! isset($this->directory)) { - $this->uri->filter_uri($_d); - $this->set_directory($_d); + $_d = $this->config->item('directory_trigger'); + $_d = isset($_GET[$_d]) ? trim($_GET[$_d], " \t\n\r\0\x0B/") : ''; + + if ($_d !== '') + { + $this->uri->filter_uri($_d); + $this->set_directory($_d); + } } $_c = trim($this->config->item('controller_trigger')); @@ -203,26 +221,6 @@ class CI_Router { return; } - // Load the routes.php file. - if (file_exists(APPPATH.'config/routes.php')) - { - include(APPPATH.'config/routes.php'); - } - - if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php')) - { - include(APPPATH.'config/'.ENVIRONMENT.'/routes.php'); - } - - // Validate & get reserved routes - if (isset($route) && is_array($route)) - { - isset($route['default_controller']) && $this->default_controller = $route['default_controller']; - isset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes']; - unset($route['default_controller'], $route['translate_uri_dashes']); - $this->routes = $route; - } - // Is there anything to parse? if ($this->uri->uri_string !== '') { @@ -333,6 +331,8 @@ class CI_Router { protected function _validate_request($segments) { $c = count($segments); + $directory_override = isset($this->directory); + // Loop through our segments and return as soon as a controller // is found or when such a directory doesn't exist while ($c-- > 0) @@ -340,7 +340,10 @@ class CI_Router { $test = $this->directory .ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]); - if ( ! file_exists(APPPATH.'controllers/'.$test.'.php') && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0])) + if ( ! file_exists(APPPATH.'controllers/'.$test.'.php') + && $directory_override === FALSE + && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]) + ) { $this->set_directory(array_shift($segments), TRUE); continue; @@ -371,29 +374,13 @@ class CI_Router { // Get HTTP verb $http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli'; - // Is there a literal match? If so we're done - if (isset($this->routes[$uri])) - { - // Check default routes format - if (is_string($this->routes[$uri])) - { - $this->_set_request(explode('/', $this->routes[$uri])); - return; - } - // Is there a matching http verb? - elseif (is_array($this->routes[$uri]) && isset($this->routes[$uri][$http_verb])) - { - $this->_set_request(explode('/', $this->routes[$uri][$http_verb])); - return; - } - } - // Loop through the route array looking for wildcards foreach ($this->routes as $key => $val) { - // Check if route format is using http verb + // Check if route format is using HTTP verbs if (is_array($val)) { + $val = array_change_key_case($val, CASE_LOWER); if (isset($val[$http_verb])) { $val = $val[$http_verb]; @@ -493,7 +480,7 @@ class CI_Router { * Set directory name * * @param string $dir Directory name - * @param bool $appent Whether we're appending rather then setting the full value + * @param bool $append Whether we're appending rather than setting the full value * @return void */ public function set_directory($dir, $append = FALSE) diff --git a/system/core/Security.php b/system/core/Security.php index 7c18c7406..36dea4cf2 100644 --- a/system/core/Security.php +++ b/system/core/Security.php @@ -275,7 +275,7 @@ class CI_Security { $secure_cookie, config_item('cookie_httponly') ); - log_message('info', 'CRSF cookie sent'); + log_message('info', 'CSRF cookie sent'); return $this; } @@ -436,7 +436,7 @@ class CI_Security { $words = array( 'javascript', 'expression', 'vbscript', 'jscript', 'wscript', 'vbs', 'script', 'base64', 'applet', 'alert', 'document', - 'write', 'cookie', 'window', 'confirm', 'prompt' + 'write', 'cookie', 'window', 'confirm', 'prompt', 'eval' ); foreach ($words as $word) @@ -480,12 +480,8 @@ class CI_Security { } } while ($original !== $str); - unset($original); - // Remove evil attributes such as style, onclick and xmlns - $str = $this->_remove_evil_attributes($str, $is_image); - /* * Sanitize naughty HTML elements * @@ -495,8 +491,29 @@ class CI_Security { * So this: <blink> * Becomes: <blink> */ - $naughty = 'alert|prompt|confirm|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|button|select|isindex|layer|link|meta|keygen|object|plaintext|style|script|textarea|title|math|video|svg|xml|xss'; - $str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str); + $pattern = '#' + .'<((?<slash>/*\s*)(?<tagName>[a-z0-9]+)(?=[^a-z0-9]|$)' // tag start and name, followed by a non-tag character + .'[^\s\042\047a-z0-9>/=]*' // a valid attribute character immediately after the tag would count as a separator + // optional attributes + .'(?<attributes>(?:[\s\042\047/=]*' // non-attribute characters, excluding > (tag close) for obvious reasons + .'[^\s\042\047>/=]+' // attribute characters + // optional attribute-value + .'(?:\s*=' // attribute-value separator + .'(?:[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*))' // single, double or non-quoted value + .')?' // end optional attribute-value group + .')*)' // end optional attributes group + .'[^>]*)(?<closeTag>\>)?#isS'; + + // Note: It would be nice to optimize this for speed, BUT + // only matching the naughty elements here results in + // false positives and in turn - vulnerabilities! + do + { + $old_str = $str; + $str = preg_replace_callback($pattern, array($this, '_sanitize_naughty_html'), $str); + } + while ($old_str !== $str); + unset($old_str); /* * Sanitize naughty scripting elements @@ -510,9 +527,11 @@ class CI_Security { * For example: eval('some code') * Becomes: eval('some code') */ - $str = preg_replace('#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', - '\\1\\2(\\3)', - $str); + $str = preg_replace( + '#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', + '\\1\\2(\\3)', + $str + ); // Final clean up // This adds a bit of extra precaution in case @@ -639,7 +658,7 @@ class CI_Security { $str_compare = $str; // Decode standard entities, avoiding false positives - if ($c = preg_match_all('/&[a-z]{2,}(?![a-z;])/i', $str, $matches)) + if (preg_match_all('/&[a-z]{2,}(?![a-z;])/i', $str, $matches)) { if ( ! isset($_entities)) { @@ -656,7 +675,7 @@ class CI_Security { { $_entities[':'] = ':'; $_entities['('] = '('; - $_entities[')'] = '&rpar'; + $_entities[')'] = ')'; $_entities["\n"] = '&newline;'; $_entities["\t"] = '&tab;'; } @@ -664,11 +683,11 @@ class CI_Security { $replace = array(); $matches = array_unique(array_map('strtolower', $matches[0])); - for ($i = 0; $i < $c; $i++) + foreach ($matches as &$match) { - if (($char = array_search($matches[$i].';', $_entities, TRUE)) !== FALSE) + if (($char = array_search($match.';', $_entities, TRUE)) !== FALSE) { - $replace[$matches[$i]] = $char; + $replace[$match] = $char; } } @@ -750,86 +769,92 @@ class CI_Security { // -------------------------------------------------------------------- /** - * Remove Evil HTML Attributes (like event handlers and style) - * - * It removes the evil attribute and either: - * - * - Everything up until a space. For example, everything between the pipes: - * - * <code> - * <a |style=document.write('hello');alert('world');| class=link> - * </code> - * - * - Everything inside the quotes. For example, everything between the pipes: + * Sanitize Naughty HTML * - * <code> - * <a |style="document.write('hello'); alert('world');"| class="link"> - * </code> + * Callback method for xss_clean() to remove naughty HTML elements. * - * @param string $str The string to check - * @param bool $is_image Whether the input is an image - * @return string The string with the evil attributes removed + * @used-by CI_Security::xss_clean() + * @param array $matches + * @return string */ - protected function _remove_evil_attributes($str, $is_image) + protected function _sanitize_naughty_html($matches) { - $evil_attributes = array('on\w*', 'style', 'xmlns', 'formaction', 'form', 'xlink:href'); + static $naughty_tags = array( + 'alert', 'prompt', 'confirm', 'applet', 'audio', 'basefont', 'base', 'behavior', 'bgsound', + 'blink', 'body', 'embed', 'expression', 'form', 'frameset', 'frame', 'head', 'html', 'ilayer', + 'iframe', 'input', 'button', 'select', 'isindex', 'layer', 'link', 'meta', 'keygen', 'object', + 'plaintext', 'style', 'script', 'textarea', 'title', 'math', 'video', 'svg', 'xml', 'xss' + ); - if ($is_image === TRUE) + static $evil_attributes = array( + 'on\w+', 'style', 'xmlns', 'formaction', 'form', 'xlink:href', 'FSCommand', 'seekSegmentTime' + ); + + // First, escape unclosed tags + if (empty($matches['closeTag'])) { - /* - * Adobe Photoshop puts XML metadata into JFIF images, - * including namespacing, so we have to allow this for images. - */ - unset($evil_attributes[array_search('xmlns', $evil_attributes)]); + return '<'.$matches[1]; } + // Is the element that we caught naughty? If so, escape it + elseif (in_array(strtolower($matches['tagName']), $naughty_tags, TRUE)) + { + return '<'.$matches[1].'>'; + } + // For other tags, see if their attributes are "evil" and strip those + elseif (isset($matches['attributes'])) + { + // We'll store the already fitlered attributes here + $attributes = array(); - do { - $count = 0; - $attribs = array(); + // Attribute-catching pattern + $attributes_pattern = '#' + .'(?<name>[^\s\042\047>/=]+)' // attribute characters + // optional attribute-value + .'(?:\s*=(?<value>[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*)))' // attribute-value separator + .'#i'; - // find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes) - preg_match_all('/(?<!\w)('.implode('|', $evil_attributes).')\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is', $str, $matches, PREG_SET_ORDER); + // Blacklist pattern for evil attribute names + $is_evil_pattern = '#^('.implode('|', $evil_attributes).')$#i'; - foreach ($matches as $attr) + // Each iteration filters a single attribute + do { - $attribs[] = preg_quote($attr[0], '/'); - } + // Strip any non-alpha characters that may preceed an attribute. + // Browsers often parse these incorrectly and that has been a + // of numerous XSS issues we've had. + $matches['attributes'] = preg_replace('#^[^a-z]+#i', '', $matches['attributes']); - // find occurrences of illegal attribute strings without quotes - preg_match_all('/(?<!\w)('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER); + if ( ! preg_match($attributes_pattern, $matches['attributes'], $attribute, PREG_OFFSET_CAPTURE)) + { + // No (valid) attribute found? Discard everything else inside the tag + break; + } - foreach ($matches as $attr) - { - $attribs[] = preg_quote($attr[0], '/'); - } + if ( + // Is it indeed an "evil" attribute? + preg_match($is_evil_pattern, $attribute['name'][0]) + // Or does it have an equals sign, but no value and not quoted? Strip that too! + OR (trim($attribute['value'][0]) === '') + ) + { + $attributes[] = 'xss=removed'; + } + else + { + $attributes[] = $attribute[0][0]; + } - // replace illegal attribute strings that are inside an html tag - if (count($attribs) > 0) - { - $str = preg_replace('/(<?)(\/?[^><]+?)([^A-Za-z<>\-])(.*?)('.implode('|', $attribs).')(.*?)([\s><]?)([><]*)/i', '$1$2 $4$6$7$8', $str, -1, $count); + $matches['attributes'] = substr($matches['attributes'], $attribute[0][1] + strlen($attribute[0][0])); } + while ($matches['attributes'] !== ''); + + $attributes = empty($attributes) + ? '' + : ' '.implode(' ', $attributes); + return '<'.$matches['slash'].$matches['tagName'].$attributes.'>'; } - while ($count); - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Sanitize Naughty HTML - * - * Callback method for xss_clean() to remove naughty HTML elements. - * - * @used-by CI_Security::xss_clean() - * @param array $matches - * @return string - */ - protected function _sanitize_naughty_html($matches) - { - return '<'.$matches[1].$matches[2].$matches[3] // encode opening brace - // encode captured opening or closing brace to prevent recursive vectors: - .str_replace(array('>', '<'), array('>', '<'), $matches[4]); + return $matches[0]; } // -------------------------------------------------------------------- @@ -849,12 +874,15 @@ class CI_Security { */ protected function _js_link_removal($match) { - return str_replace($match[1], - preg_replace('#href=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si', - '', - $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])) - ), - $match[0]); + return str_replace( + $match[1], + preg_replace( + '#href=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si', + '', + $this->_filter_attributes($match[1]) + ), + $match[0] + ); } // -------------------------------------------------------------------- @@ -874,12 +902,15 @@ class CI_Security { */ protected function _js_img_removal($match) { - return str_replace($match[1], - preg_replace('#src=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si', - '', - $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])) - ), - $match[0]); + return str_replace( + $match[1], + preg_replace( + '#src=.*?(?:(?:alert|prompt|confirm|eval)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si', + '', + $this->_filter_attributes($match[1]) + ), + $match[0] + ); } // -------------------------------------------------------------------- diff --git a/system/core/URI.php b/system/core/URI.php index 9bc34ace7..5b658f679 100644 --- a/system/core/URI.php +++ b/system/core/URI.php @@ -107,37 +107,34 @@ class CI_URI { $this->_permitted_uri_chars = $this->config->item('permitted_uri_chars'); // If it's a CLI request, ignore the configuration - if (is_cli() OR ($protocol = strtoupper($this->config->item('uri_protocol'))) === 'CLI') + if (is_cli()) { - $this->_set_uri_string($this->_parse_argv()); + $uri = $this->_parse_argv(); } - elseif ($protocol === 'AUTO') + else { - // Is there a PATH_INFO variable? This should be the easiest solution. - if (isset($_SERVER['PATH_INFO'])) - { - $this->_set_uri_string($_SERVER['PATH_INFO']); - } - // No PATH_INFO? Let's try REQUST_URI or QUERY_STRING then - elseif (($uri = $this->_parse_request_uri()) !== '' OR ($uri = $this->_parse_query_string()) !== '') - { - $this->_set_uri_string($uri); - } - // As a last ditch effor, let's try using the $_GET array - elseif (is_array($_GET) && count($_GET) === 1 && trim(key($_GET), '/') !== '') + $protocol = $this->config->item('uri_protocol'); + empty($protocol) && $protocol = 'REQUEST_URI'; + + switch ($protocol) { - $this->_set_uri_string(key($_GET)); + case 'AUTO': // For BC purposes only + case 'REQUEST_URI': + $uri = $this->_parse_request_uri(); + break; + case 'QUERY_STRING': + $uri = $this->_parse_query_string(); + break; + case 'PATH_INFO': + default: + $uri = isset($_SERVER[$protocol]) + ? $_SERVER[$protocol] + : $this->_parse_request_uri(); + break; } } - elseif (method_exists($this, ($method = '_parse_'.strtolower($protocol)))) - { - $this->_set_uri_string($this->$method()); - } - else - { - $uri = isset($_SERVER[$protocol]) ? $_SERVER[$protocol] : @getenv($protocol); - $this->_set_uri_string($uri); - } + + $this->_set_uri_string($uri); } log_message('info', 'URI Class Initialized'); @@ -204,17 +201,22 @@ class CI_URI { return ''; } - $uri = parse_url($_SERVER['REQUEST_URI']); + // parse_url() returns false if no host is present, but the path or query string + // contains a colon followed by a number + $uri = parse_url('http://dummy'.$_SERVER['REQUEST_URI']); $query = isset($uri['query']) ? $uri['query'] : ''; - $uri = isset($uri['path']) ? rawurldecode($uri['path']) : ''; + $uri = isset($uri['path']) ? $uri['path'] : ''; - if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) - { - $uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME'])); - } - elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) + if (isset($_SERVER['SCRIPT_NAME'][0])) { - $uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); + if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) + { + $uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME'])); + } + elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) + { + $uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); + } } // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct @@ -222,7 +224,7 @@ class CI_URI { if (trim($uri, '/') === '' && strncmp($query, '/', 1) === 0) { $query = explode('?', $query, 2); - $uri = rawurldecode($query[0]); + $uri = $query[0]; $_SERVER['QUERY_STRING'] = isset($query[1]) ? $query[1] : ''; } else @@ -262,7 +264,7 @@ class CI_URI { { $uri = explode('?', $uri, 2); $_SERVER['QUERY_STRING'] = isset($uri[1]) ? $uri[1] : ''; - $uri = rawurldecode($uri[0]); + $uri = $uri[0]; } parse_str($_SERVER['QUERY_STRING'], $_GET); diff --git a/system/core/compat/hash.php b/system/core/compat/hash.php index 477535dca..15954559c 100644 --- a/system/core/compat/hash.php +++ b/system/core/compat/hash.php @@ -174,9 +174,56 @@ if ( ! function_exists('hash_pbkdf2')) } $hash_length = strlen(hash($algo, NULL, TRUE)); - if (empty($length)) + empty($length) && $length = $hash_length; + + // Pre-hash password inputs longer than the algorithm's block size + // (i.e. prepare HMAC key) to mitigate potential DoS attacks. + static $block_sizes; + empty($block_sizes) && $block_sizes = array( + 'gost' => 32, + 'haval128,3' => 128, + 'haval160,3' => 128, + 'haval192,3' => 128, + 'haval224,3' => 128, + 'haval256,3' => 128, + 'haval128,4' => 128, + 'haval160,4' => 128, + 'haval192,4' => 128, + 'haval224,4' => 128, + 'haval256,4' => 128, + 'haval128,5' => 128, + 'haval160,5' => 128, + 'haval192,5' => 128, + 'haval224,5' => 128, + 'haval256,5' => 128, + 'md2' => 16, + 'md4' => 64, + 'md5' => 64, + 'ripemd128' => 64, + 'ripemd160' => 64, + 'ripemd256' => 64, + 'ripemd320' => 64, + 'salsa10' => 64, + 'salsa20' => 64, + 'sha1' => 64, + 'sha224' => 64, + 'sha256' => 64, + 'sha384' => 128, + 'sha512' => 128, + 'snefru' => 32, + 'snefru256' => 32, + 'tiger128,3' => 64, + 'tiger160,3' => 64, + 'tiger192,3' => 64, + 'tiger128,4' => 64, + 'tiger160,4' => 64, + 'tiger192,4' => 64, + 'whirlpool' => 64 + ); + + if (isset($block_sizes[$algo]) && strlen($password) > $block_sizes[$algo]) { - $length = $hash_length; + $password = hash($algo, $password, TRUE); } $hash = ''; diff --git a/system/core/compat/index.html b/system/core/compat/index.html index c942a79ce..b702fbc39 100644 --- a/system/core/compat/index.html +++ b/system/core/compat/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/core/compat/mbstring.php b/system/core/compat/mbstring.php index ddb2bae47..e335c85f7 100644 --- a/system/core/compat/mbstring.php +++ b/system/core/compat/mbstring.php @@ -92,7 +92,7 @@ if ( ! function_exists('mb_strpos')) * WARNING: This function WILL fall-back to strpos() * if iconv is not available! * - * @link http://php.net/mb_strpos() + * @link http://php.net/mb_strpos * @param string $haystack * @param string $needle * @param int $offset diff --git a/system/core/index.html b/system/core/index.html index c942a79ce..b702fbc39 100644 --- a/system/core/index.html +++ b/system/core/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/DB.php b/system/database/DB.php index 8ea7ca6fa..23de414b5 100644 --- a/system/database/DB.php +++ b/system/database/DB.php @@ -82,7 +82,7 @@ function &DB($params = '', $query_builder_override = NULL) } } - if ( ! isset($db) OR count($db) === 0) + if (empty($db)) { show_error('No database connection settings were found in the database config file.'); } @@ -213,10 +213,6 @@ function &DB($params = '', $query_builder_override = NULL) } } - if ($DB->autoinit === TRUE) - { - $DB->initialize(); - } - + $DB->initialize(); return $DB; } diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php index bbe65b410..0ea679432 100644 --- a/system/database/DB_driver.php +++ b/system/database/DB_driver.php @@ -124,15 +124,6 @@ abstract class CI_DB_driver { public $dbcollat = 'utf8_general_ci'; /** - * Auto-init flag - * - * Whether to automatically initialize the DB connection. - * - * @var bool - */ - public $autoinit = TRUE; - - /** * Encryption flag/data * * @var mixed @@ -460,7 +451,7 @@ abstract class CI_DB_driver { * * This is just a dummy method that all drivers will override. * - * @return mixed + * @return mixed */ public function db_connect() { @@ -490,7 +481,7 @@ abstract class CI_DB_driver { * This is just a dummy method to allow drivers without such * functionality to not declare it, while others will override it. * - * @return void + * @return void */ public function reconnect() { @@ -504,7 +495,7 @@ abstract class CI_DB_driver { * This is just a dummy method to allow drivers without such * functionality to not declare it, while others will override it. * - * @return bool + * @return bool */ public function db_select() { @@ -656,7 +647,10 @@ abstract class CI_DB_driver { } // This will trigger a rollback if transactions are being used - $this->_trans_status = FALSE; + if ($this->_trans_depth !== 0) + { + $this->_trans_status = FALSE; + } // Grab the error now, as we might run some additional queries before displaying the error $error = $this->error(); @@ -674,7 +668,13 @@ abstract class CI_DB_driver { { do { + $trans_depth = $this->_trans_depth; $this->trans_complete(); + if ($trans_depth === $this->_trans_depth) + { + log_message('error', 'Database: Failure during an automated transaction commit/rollback!'); + break; + } } while ($this->_trans_depth !== 0); } @@ -797,10 +797,13 @@ abstract class CI_DB_driver { /** * Enable/disable Transaction Strict Mode + * * When strict mode is enabled, if you are running multiple groups of - * transactions, if one group fails all groups will be rolled back. - * If strict mode is disabled, each group is treated autonomously, meaning - * a failure of one group will not affect any others + * transactions, if one group fails all subsequent groups will be + * rolled back. + * + * If strict mode is disabled, each group is treated autonomously, + * meaning a failure of one group will not affect any others * * @param bool $mode = TRUE * @return void @@ -816,24 +819,16 @@ abstract class CI_DB_driver { * Start Transaction * * @param bool $test_mode = FALSE - * @return void + * @return bool */ public function trans_start($test_mode = FALSE) { if ( ! $this->trans_enabled) { - return; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - $this->_trans_depth += 1; - return; + return FALSE; } - $this->trans_begin($test_mode); - $this->_trans_depth += 1; + return $this->trans_begin($test_mode); } // -------------------------------------------------------------------- @@ -850,25 +845,14 @@ abstract class CI_DB_driver { return FALSE; } - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 1) - { - $this->_trans_depth -= 1; - return TRUE; - } - else - { - $this->_trans_depth = 0; - } - // The query() function will set this flag to FALSE in the event that a query failed if ($this->_trans_status === FALSE OR $this->_trans_failure === TRUE) { $this->trans_rollback(); // If we are NOT running in strict mode, we will reset - // the _trans_status flag so that subsequent groups of transactions - // will be permitted. + // the _trans_status flag so that subsequent groups of + // transactions will be permitted. if ($this->trans_strict === FALSE) { $this->_trans_status = TRUE; @@ -878,8 +862,7 @@ abstract class CI_DB_driver { return FALSE; } - $this->trans_commit(); - return TRUE; + return $this->trans_commit(); } // -------------------------------------------------------------------- @@ -897,6 +880,87 @@ abstract class CI_DB_driver { // -------------------------------------------------------------------- /** + * Begin Transaction + * + * @param bool $test_mode + * @return bool + */ + public function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return FALSE; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 0) + { + $this->_trans_depth++; + return TRUE; + } + + // 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); + + if ($this->_trans_begin()) + { + $this->_trans_depth++; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + public function trans_commit() + { + if ( ! $this->trans_enabled OR $this->_trans_depth === 0) + { + return FALSE; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 1 OR $this->_trans_commit()) + { + $this->_trans_depth--; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + public function trans_rollback() + { + if ( ! $this->trans_enabled OR $this->_trans_depth === 0) + { + return FALSE; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 1 OR $this->_trans_rollback()) + { + $this->_trans_depth--; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** * Compile Bindings * * @param string the sql statement @@ -1227,7 +1291,7 @@ abstract class CI_DB_driver { /** * Fetch Field Names * - * @param string the table name + * @param string $table Table name * @return array */ public function list_fields($table) @@ -1486,18 +1550,18 @@ abstract class CI_DB_driver { ? '\s+'.preg_quote(trim(sprintf($this->_like_escape_str, $this->_like_escape_chr)), '/') : ''; $_operators = array( - '\s*(?:<|>|!)?=\s*', // =, <=, >=, != - '\s*<>?\s*', // <, <> - '\s*>\s*', // > - '\s+IS NULL', // IS NULL - '\s+IS NOT NULL', // IS NOT NULL - '\s+EXISTS\s*\([^\)]+\)', // EXISTS(sql) - '\s+NOT EXISTS\s*\([^\)]+\)', // NOT EXISTS(sql) - '\s+BETWEEN\s+\S+\s+AND\s+\S+', // BETWEEN value AND value - '\s+IN\s*\([^\)]+\)', // IN(list) - '\s+NOT IN\s*\([^\)]+\)', // NOT IN (list) - '\s+LIKE\s+\S+'.$_les, // LIKE 'expr'[ ESCAPE '%s'] - '\s+NOT LIKE\s+\S+'.$_les // NOT LIKE 'expr'[ ESCAPE '%s'] + '\s*(?:<|>|!)?=\s*', // =, <=, >=, != + '\s*<>?\s*', // <, <> + '\s*>\s*', // > + '\s+IS NULL', // IS NULL + '\s+IS NOT NULL', // IS NOT NULL + '\s+EXISTS\s*\([^\)]+\)', // EXISTS(sql) + '\s+NOT EXISTS\s*\([^\)]+\)', // NOT EXISTS(sql) + '\s+BETWEEN\s+', // BETWEEN value AND value + '\s+IN\s*\([^\)]+\)', // IN(list) + '\s+NOT IN\s*\([^\)]+\)', // NOT IN (list) + '\s+LIKE\s+\S.*('.$_les.')?', // LIKE 'expr'[ ESCAPE '%s'] + '\s+NOT LIKE\s+\S.*('.$_les.')?' // NOT LIKE 'expr'[ ESCAPE '%s'] ); } @@ -1760,13 +1824,13 @@ abstract class CI_DB_driver { // // Added exception for single quotes as well, we don't want to alter // literal strings. -- Narf - if (strpos($item, '(') !== FALSE OR strpos($item, "'") !== FALSE) + if (strcspn($item, "()'") !== strlen($item)) { return $item; } // Convert tabs or multiple spaces into single spaces - $item = preg_replace('/\s+/', ' ', $item); + $item = preg_replace('/\s+/', ' ', trim($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 @@ -1794,12 +1858,15 @@ abstract class CI_DB_driver { // with an alias. While we're at it, we will escape the components if (strpos($item, '.') !== FALSE) { - $parts = explode('.', $item); + $parts = explode('.', $item); // Does the first segment of the exploded item match // one of the aliases previously identified? If so, // we have nothing more to do other than escape the item - if (in_array($parts[0], $this->qb_aliased_tables)) + // + // NOTE: The ! empty() condition prevents this method + // from breaking when QB isn't enabled. + if ( ! empty($this->qb_aliased_tables) && in_array($parts[0], $this->qb_aliased_tables)) { if ($protect_identifiers === TRUE) { diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php index f6ee2a63a..f9cf76a14 100644 --- a/system/database/DB_forge.php +++ b/system/database/DB_forge.php @@ -143,7 +143,7 @@ abstract class CI_DB_forge { protected $_unsigned = TRUE; /** - * NULL value representatin in CREATE/ALTER TABLE statements + * NULL value representation in CREATE/ALTER TABLE statements * * @var string */ @@ -239,6 +239,12 @@ abstract class CI_DB_forge { */ public function add_key($key, $primary = FALSE) { + // DO NOT change this! This condition is only applicable + // for PRIMARY keys because you can only have one such, + // and therefore all fields you add to it will be included + // in the same, composite PRIMARY KEY. + // + // It's not the same for regular indexes. if ($primary === TRUE && is_array($key)) { foreach ($key as $one) @@ -453,12 +459,7 @@ abstract class CI_DB_forge { return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE; } - $query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists); - if ($query === FALSE) - { - return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; - } - elseif ($query === TRUE) + if (($query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists)) === TRUE) { return TRUE; } diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php index 79cbfb3ad..cf1100d27 100644 --- a/system/database/DB_query_builder.php +++ b/system/database/DB_query_builder.php @@ -794,13 +794,23 @@ abstract class CI_DB_query_builder extends CI_DB_driver { $not = ($not) ? ' NOT' : ''; - $where_in = array(); - foreach ($values as $value) + if ($escape === TRUE) { - $where_in[] = $this->escape($value); + $where_in = array(); + foreach ($values as $value) + { + $where_in[] = $this->escape($value); + } } + else + { + $where_in = array_values($values); + } + + $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) + ? $this->_group_get_type('') + : $this->_group_get_type($type); - $prefix = (count($this->qb_where) === 0) ? $this->_group_get_type('') : $this->_group_get_type($type); $where_in = array( 'condition' => $prefix.$key.$not.' IN('.implode(', ', $where_in).')', 'escape' => $escape @@ -918,13 +928,18 @@ abstract class CI_DB_query_builder extends CI_DB_driver { } is_bool($escape) OR $escape = $this->_protect_identifiers; + // lowercase $side in case somebody writes e.g. 'BEFORE' instead of 'before' (doh) + $side = strtolower($side); foreach ($field as $k => $v) { $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) ? $this->_group_get_type('') : $this->_group_get_type($type); - $v = $this->escape_like_str($v); + if ($escape === TRUE) + { + $v = $this->escape_like_str($v); + } if ($side === 'none') { @@ -944,7 +959,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { } // some platforms require an escape sequence definition for LIKE wildcards - if ($this->_like_escape_str !== '') + if ($escape === TRUE && $this->_like_escape_str !== '') { $like_statement .= sprintf($this->_like_escape_str, $this->_like_escape_chr); } @@ -1292,7 +1307,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { * Compiles a SELECT query string and returns the sql. * * @param string the table name to select from (optional) - * @param bool TRUE: resets QB values; FALSE: leave QB vaules alone + * @param bool TRUE: resets QB values; FALSE: leave QB values alone * @return string */ public function get_compiled_select($table = '', $reset = TRUE) @@ -1353,9 +1368,10 @@ abstract class CI_DB_query_builder extends CI_DB_driver { * returned by an Query Builder query. * * @param string + * @param bool the reset clause * @return int */ - public function count_all_results($table = '') + public function count_all_results($table = '', $reset = TRUE) { if ($table !== '') { @@ -1366,7 +1382,11 @@ abstract class CI_DB_query_builder extends CI_DB_driver { $result = ($this->qb_distinct === TRUE) ? $this->query($this->_count_string.$this->protect_identifiers('numrows')."\nFROM (\n".$this->_compile_select()."\n) CI_count_all_results") : $this->query($this->_compile_select($this->_count_string.$this->protect_identifiers('numrows'))); - $this->_reset_select(); + + if ($reset === TRUE) + { + $this->_reset_select(); + } if ($result->num_rows() === 0) { @@ -1729,7 +1749,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { return FALSE; } - $sql = $this->_update($this->protect_identifiers($this->qb_from[0], TRUE, NULL, FALSE), $this->qb_set); + $sql = $this->_update($this->qb_from[0], $this->qb_set); if ($reset === TRUE) { @@ -1777,7 +1797,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { $this->limit($limit); } - $sql = $this->_update($this->protect_identifiers($this->qb_from[0], TRUE, NULL, FALSE), $this->qb_set); + $sql = $this->_update($this->qb_from[0], $this->qb_set); $this->_reset_write(); return $this->query($sql); } @@ -1794,7 +1814,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { * @param string the table to update data on * @return bool */ - protected function _validate_update($table = '') + protected function _validate_update($table) { if (count($this->qb_set) === 0) { @@ -1803,7 +1823,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { if ($table !== '') { - $this->qb_from[0] = $table; + $this->qb_from = array($this->protect_identifiers($table, TRUE, NULL, FALSE)); } elseif ( ! isset($this->qb_from[0])) { @@ -2085,10 +2105,13 @@ abstract class CI_DB_query_builder extends CI_DB_driver { } elseif (is_array($table)) { + empty($where) && $reset_data = FALSE; + foreach ($table as $single_table) { $this->delete($single_table, $where, $limit, $reset_data); } + return; } else @@ -2248,7 +2271,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { else { // Cycle through the "select" portion of the query and prep each column name. - // The reason we protect identifiers here rather then in the select() function + // The reason we protect identifiers here rather than in the select() function // is because until the user calls the from() function we don't know if there are aliases foreach ($this->qb_select as $key => $val) { @@ -2319,7 +2342,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { // Split multiple conditions $conditions = preg_split( - '/(\s*AND\s+|\s*OR\s+)/i', + '/((?:^|\s+)AND\s+|(?:^|\s+)OR\s+)/i', $this->{$qb_key}[$i]['condition'], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY diff --git a/system/database/DB_utility.php b/system/database/DB_utility.php index 57356ac53..b51893e18 100644 --- a/system/database/DB_utility.php +++ b/system/database/DB_utility.php @@ -249,16 +249,17 @@ abstract class CI_DB_utility { $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim; } - $out = substr(rtrim($out), 0, -strlen($delim)).$newline; + $out = substr($out, 0, -strlen($delim)).$newline; // Next blast through the result array and build out the rows while ($row = $query->unbuffered_row('array')) { + $line = array(); foreach ($row as $item) { - $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure.$delim; + $line[] = $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure; } - $out = substr(rtrim($out), 0, -strlen($delim)).$newline; + $out .= implode($delim, $line).$newline; } return $out; diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php index 402117978..65f4adb3f 100644 --- a/system/database/drivers/cubrid/cubrid_driver.php +++ b/system/database/drivers/cubrid/cubrid_driver.php @@ -163,10 +163,6 @@ class CI_DB_cubrid_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } return ( ! $this->conn_id OR ($version = cubrid_get_server_info($this->conn_id)) === FALSE) ? FALSE @@ -191,25 +187,17 @@ class CI_DB_cubrid_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if (($autocommit = cubrid_get_autocommit($this->conn_id)) === NULL) { - return TRUE; + return FALSE; } - - // 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); - - if (cubrid_get_autocommit($this->conn_id)) + elseif ($autocommit === TRUE) { - cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_FALSE); + return cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_FALSE); } return TRUE; @@ -222,19 +210,16 @@ class CI_DB_cubrid_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if ( ! cubrid_commit($this->conn_id)) { - return TRUE; + return FALSE; } - cubrid_commit($this->conn_id); - if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id)) { - cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); + return cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); } return TRUE; @@ -247,16 +232,13 @@ class CI_DB_cubrid_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if ( ! cubrid_rollback($this->conn_id)) { - return TRUE; + return FALSE; } - cubrid_rollback($this->conn_id); - if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id)) { cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); diff --git a/system/database/drivers/cubrid/index.html b/system/database/drivers/cubrid/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/cubrid/index.html +++ b/system/database/drivers/cubrid/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/ibase/ibase_driver.php b/system/database/drivers/ibase/ibase_driver.php index 529c320cd..82550d51b 100644 --- a/system/database/drivers/ibase/ibase_driver.php +++ b/system/database/drivers/ibase/ibase_driver.php @@ -134,24 +134,16 @@ class CI_DB_ibase_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if (($trans_handle = ibase_trans($this->conn_id)) === FALSE) { - return TRUE; + return FALSE; } - // 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); - - $this->_ibase_trans = ibase_trans($this->conn_id); - + $this->_ibase_trans = $trans_handle; return TRUE; } @@ -162,15 +154,15 @@ class CI_DB_ibase_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans->depth > 0) + if (ibase_commit($this->_ibase_trans)) { + $this->_ibase_trans = NULL; return TRUE; } - return ibase_commit($this->_ibase_trans); + return FALSE; } // -------------------------------------------------------------------- @@ -180,15 +172,15 @@ class CI_DB_ibase_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if (ibase_rollback($this->_ibase_trans)) { + $this->_ibase_trans = NULL; return TRUE; } - return ibase_rollback($this->_ibase_trans); + return FALSE; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/ibase/index.html b/system/database/drivers/ibase/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/ibase/index.html +++ b/system/database/drivers/ibase/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/index.html b/system/database/drivers/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/index.html +++ b/system/database/drivers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/mssql/index.html b/system/database/drivers/mssql/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/mssql/index.html +++ b/system/database/drivers/mssql/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php index 8f15d8d69..883973ae1 100644 --- a/system/database/drivers/mssql/mssql_driver.php +++ b/system/database/drivers/mssql/mssql_driver.php @@ -182,22 +182,10 @@ class CI_DB_mssql_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // 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); - return $this->simple_query('BEGIN TRAN'); } @@ -208,14 +196,8 @@ class CI_DB_mssql_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->simple_query('COMMIT TRAN'); } @@ -226,14 +208,8 @@ class CI_DB_mssql_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->simple_query('ROLLBACK TRAN'); } @@ -381,9 +357,19 @@ class CI_DB_mssql_driver extends CI_DB { */ public function error() { - $query = $this->query('SELECT @@ERROR AS code'); - $query = $query->row(); - return array('code' => $query->code, 'message' => mssql_get_last_message()); + // We need this because the error info is discarded by the + // server the first time you request it, and query() already + // calls error() once for logging purposes when a query fails. + static $error = array('code' => 0, 'message' => NULL); + + $message = mssql_get_last_message(); + if ( ! empty($message)) + { + $error['code'] = $this->query('SELECT @@ERROR AS code')->row()->code; + $error['message'] = $message; + } + + return $error; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/mysql/index.html b/system/database/drivers/mysql/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/mysql/index.html +++ b/system/database/drivers/mysql/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php index f8e9b6d61..9c630d0d6 100644 --- a/system/database/drivers/mysql/mysql_driver.php +++ b/system/database/drivers/mysql/mysql_driver.php @@ -223,10 +223,6 @@ class CI_DB_mysql_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } if ( ! $this->conn_id OR ($version = mysql_get_server_info($this->conn_id)) === FALSE) { @@ -276,25 +272,12 @@ class CI_DB_mysql_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // 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); - $this->simple_query('SET AUTOCOMMIT=0'); - $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK - return TRUE; + return $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK } // -------------------------------------------------------------------- @@ -304,17 +287,15 @@ class CI_DB_mysql_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if ($this->simple_query('COMMIT')) { + $this->simple_query('SET AUTOCOMMIT=1'); return TRUE; } - $this->simple_query('COMMIT'); - $this->simple_query('SET AUTOCOMMIT=1'); - return TRUE; + return FALSE; } // -------------------------------------------------------------------- @@ -324,17 +305,15 @@ class CI_DB_mysql_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if ($this->simple_query('ROLLBACK')) { + $this->simple_query('SET AUTOCOMMIT=1'); return TRUE; } - $this->simple_query('ROLLBACK'); - $this->simple_query('SET AUTOCOMMIT=1'); - return TRUE; + return FALSE; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/mysqli/index.html b/system/database/drivers/mysqli/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/mysqli/index.html +++ b/system/database/drivers/mysqli/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php index 22a8ba678..827470078 100644 --- a/system/database/drivers/mysqli/mysqli_driver.php +++ b/system/database/drivers/mysqli/mysqli_driver.php @@ -102,7 +102,6 @@ class CI_DB_mysqli_driver extends CI_DB { * * @param bool $persistent * @return object - * @todo SSL support */ public function db_connect($persistent = FALSE) { @@ -132,8 +131,52 @@ class CI_DB_mysqli_driver extends CI_DB { $mysqli->options(MYSQLI_INIT_COMMAND, 'SET SESSION sql_mode="STRICT_ALL_TABLES"'); } - return $mysqli->real_connect($hostname, $this->username, $this->password, $this->database, $port, $socket, $client_flags) - ? $mysqli : FALSE; + if (is_array($this->encrypt)) + { + $ssl = array(); + empty($this->encrypt['ssl_key']) OR $ssl['key'] = $this->encrypt['ssl_key']; + empty($this->encrypt['ssl_cert']) OR $ssl['cert'] = $this->encrypt['ssl_cert']; + empty($this->encrypt['ssl_ca']) OR $ssl['ca'] = $this->encrypt['ssl_ca']; + empty($this->encrypt['ssl_capath']) OR $ssl['capath'] = $this->encrypt['ssl_capath']; + empty($this->encrypt['ssl_cipher']) OR $ssl['cipher'] = $this->encrypt['ssl_cipher']; + + if ( ! empty($ssl)) + { + if ( ! empty($this->encrypt['ssl_verify']) && defined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT')) + { + $mysqli->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, TRUE); + } + + $client_flags |= MYSQLI_CLIENT_SSL; + $mysqli->ssl_set( + isset($ssl['key']) ? $ssl['key'] : NULL, + isset($ssl['cert']) ? $ssl['cert'] : NULL, + isset($ssl['ca']) ? $ssl['ca'] : NULL, + isset($ssl['capath']) ? $ssl['capath'] : NULL, + isset($ssl['cipher']) ? $ssl['cipher'] : NULL + ); + } + } + + if ($mysqli->real_connect($hostname, $this->username, $this->password, $this->database, $port, $socket, $client_flags)) + { + // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails + if ( + ($client_flags & MYSQLI_CLIENT_SSL) + && version_compare($mysqli->client_info, '5.7.3', '<=') + && empty($mysqli->query("SHOW STATUS LIKE 'ssl_cipher'")->fetch_object()->Value) + ) + { + $mysqli->close(); + $message = 'MySQLi was configured for an SSL connection, but got an unencrypted connection instead!'; + log_message('error', $message); + return ($this->db->db_debug) ? $this->db->display_error($message, '', TRUE) : FALSE; + } + + return $mysqli; + } + + return FALSE; } // -------------------------------------------------------------------- @@ -204,10 +247,6 @@ class CI_DB_mysqli_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } return $this->data_cache['version'] = $this->conn_id->server_info; } @@ -252,22 +291,10 @@ class CI_DB_mysqli_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // 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); - $this->conn_id->autocommit(FALSE); return is_php('5.5') ? $this->conn_id->begin_transaction() @@ -281,14 +308,8 @@ class CI_DB_mysqli_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - if ($this->conn_id->commit()) { $this->conn_id->autocommit(TRUE); @@ -305,14 +326,8 @@ class CI_DB_mysqli_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - if ($this->conn_id->rollback()) { $this->conn_id->autocommit(TRUE); diff --git a/system/database/drivers/oci8/index.html b/system/database/drivers/oci8/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/oci8/index.html +++ b/system/database/drivers/oci8/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php index b87b41112..916ddeb90 100644 --- a/system/database/drivers/oci8/oci8_driver.php +++ b/system/database/drivers/oci8/oci8_driver.php @@ -102,6 +102,14 @@ class CI_DB_oci8_driver extends CI_DB { // -------------------------------------------------------------------- /** + * Reset $stmt_id flag + * + * Used by stored_procedure() to prevent _execute() from + * re-setting the statement ID. + */ + protected $_reset_stmt_id = TRUE; + + /** * List of reserved identifiers * * Identifiers that must NOT be escaped. @@ -243,10 +251,6 @@ class CI_DB_oci8_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } if ( ! $this->conn_id OR ($version = oci_server_version($this->conn_id)) === FALSE) { @@ -269,26 +273,13 @@ class CI_DB_oci8_driver extends CI_DB { /* Oracle must parse the query before it is run. All of the actions with * the query are based on the statement id returned by oci_parse(). */ - $this->stmt_id = FALSE; - $this->_set_stmt_id($sql); - oci_set_prefetch($this->stmt_id, 1000); - return oci_execute($this->stmt_id, $this->commit_mode); - } - - // -------------------------------------------------------------------- - - /** - * Generate a statement ID - * - * @param string $sql an SQL query - * @return void - */ - protected function _set_stmt_id($sql) - { - if ( ! is_resource($this->stmt_id)) + if ($this->_reset_stmt_id === TRUE) { $this->stmt_id = oci_parse($this->conn_id, $sql); } + + oci_set_prefetch($this->stmt_id, 1000); + return oci_execute($this->stmt_id, $this->commit_mode); } // -------------------------------------------------------------------- @@ -315,22 +306,22 @@ class CI_DB_oci8_driver extends CI_DB { * * params array keys * - * KEY OPTIONAL NOTES - * name no the name of the parameter should be in :<param_name> format - * value no the value of the parameter. If this is an OUT or IN OUT parameter, - * this should be a reference to a variable - * type yes the type of the parameter - * length yes the max size of the parameter + * KEY OPTIONAL NOTES + * name no the name of the parameter should be in :<param_name> format + * value no the value of the parameter. If this is an OUT or IN OUT parameter, + * this should be a reference to a variable + * type yes the type of the parameter + * length yes the max size of the parameter */ - public function stored_procedure($package, $procedure, $params) + public function stored_procedure($package, $procedure, array $params) { - if ($package === '' OR $procedure === '' OR ! is_array($params)) + if ($package === '' OR $procedure === '') { log_message('error', 'Invalid query: '.$package.'.'.$procedure); return ($this->db_debug) ? $this->display_error('db_invalid_query') : FALSE; } - // build the query string + // Build the query string $sql = 'BEGIN '.$package.'.'.$procedure.'('; $have_cursor = FALSE; @@ -345,10 +336,12 @@ class CI_DB_oci8_driver extends CI_DB { } $sql = trim($sql, ',').'); END;'; - $this->stmt_id = FALSE; - $this->_set_stmt_id($sql); + $this->_reset_stmt_id = FALSE; + $this->stmt_id = oci_parse($this->conn_id, $sql); $this->_bind_params($params); - return $this->query($sql, FALSE, $have_cursor); + $result = $this->query($sql, FALSE, $have_cursor); + $this->_reset_stmt_id = TRUE; + return $result; } // -------------------------------------------------------------------- @@ -385,27 +378,10 @@ class CI_DB_oci8_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // 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); - $this->commit_mode = is_php('5.3.2') ? OCI_NO_AUTO_COMMIT : OCI_DEFAULT; return TRUE; } @@ -417,20 +393,10 @@ class CI_DB_oci8_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected 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) - { - return TRUE; - } - $this->commit_mode = OCI_COMMIT_ON_SUCCESS; + return oci_commit($this->conn_id); } @@ -441,14 +407,8 @@ class CI_DB_oci8_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - $this->commit_mode = OCI_COMMIT_ON_SUCCESS; return oci_rollback($this->conn_id); } @@ -577,7 +537,7 @@ class CI_DB_oci8_driver extends CI_DB { { $default = ''; } - $retval[$i]->default = $query[$i]->COLUMN_DEFAULT; + $retval[$i]->default = $default; } return $retval; diff --git a/system/database/drivers/odbc/index.html b/system/database/drivers/odbc/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/odbc/index.html +++ b/system/database/drivers/odbc/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php index f5d77a147..409284b44 100644 --- a/system/database/drivers/odbc/odbc_driver.php +++ b/system/database/drivers/odbc/odbc_driver.php @@ -143,22 +143,10 @@ class CI_DB_odbc_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // 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); - return odbc_autocommit($this->conn_id, FALSE); } @@ -169,17 +157,15 @@ class CI_DB_odbc_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if (odbc_commit($this->conn_id)) { + odbc_autocommit($this->conn_id, TRUE); return TRUE; } - $ret = odbc_commit($this->conn_id); - odbc_autocommit($this->conn_id, TRUE); - return $ret; + return FALSE; } // -------------------------------------------------------------------- @@ -189,17 +175,15 @@ class CI_DB_odbc_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if (odbc_rollback($this->conn_id)) { + odbc_autocommit($this->conn_id, TRUE); return TRUE; } - $ret = odbc_rollback($this->conn_id); - odbc_autocommit($this->conn_id, TRUE); - return $ret; + return FALSE; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/pdo/index.html b/system/database/drivers/pdo/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/pdo/index.html +++ b/system/database/drivers/pdo/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php index 68aeb0eef..8c5a5e7e3 100644 --- a/system/database/drivers/pdo/pdo_driver.php +++ b/system/database/drivers/pdo/pdo_driver.php @@ -156,10 +156,6 @@ class CI_DB_pdo_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } // Not all subdrivers support the getAttribute() method try @@ -190,22 +186,10 @@ class CI_DB_pdo_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // 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); - return $this->conn_id->beginTransaction(); } @@ -216,14 +200,8 @@ class CI_DB_pdo_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->conn_id->commit(); } @@ -234,14 +212,8 @@ class CI_DB_pdo_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->conn_id->rollBack(); } diff --git a/system/database/drivers/pdo/subdrivers/index.html b/system/database/drivers/pdo/subdrivers/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/pdo/subdrivers/index.html +++ b/system/database/drivers/pdo/subdrivers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php index cf5a0c77f..7a767ef40 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php @@ -158,7 +158,7 @@ class CI_DB_pdo_4d_driver extends CI_DB_pdo_driver { * @param string $table * @param array $values * @return string - */ + */ protected function _update($table, $values) { $this->qb_limit = FALSE; diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php index c8d7c770d..ae2b9983b 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php @@ -154,7 +154,7 @@ class CI_DB_pdo_dblib_driver extends CI_DB_pdo_driver { */ protected function _list_tables($prefix_limit = FALSE) { - return 'SELECT '.$this->escape_identifiers('name') + $sql = 'SELECT '.$this->escape_identifiers('name') .' FROM '.$this->escape_identifiers('sysobjects') .' WHERE '.$this->escape_identifiers('type')." = 'U'"; @@ -227,7 +227,7 @@ class CI_DB_pdo_dblib_driver extends CI_DB_pdo_driver { * @param string $table * @param array $values * @return string - */ + */ protected function _update($table, $values) { $this->qb_limit = FALSE; diff --git a/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php index 3bd5c9354..0bafde861 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php @@ -211,7 +211,7 @@ class CI_DB_pdo_firebird_driver extends CI_DB_pdo_driver { * * @param string $table * @return string - */ + */ protected function _truncate($table) { return 'DELETE FROM '.$table; diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php index 67dc5f5ec..e9d25cebc 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php @@ -119,7 +119,6 @@ class CI_DB_pdo_mysql_driver extends CI_DB_pdo_driver { * * @param bool $persistent * @return object - * @todo SSL support */ public function db_connect($persistent = FALSE) { @@ -151,7 +150,59 @@ class CI_DB_pdo_mysql_driver extends CI_DB_pdo_driver { $this->options[PDO::MYSQL_ATTR_COMPRESS] = TRUE; } - return parent::db_connect($persistent); + // SSL support was added to PDO_MYSQL in PHP 5.3.7 + if (is_array($this->encrypt) && is_php('5.3.7')) + { + $ssl = array(); + empty($this->encrypt['ssl_key']) OR $ssl[PDO::MYSQL_ATTR_SSL_KEY] = $this->encrypt['ssl_key']; + empty($this->encrypt['ssl_cert']) OR $ssl[PDO::MYSQL_ATTR_SSL_CERT] = $this->encrypt['ssl_cert']; + empty($this->encrypt['ssl_ca']) OR $ssl[PDO::MYSQL_ATTR_SSL_CA] = $this->encrypt['ssl_ca']; + empty($this->encrypt['ssl_capath']) OR $ssl[PDO::MYSQL_ATTR_SSL_CAPATH] = $this->encrypt['ssl_capath']; + empty($this->encrypt['ssl_cipher']) OR $ssl[PDO::MYSQL_ATTR_SSL_CIPHER] = $this->encrypt['ssl_cipher']; + + // DO NOT use array_merge() here! + // It re-indexes numeric keys and the PDO_MYSQL_ATTR_SSL_* constants are integers. + empty($ssl) OR $this->options += $ssl; + } + + // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails + if ( + ($pdo = parent::db_connect($persistent)) !== FALSE + && ! empty($ssl) + && version_compare($pdo->getAttribute(PDO::ATTR_CLIENT_VERSION), '5.7.3', '<=') + && empty($pdo->query("SHOW STATUS LIKE 'ssl_cipher'")->fetchObject()->Value) + ) + { + $message = 'PDO_MYSQL was configured for an SSL connection, but got an unencrypted connection instead!'; + log_message('error', $message); + return ($this->db->db_debug) ? $this->db->display_error($message, '', TRUE) : FALSE; + } + + return $pdo; + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @param string $database + * @return bool + */ + public function db_select($database = '') + { + if ($database === '') + { + $database = $this->database; + } + + if (FALSE !== $this->simple_query('USE '.$this->escape_identifiers($database))) + { + $this->database = $database; + return TRUE; + } + + return FALSE; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php index f07f49f84..409e6501b 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php @@ -121,17 +121,31 @@ class CI_DB_pdo_sqlite_driver extends CI_DB_pdo_driver { // -------------------------------------------------------------------- /** - * Show column query + * Fetch Field Names * - * Generates a platform-specific query string so that the column names can be fetched - * - * @param string $table - * @return string + * @param string $table Table name + * @return array */ - protected function _list_columns($table = '') + public function list_fields($table) { - // Not supported - return FALSE; + // Is there a cached result? + if (isset($this->data_cache['field_names'][$table])) + { + return $this->data_cache['field_names'][$table]; + } + + if (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE) + { + return FALSE; + } + + $this->data_cache['field_names'][$table] = array(); + foreach ($result->result_array() as $row) + { + $this->data_cache['field_names'][$table][] = $row['name']; + } + + return $this->data_cache['field_names'][$table]; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php index 28faaddb7..15afbdef5 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php +++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php @@ -89,6 +89,7 @@ class CI_DB_pdo_sqlite_forge extends CI_DB_pdo_forge { if (version_compare($this->db->version(), '3.3', '<')) { $this->_create_table_if = FALSE; + $this->_drop_table_if = FALSE; } } diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php index b5bdbf91a..f8ae5f6db 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php @@ -183,7 +183,7 @@ class CI_DB_pdo_sqlsrv_driver extends CI_DB_pdo_driver { */ protected function _list_tables($prefix_limit = FALSE) { - return 'SELECT '.$this->escape_identifiers('name') + $sql = 'SELECT '.$this->escape_identifiers('name') .' FROM '.$this->escape_identifiers('sysobjects') .' WHERE '.$this->escape_identifiers('type')." = 'U'"; diff --git a/system/database/drivers/postgre/index.html b/system/database/drivers/postgre/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/postgre/index.html +++ b/system/database/drivers/postgre/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php index 3f3e3f018..b1df326f7 100644 --- a/system/database/drivers/postgre/postgre_driver.php +++ b/system/database/drivers/postgre/postgre_driver.php @@ -213,10 +213,6 @@ class CI_DB_postgre_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } if ( ! $this->conn_id OR ($pg_version = pg_version($this->conn_id)) === FALSE) { @@ -251,22 +247,10 @@ class CI_DB_postgre_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // 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); - return (bool) pg_query($this->conn_id, 'BEGIN'); } @@ -277,14 +261,8 @@ class CI_DB_postgre_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return (bool) pg_query($this->conn_id, 'COMMIT'); } @@ -295,14 +273,8 @@ class CI_DB_postgre_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return (bool) pg_query($this->conn_id, 'ROLLBACK'); } diff --git a/system/database/drivers/sqlite/index.html b/system/database/drivers/sqlite/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/sqlite/index.html +++ b/system/database/drivers/sqlite/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php index 9d9caa0b4..e000a8e50 100644 --- a/system/database/drivers/sqlite/sqlite_driver.php +++ b/system/database/drivers/sqlite/sqlite_driver.php @@ -122,24 +122,11 @@ class CI_DB_sqlite_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // 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); - - $this->simple_query('BEGIN TRANSACTION'); - return TRUE; + return $this->simple_query('BEGIN TRANSACTION'); } // -------------------------------------------------------------------- @@ -149,16 +136,9 @@ class CI_DB_sqlite_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('COMMIT'); - return TRUE; + return $this->simple_query('COMMIT'); } // -------------------------------------------------------------------- @@ -168,16 +148,9 @@ class CI_DB_sqlite_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('ROLLBACK'); - return TRUE; + return $this->simple_query('ROLLBACK'); } // -------------------------------------------------------------------- diff --git a/system/database/drivers/sqlite3/index.html b/system/database/drivers/sqlite3/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/sqlite3/index.html +++ b/system/database/drivers/sqlite3/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/sqlite3/sqlite3_driver.php b/system/database/drivers/sqlite3/sqlite3_driver.php index fdbe94939..73e453785 100644 --- a/system/database/drivers/sqlite3/sqlite3_driver.php +++ b/system/database/drivers/sqlite3/sqlite3_driver.php @@ -134,22 +134,10 @@ class CI_DB_sqlite3_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // 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); - return $this->conn_id->exec('BEGIN TRANSACTION'); } @@ -160,14 +148,8 @@ class CI_DB_sqlite3_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->conn_id->exec('END TRANSACTION'); } @@ -178,14 +160,8 @@ class CI_DB_sqlite3_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->conn_id->exec('ROLLBACK'); } @@ -247,17 +223,31 @@ class CI_DB_sqlite3_driver extends CI_DB { // -------------------------------------------------------------------- /** - * Show column query + * Fetch Field Names * - * Generates a platform-specific query string so that the column names can be fetched - * - * @param string $table - * @return string + * @param string $table Table name + * @return array */ - protected function _list_columns($table = '') + public function list_fields($table) { - // Not supported - return FALSE; + // Is there a cached result? + if (isset($this->data_cache['field_names'][$table])) + { + return $this->data_cache['field_names'][$table]; + } + + if (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE) + { + return FALSE; + } + + $this->data_cache['field_names'][$table] = array(); + foreach ($result->result_array() as $row) + { + $this->data_cache['field_names'][$table][] = $row['name']; + } + + return $this->data_cache['field_names'][$table]; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/sqlite3/sqlite3_forge.php b/system/database/drivers/sqlite3/sqlite3_forge.php index 69f65b6f3..24690ba20 100644 --- a/system/database/drivers/sqlite3/sqlite3_forge.php +++ b/system/database/drivers/sqlite3/sqlite3_forge.php @@ -74,7 +74,8 @@ class CI_DB_sqlite3_forge extends CI_DB_forge { if (version_compare($this->db->version(), '3.3', '<')) { - $this->create_table_if = FALSE; + $this->_create_table_if = FALSE; + $this->_drop_table_if = FALSE; } } diff --git a/system/database/drivers/sqlsrv/index.html b/system/database/drivers/sqlsrv/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/sqlsrv/index.html +++ b/system/database/drivers/sqlsrv/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php index 8f17c8f96..414669a4b 100644 --- a/system/database/drivers/sqlsrv/sqlsrv_driver.php +++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php @@ -141,13 +141,14 @@ class CI_DB_sqlsrv_driver extends CI_DB { unset($connection['UID'], $connection['PWD']); } - $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('[', ']'); + if (FALSE !== ($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; } @@ -196,22 +197,10 @@ class CI_DB_sqlsrv_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // 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); - return sqlsrv_begin_transaction($this->conn_id); } @@ -222,14 +211,8 @@ class CI_DB_sqlsrv_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return sqlsrv_commit($this->conn_id); } @@ -240,14 +223,8 @@ class CI_DB_sqlsrv_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return sqlsrv_rollback($this->conn_id); } @@ -274,9 +251,7 @@ class CI_DB_sqlsrv_driver extends CI_DB { */ public function insert_id() { - $query = $this->query('SELECT @@IDENTITY AS insert_id'); - $query = $query->row(); - return $query->insert_id; + return $this->query('SELECT SCOPE_IDENTITY() AS insert_id')->row()->insert_id; } // -------------------------------------------------------------------- @@ -292,10 +267,6 @@ class CI_DB_sqlsrv_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } if ( ! $this->conn_id OR ($info = sqlsrv_server_info($this->conn_id)) === FALSE) { diff --git a/system/database/index.html b/system/database/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/index.html +++ b/system/database/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/fonts/index.html b/system/fonts/index.html index c942a79ce..b702fbc39 100644 --- a/system/fonts/index.html +++ b/system/fonts/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/helpers/array_helper.php b/system/helpers/array_helper.php index e07b52bb5..2ce55b9c4 100644 --- a/system/helpers/array_helper.php +++ b/system/helpers/array_helper.php @@ -62,7 +62,7 @@ if ( ! function_exists('element')) * @param mixed * @return mixed depends on what the array contains */ - function element($item, $array, $default = NULL) + function element($item, array $array, $default = NULL) { return array_key_exists($item, $array) ? $array[$item] : $default; } @@ -99,7 +99,7 @@ if ( ! function_exists('elements')) * @param mixed * @return mixed depends on what the array contains */ - function elements($items, $array, $default = NULL) + function elements($items, array $array, $default = NULL) { $return = array(); diff --git a/system/helpers/captcha_helper.php b/system/helpers/captcha_helper.php index 201987ac8..85bcfb5a0 100644 --- a/system/helpers/captcha_helper.php +++ b/system/helpers/captcha_helper.php @@ -125,9 +125,94 @@ if ( ! function_exists('create_captcha')) if (empty($word)) { $word = ''; - for ($i = 0, $mt_rand_max = strlen($pool) - 1; $i < $word_length; $i++) + $pool_length = strlen($pool); + $rand_max = $pool_length - 1; + + // PHP7 or a suitable polyfill + if (function_exists('random_int')) + { + try + { + for ($i = 0; $i < $word_length; $i++) + { + $word .= $pool[random_int(0, $rand_max)]; + } + } + catch (Exception $e) + { + // This means fallback to the next possible + // alternative to random_int() + $word = ''; + } + } + } + + if (empty($word)) + { + // Nobody will have a larger character pool than + // 256 characters, but let's handle it just in case ... + // + // No, I do not care that the fallback to mt_rand() can + // handle it; if you trigger this, you're very obviously + // trying to break it. -- Narf + if ($pool_length > 256) + { + return FALSE; + } + + // We'll try using the operating system's PRNG first, + // which we can access through CI_Security::get_random_bytes() + $security = get_instance()->security; + + // To avoid numerous get_random_bytes() calls, we'll + // just try fetching as much bytes as we need at once. + if (($bytes = $security->get_random_bytes($pool_length)) !== FALSE) + { + $byte_index = $word_index = 0; + while ($word_index < $word_length) + { + if (($rand_index = unpack('C', $bytes[$byte_index++])) > $rand_max) + { + // Was this the last byte we have? + // If so, try to fetch more. + if ($byte_index === $pool_length) + { + // No failures should be possible if + // the first get_random_bytes() call + // didn't return FALSE, but still ... + for ($i = 0; $i < 5; $i++) + { + if (($bytes = $security->get_random_bytes($pool_length)) === FALSE) + { + continue; + } + + $byte_index = 0; + break; + } + + if ($bytes === FALSE) + { + // Sadly, this means fallback to mt_rand() + $word = ''; + break; + } + } + + continue; + } + + $word .= $pool[$rand_index]; + $word_index++; + } + } + } + + if (empty($word)) + { + for ($i = 0; $i < $word_length; $i++) { - $word .= $pool[mt_rand(0, $mt_rand_max)]; + $word .= $pool[mt_rand(0, $rand_max)]; } } elseif ( ! is_string($word)) diff --git a/system/helpers/download_helper.php b/system/helpers/download_helper.php index 95c94a1b8..73f6456c4 100644 --- a/system/helpers/download_helper.php +++ b/system/helpers/download_helper.php @@ -69,16 +69,14 @@ if ( ! function_exists('force_download')) } elseif ($data === NULL) { - if (@is_file($filename) && ($filesize = @filesize($filename)) !== FALSE) - { - $filepath = $filename; - $filename = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $filename)); - $filename = end($filename); - } - else + if ( ! @is_file($filename) OR ($filesize = @filesize($filename)) === FALSE) { return; } + + $filepath = $filename; + $filename = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $filename)); + $filename = end($filename); } else { @@ -140,14 +138,7 @@ if ( ! function_exists('force_download')) header('Expires: 0'); header('Content-Transfer-Encoding: binary'); header('Content-Length: '.$filesize); - - // Internet Explorer-specific headers - if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== FALSE) - { - header('Cache-Control: no-cache, no-store, must-revalidate'); - } - - header('Pragma: no-cache'); + header('Cache-Control: private, no-transform, no-store, must-revalidate'); // If we have raw data - just dump it if ($data !== NULL) diff --git a/system/helpers/file_helper.php b/system/helpers/file_helper.php index 8b15e60a5..f6cb1629a 100644 --- a/system/helpers/file_helper.php +++ b/system/helpers/file_helper.php @@ -54,7 +54,7 @@ if ( ! function_exists('read_file')) /** * Read File * - * Opens the file specfied in the path and returns it as a string. + * Opens the file specified in the path and returns it as a string. * * @todo Remove in version 3.1+. * @deprecated 3.0.0 It is now just an alias for PHP's native file_get_contents(). @@ -343,7 +343,7 @@ if ( ! function_exists('get_mime_by_extension')) if ( ! is_array($mimes)) { - $mimes =& get_mimes(); + $mimes = get_mimes(); if (empty($mimes)) { diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php index 53ee8eb11..fd807769a 100644 --- a/system/helpers/form_helper.php +++ b/system/helpers/form_helper.php @@ -197,7 +197,7 @@ if ( ! function_exists('form_input')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_input($data = '', $value = '', $extra = '') @@ -208,7 +208,7 @@ if ( ! function_exists('form_input')) 'value' => $value ); - return '<input '._parse_form_attributes($data, $defaults).$extra." />\n"; + return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n"; } } @@ -223,7 +223,7 @@ if ( ! function_exists('form_password')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_password($data = '', $value = '', $extra = '') @@ -245,7 +245,7 @@ if ( ! function_exists('form_upload')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_upload($data = '', $value = '', $extra = '') @@ -253,7 +253,8 @@ if ( ! function_exists('form_upload')) $defaults = array('type' => 'file', 'name' => ''); is_array($data) OR $data = array('name' => $data); $data['type'] = 'file'; - return '<input '._parse_form_attributes($data, $defaults).$extra." />\n"; + + return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n"; } } @@ -266,7 +267,7 @@ if ( ! function_exists('form_textarea')) * * @param mixed $data * @param string $value - * @param string $extra + * @param mixed $extra * @return string */ function form_textarea($data = '', $value = '', $extra = '') @@ -287,7 +288,9 @@ if ( ! function_exists('form_textarea')) unset($data['value']); // textareas don't use the value attribute } - return '<textarea '._parse_form_attributes($data, $defaults).$extra.'>'.html_escape($val)."</textarea>\n"; + return '<textarea '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).'>' + .html_escape($val) + ."</textarea>\n"; } } @@ -301,12 +304,13 @@ if ( ! function_exists('form_multiselect')) * @param string * @param array * @param mixed - * @param string + * @param mixed * @return string */ function form_multiselect($name = '', $options = array(), $selected = array(), $extra = '') { - if ( ! strpos($extra, 'multiple')) + $extra = _attributes_to_string($extra); + if (stripos($extra, 'multiple') === FALSE) { $extra .= ' multiple="multiple"'; } @@ -372,7 +376,7 @@ if ( ! function_exists('form_dropdown')) $extra = _attributes_to_string($extra); - $multiple = (count($selected) > 1 && strpos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : ''; + $multiple = (count($selected) > 1 && stripos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : ''; $form = '<select '.rtrim(_parse_form_attributes($data, $defaults)).$extra.$multiple.">\n"; @@ -420,7 +424,7 @@ if ( ! function_exists('form_checkbox')) * @param mixed * @param string * @param bool - * @param string + * @param mixed * @return string */ function form_checkbox($data = '', $value = '', $checked = FALSE, $extra = '') @@ -450,7 +454,7 @@ if ( ! function_exists('form_checkbox')) unset($defaults['checked']); } - return '<input '._parse_form_attributes($data, $defaults).$extra." />\n"; + return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n"; } } @@ -464,13 +468,14 @@ if ( ! function_exists('form_radio')) * @param mixed * @param string * @param bool - * @param string + * @param mixed * @return string */ function form_radio($data = '', $value = '', $checked = FALSE, $extra = '') { is_array($data) OR $data = array('name' => $data); $data['type'] = 'radio'; + return form_checkbox($data, $value, $checked, $extra); } } @@ -484,7 +489,7 @@ if ( ! function_exists('form_submit')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_submit($data = '', $value = '', $extra = '') @@ -495,7 +500,7 @@ if ( ! function_exists('form_submit')) 'value' => $value ); - return '<input '._parse_form_attributes($data, $defaults).$extra." />\n"; + return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n"; } } @@ -508,7 +513,7 @@ if ( ! function_exists('form_reset')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_reset($data = '', $value = '', $extra = '') @@ -519,7 +524,7 @@ if ( ! function_exists('form_reset')) 'value' => $value ); - return '<input '._parse_form_attributes($data, $defaults).$extra." />\n"; + return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n"; } } @@ -532,7 +537,7 @@ if ( ! function_exists('form_button')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_button($data = '', $content = '', $extra = '') @@ -548,7 +553,9 @@ if ( ! function_exists('form_button')) unset($data['content']); // content is not an attribute } - return '<button '._parse_form_attributes($data, $defaults).$extra.'>'.$content."</button>\n"; + return '<button '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).'>' + .$content + ."</button>\n"; } } diff --git a/system/helpers/index.html b/system/helpers/index.html index c942a79ce..b702fbc39 100644 --- a/system/helpers/index.html +++ b/system/helpers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/helpers/inflector_helper.php b/system/helpers/inflector_helper.php index d8ed45df9..f2890059f 100644 --- a/system/helpers/inflector_helper.php +++ b/system/helpers/inflector_helper.php @@ -133,6 +133,7 @@ if ( ! function_exists('plural')) } $plural_rules = array( + '/(quiz)$/' => '\1zes', // quizzes '/^(ox)$/' => '\1\2en', // ox '/([m|l])ouse$/' => '\1ice', // mouse, louse '/(matr|vert|ind)ix|ex$/' => '\1ices', // matrix, vertex, index diff --git a/system/helpers/path_helper.php b/system/helpers/path_helper.php index c23ec6435..c96d0b8b3 100644 --- a/system/helpers/path_helper.php +++ b/system/helpers/path_helper.php @@ -61,7 +61,7 @@ if ( ! function_exists('set_realpath')) function set_realpath($path, $check_existance = FALSE) { // Security check to make sure the path is NOT a URL. No remote file inclusion! - if (preg_match('#^(http:\/\/|https:\/\/|www\.|ftp|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})#i', $path)) + if (preg_match('#^(http:\/\/|https:\/\/|www\.|ftp)#i', $path) OR filter_var($path, FILTER_VALIDATE_IP) === $path ) { show_error('The path you submitted must be a local server path, not a URL'); } diff --git a/system/helpers/string_helper.php b/system/helpers/string_helper.php index 5860e15fb..637835160 100644 --- a/system/helpers/string_helper.php +++ b/system/helpers/string_helper.php @@ -253,7 +253,7 @@ if ( ! function_exists('increment_string')) */ function increment_string($str, $separator = '_', $first = 1) { - preg_match('/(.+)'.$separator.'([0-9]+)$/', $str, $match); + preg_match('/(.+)'.preg_quote($separator, '/').'([0-9]+)$/', $str, $match); return isset($match[2]) ? $match[1].$separator.($match[2] + 1) : $str.$separator.$first; } } diff --git a/system/helpers/text_helper.php b/system/helpers/text_helper.php index f2290c895..fb47036f2 100644 --- a/system/helpers/text_helper.php +++ b/system/helpers/text_helper.php @@ -254,7 +254,7 @@ if ( ! function_exists('word_censor')) * word you've submitted. * * @param string the text string - * @param string the array of censoered words + * @param string the array of censored words * @param string the optional replacement value * @return string */ diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php index bf623b000..d65f92f1b 100644 --- a/system/helpers/url_helper.php +++ b/system/helpers/url_helper.php @@ -161,7 +161,7 @@ if ( ! function_exists('anchor')) $site_url = is_array($uri) ? site_url($uri) - : preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri); + : (preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri)); if ($title === '') { @@ -474,7 +474,7 @@ if ( ! function_exists('url_title')) * @param string $str Input string * @param string $separator Word separator * (usually '-' or '_') - * @param bool $lowercase Wether to transform the output string to lowercase + * @param bool $lowercase Whether to transform the output string to lowercase * @return string */ function url_title($str, $separator = '-', $lowercase = FALSE) @@ -492,7 +492,7 @@ if ( ! function_exists('url_title')) $trans = array( '&.+?;' => '', - '[^a-z0-9 _-]' => '', + '[^\w\d _-]' => '', '\s+' => $separator, '('.$q_separator.')+' => $separator ); @@ -500,7 +500,7 @@ if ( ! function_exists('url_title')) $str = strip_tags($str); foreach ($trans as $key => $val) { - $str = preg_replace('#'.$key.'#i', $val, $str); + $str = preg_replace('#'.$key.'#i'.(UTF8_ENABLED ? 'u' : ''), $val, $str); } if ($lowercase === TRUE) diff --git a/system/index.html b/system/index.html index c942a79ce..b702fbc39 100644 --- a/system/index.html +++ b/system/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/language/english/index.html b/system/language/english/index.html index c942a79ce..b702fbc39 100644 --- a/system/language/english/index.html +++ b/system/language/english/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/language/index.html b/system/language/index.html index c942a79ce..b702fbc39 100644 --- a/system/language/index.html +++ b/system/language/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Cache/Cache.php b/system/libraries/Cache/Cache.php index 40ac70103..0c87a5628 100644 --- a/system/libraries/Cache/Cache.php +++ b/system/libraries/Cache/Cache.php @@ -100,28 +100,10 @@ class CI_Cache extends CI_Driver_Library { */ public function __construct($config = array()) { - $default_config = array( - 'adapter', - 'memcached' - ); - - foreach ($default_config as $key) - { - if (isset($config[$key])) - { - $param = '_'.$key; - - $this->{$param} = $config[$key]; - } - } - + isset($config['adapter']) && $this->_adapter = $config['adapter']; + isset($config['backup']) && $this->_backup_driver = $config['backup']; isset($config['key_prefix']) && $this->key_prefix = $config['key_prefix']; - if (isset($config['backup']) && in_array($config['backup'], $this->valid_drivers)) - { - $this->_backup_driver = $config['backup']; - } - // If the specified adapter isn't available, check the backup. if ( ! $this->is_supported($this->_adapter)) { @@ -196,7 +178,7 @@ class CI_Cache extends CI_Driver_Library { */ public function increment($id, $offset = 1) { - return $this->{$this->_adapter}->increment($id, $offset); + return $this->{$this->_adapter}->increment($this->key_prefix.$id, $offset); } // ------------------------------------------------------------------------ @@ -210,7 +192,7 @@ class CI_Cache extends CI_Driver_Library { */ public function decrement($id, $offset = 1) { - return $this->{$this->_adapter}->decrement($id, $offset); + return $this->{$this->_adapter}->decrement($this->key_prefix.$id, $offset); } // ------------------------------------------------------------------------ @@ -261,14 +243,13 @@ class CI_Cache extends CI_Driver_Library { */ public function is_supported($driver) { - static $support = array(); + static $support; - if ( ! isset($support[$driver])) + if ( ! isset($support, $support[$driver])) { $support[$driver] = $this->{$driver}->is_supported(); } return $support[$driver]; } - } diff --git a/system/libraries/Cache/drivers/Cache_file.php b/system/libraries/Cache/drivers/Cache_file.php index 68bc1ec96..c046f3b7d 100644 --- a/system/libraries/Cache/drivers/Cache_file.php +++ b/system/libraries/Cache/drivers/Cache_file.php @@ -267,7 +267,7 @@ class CI_Cache_file extends CI_Driver { */ protected function _get($id) { - if ( ! file_exists($this->_cache_path.$id)) + if ( ! is_file($this->_cache_path.$id)) { return FALSE; } diff --git a/system/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php index b90b561c9..59cf4685d 100644 --- a/system/libraries/Cache/drivers/Cache_memcached.php +++ b/system/libraries/Cache/drivers/Cache_memcached.php @@ -68,6 +68,76 @@ class CI_Cache_memcached extends CI_Driver { ) ); + // ------------------------------------------------------------------------ + + /** + * Class constructor + * + * Setup Memcache(d) + * + * @return void + */ + public function __construct() + { + // Try to load memcached server info from the config file. + $CI =& get_instance(); + $defaults = $this->_memcache_conf['default']; + + if ($CI->config->load('memcached', TRUE, TRUE)) + { + if (is_array($CI->config->config['memcached'])) + { + $this->_memcache_conf = array(); + + foreach ($CI->config->config['memcached'] as $name => $conf) + { + $this->_memcache_conf[$name] = $conf; + } + } + } + + if (class_exists('Memcached', FALSE)) + { + $this->_memcached = new Memcached(); + } + elseif (class_exists('Memcache', FALSE)) + { + $this->_memcached = new Memcache(); + } + else + { + log_message('error', 'Cache: Failed to create Memcache(d) object; extension not loaded?'); + } + + foreach ($this->_memcache_conf as $cache_server) + { + isset($cache_server['hostname']) OR $cache_server['hostname'] = $defaults['host']; + isset($cache_server['port']) OR $cache_server['port'] = $defaults['port']; + isset($cache_server['weight']) OR $cache_server['weight'] = $defaults['weight']; + + if (get_class($this->_memcached) === 'Memcache') + { + // Third parameter is persistance and defaults to TRUE. + $this->_memcached->addServer( + $cache_server['hostname'], + $cache_server['port'], + TRUE, + $cache_server['weight'] + ); + } + else + { + $this->_memcached->addServer( + $cache_server['hostname'], + $cache_server['port'], + $cache_server['weight'] + ); + } + } + } + + // ------------------------------------------------------------------------ + /** * Fetch from cache * @@ -205,75 +275,6 @@ class CI_Cache_memcached extends CI_Driver { // ------------------------------------------------------------------------ /** - * Setup memcached. - * - * @return bool - */ - protected function _setup_memcached() - { - // Try to load memcached server info from the config file. - $CI =& get_instance(); - $defaults = $this->_memcache_conf['default']; - - if ($CI->config->load('memcached', TRUE, TRUE)) - { - if (is_array($CI->config->config['memcached'])) - { - $this->_memcache_conf = array(); - - foreach ($CI->config->config['memcached'] as $name => $conf) - { - $this->_memcache_conf[$name] = $conf; - } - } - } - - if (class_exists('Memcached', FALSE)) - { - $this->_memcached = new Memcached(); - } - elseif (class_exists('Memcache', FALSE)) - { - $this->_memcached = new Memcache(); - } - else - { - log_message('error', 'Failed to create object for Memcached Cache; extension not loaded?'); - return FALSE; - } - - foreach ($this->_memcache_conf as $cache_server) - { - isset($cache_server['hostname']) OR $cache_server['hostname'] = $defaults['host']; - isset($cache_server['port']) OR $cache_server['port'] = $defaults['port']; - isset($cache_server['weight']) OR $cache_server['weight'] = $defaults['weight']; - - if (get_class($this->_memcached) === 'Memcache') - { - // Third parameter is persistance and defaults to TRUE. - $this->_memcached->addServer( - $cache_server['hostname'], - $cache_server['port'], - TRUE, - $cache_server['weight'] - ); - } - else - { - $this->_memcached->addServer( - $cache_server['hostname'], - $cache_server['port'], - $cache_server['weight'] - ); - } - } - - return TRUE; - } - - // ------------------------------------------------------------------------ - - /** * Is supported * * Returns FALSE if memcached is not supported on the system. @@ -283,13 +284,6 @@ class CI_Cache_memcached extends CI_Driver { */ public function is_supported() { - if ( ! extension_loaded('memcached') && ! extension_loaded('memcache')) - { - log_message('debug', 'The Memcached Extension must be loaded to use Memcached Cache.'); - return FALSE; - } - - return $this->_setup_memcached(); + return (extension_loaded('memcached') OR extension_loaded('memcache')); } - } diff --git a/system/libraries/Cache/drivers/Cache_redis.php b/system/libraries/Cache/drivers/Cache_redis.php index f2a41cc67..ea0059ff7 100644 --- a/system/libraries/Cache/drivers/Cache_redis.php +++ b/system/libraries/Cache/drivers/Cache_redis.php @@ -79,6 +79,63 @@ class CI_Cache_redis extends CI_Driver // ------------------------------------------------------------------------ /** + * Class constructor + * + * Setup Redis + * + * Loads Redis config file if present. Will halt execution + * if a Redis connection can't be established. + * + * @return void + * @see Redis::connect() + */ + public function __construct() + { + $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 + { + if ($config['socket_type'] === 'unix') + { + $success = $this->_redis->connect($config['socket']); + } + else // tcp socket + { + $success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']); + } + + if ( ! $success) + { + log_message('error', 'Cache: Redis connection failed. Check your configuration.'); + } + + if (isset($config['password']) && ! $this->_redis->auth($config['password'])) + { + log_message('error', 'Cache: Redis authentication failed.'); + } + } + catch (RedisException $e) + { + log_message('error', 'Cache: Redis connection refused ('.$e->getMessage().')'); + } + + // Initialize the index of serialized values. + $serialized = $this->_redis->sMembers('_ci_redis_serialized'); + empty($serialized) OR $this->_serialized = array_flip($serialized); + } + + // ------------------------------------------------------------------------ + + /** * Get cache * * @param string Cache ID @@ -111,7 +168,7 @@ class CI_Cache_redis extends CI_Driver { if (is_array($data) OR is_object($data)) { - if ( ! $this->_redis->sAdd('_ci_redis_serialized', $id)) + if ( ! $this->_redis->sIsMember('_ci_redis_serialized', $id) && ! $this->_redis->sAdd('_ci_redis_serialized', $id)) { return FALSE; } @@ -125,9 +182,7 @@ class CI_Cache_redis extends CI_Driver $this->_redis->sRemove('_ci_redis_serialized', $id); } - return ($ttl) - ? $this->_redis->setex($id, $ttl, $data) - : $this->_redis->set($id, $data); + return $this->_redis->set($id, $data, $ttl); } // ------------------------------------------------------------------------ @@ -223,7 +278,7 @@ class CI_Cache_redis extends CI_Driver { $value = $this->get($key); - if ($value) + if ($value !== FALSE) { return array( 'expire' => time() + $this->_redis->ttl($key), @@ -243,78 +298,7 @@ class CI_Cache_redis extends CI_Driver */ public function is_supported() { - if (extension_loaded('redis')) - { - return $this->_setup_redis(); - } - else - { - log_message('debug', '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 - { - if ($config['socket_type'] === 'unix') - { - $success = $this->_redis->connect($config['socket']); - } - else // tcp socket - { - $success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']); - } - - if ( ! $success) - { - log_message('debug', 'Cache: Redis connection refused. Check the config.'); - return FALSE; - } - } - catch (RedisException $e) - { - log_message('debug', 'Cache: Redis connection refused ('.$e->getMessage().')'); - return FALSE; - } - - if (isset($config['password'])) - { - $this->_redis->auth($config['password']); - } - - // Initialize the index of serialized values. - $serialized = $this->_redis->sMembers('_ci_redis_serialized'); - if ( ! empty($serialized)) - { - $this->_serialized = array_flip($serialized); - } - - return TRUE; + return extension_loaded('redis'); } // ------------------------------------------------------------------------ @@ -333,5 +317,4 @@ class CI_Cache_redis extends CI_Driver $this->_redis->close(); } } - } 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/Cache/drivers/index.html b/system/libraries/Cache/drivers/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/Cache/drivers/index.html +++ b/system/libraries/Cache/drivers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Cache/index.html b/system/libraries/Cache/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/Cache/index.html +++ b/system/libraries/Cache/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Calendar.php b/system/libraries/Calendar.php index 014daac58..f6a0c39c4 100644 --- a/system/libraries/Calendar.php +++ b/system/libraries/Calendar.php @@ -130,11 +130,7 @@ class CI_Calendar { public function __construct($config = array()) { $this->CI =& get_instance(); - - if ( ! in_array('calendar_lang.php', $this->CI->lang->is_loaded, TRUE)) - { - $this->CI->lang->load('calendar'); - } + $this->CI->lang->load('calendar'); empty($config) OR $this->initialize($config); diff --git a/system/libraries/Email.php b/system/libraries/Email.php index 4e0e0cd9f..ebff7567a 100644 --- a/system/libraries/Email.php +++ b/system/libraries/Email.php @@ -405,7 +405,7 @@ class CI_Email { * @param array $config = array() * @return void */ - public function __construct($config = array()) + public function __construct(array $config = array()) { $this->charset = config_item('charset'); @@ -804,11 +804,12 @@ class CI_Email { * * @param string * @param string - * @return void + * @return CI_Email */ public function set_header($header, $value) { $this->_headers[$header] = str_replace(array("\n", "\r"), '', $value); + return $this; } // -------------------------------------------------------------------- @@ -1562,11 +1563,10 @@ class CI_Email { if ($this->charset === 'UTF-8') { - if (MB_ENABLED === TRUE) - { - return mb_encode_mimeheader($str, $this->charset, 'Q', $this->crlf); - } - elseif (ICONV_ENABLED === TRUE) + // Note: We used to have mb_encode_mimeheader() as the first choice + // here, but it turned out to be buggy and unreliable. DO NOT + // re-add it! -- Narf + if (ICONV_ENABLED === TRUE) { $output = @iconv_mime_encode('', $str, array( @@ -1589,6 +1589,10 @@ class CI_Email { $chars = iconv_strlen($str, 'UTF-8'); } + elseif (MB_ENABLED === TRUE) + { + $chars = mb_strlen($str, 'UTF-8'); + } } // We might already have this set for UTF-8 @@ -1868,20 +1872,26 @@ class CI_Email { return FALSE; } - $this->_send_command('from', $this->clean_email($this->_headers['From'])); + if ( ! $this->_send_command('from', $this->clean_email($this->_headers['From']))) + { + return FALSE; + } foreach ($this->_recipients as $val) { - $this->_send_command('to', $val); + if ( ! $this->_send_command('to', $val)) + { + return FALSE; + } } if (count($this->_cc_array) > 0) { foreach ($this->_cc_array as $val) { - if ($val !== '') + if ($val !== '' && ! $this->_send_command('to', $val)) { - $this->_send_command('to', $val); + return FALSE; } } } @@ -1890,14 +1900,17 @@ class CI_Email { { foreach ($this->_bcc_array as $val) { - if ($val !== '') + if ($val !== '' && ! $this->_send_command('to', $val)) { - $this->_send_command('to', $val); + return FALSE; } } } - $this->_send_command('data'); + if ( ! $this->_send_command('data')) + { + return FALSE; + } // perform dot transformation on any lines that begin with a dot $this->_send_data($this->_header_str.preg_replace('/^\./m', '..$1', $this->_finalbody)); @@ -2126,12 +2139,32 @@ class CI_Email { protected function _send_data($data) { $data .= $this->newline; - for ($written = 0, $length = strlen($data); $written < $length; $written += $result) + for ($written = $timestamp = 0, $length = strlen($data); $written < $length; $written += $result) { if (($result = fwrite($this->_smtp_connect, substr($data, $written))) === FALSE) { break; } + // See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951 + elseif ($result === 0) + { + if ($timestamp === 0) + { + $timestamp = time(); + } + elseif ($timestamp < (time() - $this->smtp_timeout)) + { + $result = FALSE; + break; + } + + usleep(250000); + continue; + } + else + { + $timestamp = 0; + } } if ($result === FALSE) @@ -2172,11 +2205,22 @@ class CI_Email { /** * Get Hostname * + * There are only two legal types of hostname - either a fully + * qualified domain name (eg: "mail.example.com") or an IP literal + * (eg: "[1.2.3.4]"). + * + * @link https://tools.ietf.org/html/rfc5321#section-2.3.5 + * @link http://cbl.abuseat.org/namingproblems.html * @return string */ protected function _get_hostname() { - return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain'; + if (isset($_SERVER['SERVER_NAME'])) + { + return $_SERVER['SERVER_NAME']; + } + + return isset($_SERVER['SERVER_ADDR']) ? '['.$_SERVER['SERVER_ADDR'].']' : '[127.0.0.1]'; } // -------------------------------------------------------------------- diff --git a/system/libraries/Encrypt.php b/system/libraries/Encrypt.php index 5faf1f206..a46d4f462 100644 --- a/system/libraries/Encrypt.php +++ b/system/libraries/Encrypt.php @@ -65,7 +65,7 @@ class CI_Encrypt { protected $_hash_type = 'sha1'; /** - * Flag for the existance of mcrypt + * Flag for the existence of mcrypt * * @var bool */ diff --git a/system/libraries/Encryption.php b/system/libraries/Encryption.php index e3e68139a..f3e039881 100644 --- a/system/libraries/Encryption.php +++ b/system/libraries/Encryption.php @@ -121,7 +121,7 @@ class CI_Encryption { ); /** - * List of supported HMAC algorightms + * List of supported HMAC algorithms * * name => digest size pairs * diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php index f161b40e7..a158225ee 100644 --- a/system/libraries/Form_validation.php +++ b/system/libraries/Form_validation.php @@ -198,22 +198,20 @@ class CI_Form_validation { return $this; } - // No fields? Nothing to do... - if ( ! is_string($field) OR $field === '') + // No fields or no rules? Nothing to do... + if ( ! is_string($field) OR $field === '' OR empty($rules)) { return $this; } elseif ( ! is_array($rules)) { // BC: Convert pipe-separated rules string to an array - if (is_string($rules)) - { - $rules = explode('|', $rules); - } - else + if ( ! is_string($rules)) { return $this; } + + $rules = preg_split('/\|(?![^\[]*\])/', $rules); } // If the field label wasn't passed we use the field name @@ -463,7 +461,7 @@ class CI_Form_validation { { $this->_field_data[$field]['postdata'] = $this->_reduce_array($validation_array, $row['keys']); } - elseif (isset($validation_array[$field]) && $validation_array[$field] !== '') + elseif (isset($validation_array[$field])) { $this->_field_data[$field]['postdata'] = $validation_array[$field]; } @@ -620,6 +618,12 @@ class CI_Form_validation { $rules = array(1 => $rule); break; } + elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1])) + { + $callback = TRUE; + $rules = array(array($rule[0], $rule[1])); + break; + } } if ( ! $callback) @@ -817,11 +821,10 @@ class CI_Form_validation { // Callable rules might not have named error messages if ( ! is_string($rule)) { - return; + $line = $this->CI->lang->line('form_validation_error_message_not_set').'(Anonymous function)'; } - // Check if a custom message is defined - if (isset($this->_field_data[$row['field']]['errors'][$rule])) + elseif (isset($this->_field_data[$row['field']]['errors'][$rule])) { $line = $this->_field_data[$row['field']]['errors'][$rule]; } @@ -831,7 +834,7 @@ class CI_Form_validation { // DEPRECATED support for non-prefixed keys && FALSE === ($line = $this->CI->lang->line($rule, FALSE))) { - $line = $this->CI->lang->line('form_validation_error_message_not_set'); + $line = $this->CI->lang->line('form_validation_error_message_not_set').'('.$rule.')'; } } else @@ -872,17 +875,11 @@ class CI_Form_validation { */ protected function _translate_fieldname($fieldname) { - // Do we need to translate the field name? - // We look for the prefix lang: to determine this - if (sscanf($fieldname, 'lang:%s', $line) === 1) + // Do we need to translate the field name? We look for the prefix 'lang:' to determine this + // If we find one, but there's no translation for the string - just return it + if (sscanf($fieldname, 'lang:%s', $line) === 1 && FALSE === ($fieldname = $this->CI->lang->line($line, FALSE))) { - // Were we able to translate the field name? If not we use $line - if (FALSE === ($fieldname = $this->CI->lang->line('form_validation_'.$line)) - // DEPRECATED support for non-prefixed keys - && FALSE === ($fieldname = $this->CI->lang->line($line, FALSE))) - { - return $line; - } + return $line; } return $fieldname; @@ -1151,9 +1148,7 @@ class CI_Form_validation { return FALSE; } - return (MB_ENABLED === TRUE) - ? ($val <= mb_strlen($str)) - : ($val <= strlen($str)); + return ($val <= mb_strlen($str)); } // -------------------------------------------------------------------- @@ -1172,9 +1167,7 @@ class CI_Form_validation { return FALSE; } - return (MB_ENABLED === TRUE) - ? ($val >= mb_strlen($str)) - : ($val >= strlen($str)); + return ($val >= mb_strlen($str)); } // -------------------------------------------------------------------- @@ -1193,9 +1186,7 @@ class CI_Form_validation { return FALSE; } - return (MB_ENABLED === TRUE) - ? (mb_strlen($str) === (int) $val) - : (strlen($str) === (int) $val); + return (mb_strlen($str) === (int) $val); } // -------------------------------------------------------------------- @@ -1595,7 +1586,6 @@ class CI_Form_validation { public function reset_validation() { $this->_field_data = array(); - $this->_config_rules = array(); $this->_error_array = array(); $this->_error_messages = array(); $this->error_string = ''; diff --git a/system/libraries/Ftp.php b/system/libraries/Ftp.php index af45bb55f..2d345c294 100644 --- a/system/libraries/Ftp.php +++ b/system/libraries/Ftp.php @@ -466,7 +466,7 @@ class CI_FTP { /** * Delete a folder and recursively delete everything (including sub-folders) - * containted within it. + * contained within it. * * @param string $filepath * @return bool @@ -490,7 +490,7 @@ class CI_FTP { // so we'll recursively call delete_dir() if ( ! preg_match('#/\.\.?$#', $list[$i]) && ! @ftp_delete($this->conn_id, $list[$i])) { - $this->delete_dir($list[$i]); + $this->delete_dir($filepath.$list[$i]); } } } diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php index e056654bb..e813efd89 100644 --- a/system/libraries/Image_lib.php +++ b/system/libraries/Image_lib.php @@ -779,7 +779,7 @@ class CI_Image_lib { $this->y_axis = 0; } - // Create the image handle + // Create the image handle if ( ! ($src_img = $this->image_create_gd())) { return FALSE; @@ -845,7 +845,7 @@ class CI_Image_lib { */ public function image_process_imagemagick($action = 'resize') { - // Do we have a vaild library path? + // Do we have a vaild library path? if ($this->library_path === '') { $this->set_error('imglib_libpath_invalid'); @@ -1010,7 +1010,7 @@ class CI_Image_lib { // going to have to figure out how to determine the color // of the alpha channel in a future release. - $white = imagecolorallocate($src_img, 255, 255, 255); + $white = imagecolorallocate($src_img, 255, 255, 255); // Rotate it! $dst_img = imagerotate($src_img, $this->rotation_angle, $white); @@ -1055,8 +1055,11 @@ class CI_Image_lib { if ($this->rotation_angle === 'hor') { - for ($i = 0; $i < $height; $i++, $left = 0, $right = $width-1) + for ($i = 0; $i < $height; $i++) { + $left = 0; + $right = $width - 1; + while ($left < $right) { $cl = imagecolorat($src_img, $left, $i); @@ -1072,18 +1075,21 @@ class CI_Image_lib { } else { - for ($i = 0; $i < $width; $i++, $top = 0, $bot = $height-1) + for ($i = 0; $i < $width; $i++) { - while ($top < $bot) + $top = 0; + $bottom = $height - 1; + + while ($top < $bottom) { $ct = imagecolorat($src_img, $i, $top); - $cb = imagecolorat($src_img, $i, $bot); + $cb = imagecolorat($src_img, $i, $bottom); imagesetpixel($src_img, $i, $top, $cb); - imagesetpixel($src_img, $i, $bot, $ct); + imagesetpixel($src_img, $i, $bottom, $ct); $top++; - $bot--; + $bottom--; } } } @@ -1189,7 +1195,7 @@ class CI_Image_lib { $x_axis += $this->orig_width - $wm_width; } - // Build the finalized image + // Build the finalized image if ($wm_img_type === 3 && function_exists('imagealphablending')) { @imagealphablending($src_img, TRUE); @@ -1327,7 +1333,7 @@ class CI_Image_lib { { $y_axis += $this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight / 2); } - + // Set horizontal alignment if ($this->wm_hor_alignment === 'R') { @@ -1337,13 +1343,13 @@ class CI_Image_lib { { $x_axis += floor(($this->orig_width - ($fontwidth * strlen($this->wm_text))) / 2); } - + if ($this->wm_use_drop_shadow) { // Offset from text $x_shad = $x_axis + $this->wm_shadow_distance; $y_shad = $y_axis + $this->wm_shadow_distance; - + /* Set RGB values for shadow * * First character is #, so we don't really need it. @@ -1352,7 +1358,7 @@ class CI_Image_lib { */ $drp_color = str_split(substr($this->wm_shadow_color, 1, 6), 2); $drp_color = imagecolorclosest($src_img, hexdec($drp_color[0]), hexdec($drp_color[1]), hexdec($drp_color[2])); - + // Add the shadow to the source image if ($this->wm_use_truetype) { @@ -1363,7 +1369,7 @@ class CI_Image_lib { imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color); } } - + /* Set RGB values for text * * First character is #, so we don't really need it. @@ -1382,7 +1388,7 @@ class CI_Image_lib { { imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color); } - + // We can preserve transparency for PNG images if ($this->image_type === 3) { @@ -1431,7 +1437,7 @@ class CI_Image_lib { switch ($image_type) { - case 1 : + case 1: if ( ! function_exists('imagecreatefromgif')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported')); @@ -1439,7 +1445,7 @@ class CI_Image_lib { } return imagecreatefromgif($path); - case 2 : + case 2: if ( ! function_exists('imagecreatefromjpeg')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported')); @@ -1447,7 +1453,7 @@ class CI_Image_lib { } return imagecreatefromjpeg($path); - case 3 : + case 3: if ( ! function_exists('imagecreatefrompng')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported')); diff --git a/system/libraries/Javascript/Jquery.php b/system/libraries/Javascript/Jquery.php index 25acceef7..11f2d2361 100644 --- a/system/libraries/Javascript/Jquery.php +++ b/system/libraries/Javascript/Jquery.php @@ -84,7 +84,7 @@ class CI_Jquery extends CI_Javascript { public $jquery_table_sorter_active = FALSE; /** - * JQuery table sorder pager active + * JQuery table sorter pager active * * @var bool */ diff --git a/system/libraries/Javascript/index.html b/system/libraries/Javascript/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/Javascript/index.html +++ b/system/libraries/Javascript/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php index ae36a3b45..45a3cbbce 100644 --- a/system/libraries/Migration.php +++ b/system/libraries/Migration.php @@ -191,7 +191,7 @@ class CI_Migration { * choice * * @param string $target_version Target schema version - * @return mixed TRUE if already latest, FALSE if failed, string if upgraded + * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure */ public function version($target_version) { @@ -294,7 +294,7 @@ class CI_Migration { /** * Sets the schema to the latest migration * - * @return mixed TRUE if already latest, FALSE if failed, string if upgraded + * @return mixed Current version string on success, FALSE on failure */ public function latest() { @@ -318,7 +318,7 @@ class CI_Migration { /** * Sets the schema to the migration version set in config * - * @return mixed TRUE if already current, FALSE if failed, string if upgraded + * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure */ public function current() { diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php index d63f61df6..4d18998b9 100644 --- a/system/libraries/Pagination.php +++ b/system/libraries/Pagination.php @@ -353,7 +353,8 @@ class CI_Pagination { */ public function initialize(array $params = array()) { - if (isset($params['attributes']) && is_array($params['attributes'])) + isset($params['attributes']) OR $params['attributes'] = array(); + if (is_array($params['attributes'])) { $this->_parse_attributes($params['attributes']); unset($params['attributes']); @@ -570,7 +571,7 @@ class CI_Pagination { { $i = ($this->use_page_numbers) ? $uri_page_number - 1 : $uri_page_number - $this->per_page; - $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, ($this->cur_page - 1)); if ($i === $base_page) { @@ -591,11 +592,11 @@ class CI_Pagination { if ($this->display_pages !== FALSE) { // Write the digit links - for ($loop = $start -1; $loop <= $end; $loop++) + for ($loop = $start - 1; $loop <= $end; $loop++) { $i = ($this->use_page_numbers) ? $loop : ($loop * $this->per_page) - $this->per_page; - $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $loop); if ($i >= $base_page) { @@ -613,7 +614,7 @@ class CI_Pagination { else { $append = $this->prefix.$i.$this->suffix; - $output .= $this->num_tag_open.'<a href="'.$base_url.$append.'"'.$attributes.$this->_attr_rel('start').'>' + $output .= $this->num_tag_open.'<a href="'.$base_url.$append.'"'.$attributes.'>' .$loop.'</a>'.$this->num_tag_close; } } @@ -625,7 +626,7 @@ class CI_Pagination { { $i = ($this->use_page_numbers) ? $this->cur_page + 1 : $this->cur_page * $this->per_page; - $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $this->cur_page + 1); $output .= $this->next_tag_open.'<a href="'.$base_url.$this->prefix.$i.$this->suffix.'"'.$attributes .$this->_attr_rel('next').'>'.$this->next_link.'</a>'.$this->next_tag_close; @@ -636,7 +637,7 @@ class CI_Pagination { { $i = ($this->use_page_numbers) ? $num_pages : ($num_pages * $this->per_page) - $this->per_page; - $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $num_pages); $output .= $this->last_tag_open.'<a href="'.$base_url.$this->prefix.$i.$this->suffix.'"'.$attributes.'>' .$this->last_link.'</a>'.$this->last_tag_close; @@ -644,7 +645,7 @@ class CI_Pagination { // Kill double slashes. Note: Sometimes we can end up with a double slash // in the penultimate link so we'll kill all double slashes. - $output = preg_replace('#([^:])//+#', '\\1/', $output); + $output = preg_replace('#([^:"])//+#', '\\1/', $output); // Add the wrapper HTML if exists return $this->full_tag_open.$output.$this->full_tag_close; diff --git a/system/libraries/Profiler.php b/system/libraries/Profiler.php index f35d23faf..1e464d8b0 100644 --- a/system/libraries/Profiler.php +++ b/system/libraries/Profiler.php @@ -100,12 +100,6 @@ class CI_Profiler { $this->CI =& get_instance(); $this->CI->load->language('profiler'); - if (isset($config['query_toggle_count'])) - { - $this->_query_toggle_count = (int) $config['query_toggle_count']; - unset($config['query_toggle_count']); - } - // default all sections to display foreach ($this->_available_sections as $section) { diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php index ba1919b44..05a470d86 100644 --- a/system/libraries/Session/Session.php +++ b/system/libraries/Session/Session.php @@ -94,10 +94,7 @@ class CI_Session { $this->_driver = 'database'; } - if (($class = $this->_ci_load_classes($this->_driver)) === FALSE) - { - return; - } + $class = $this->_ci_load_classes($this->_driver); // Configuration ... $this->_configure($params); @@ -153,7 +150,7 @@ class CI_Session { } elseif ($_SESSION['__ci_last_regenerate'] < (time() - $regenerate_time)) { - $this->sess_regenerate(FALSE); + $this->sess_regenerate((bool) config_item('sess_regenerate_destroy')); } } // Another work-around ... PHP doesn't seem to send the session cookie @@ -230,8 +227,7 @@ class CI_Session { if ( ! class_exists('CI_'.$class, FALSE) && ! class_exists($class, FALSE)) { - log_message('error', "Session: Configured driver '".$driver."' was not found. Aborting."); - return FALSE; + throw new UnexpectedValueException("Session: Configured driver '".$driver."' was not found. Aborting."); } } @@ -314,7 +310,7 @@ class CI_Session { $this->_config = $params; // Security is king - ini_set('session.use_trans_id', 0); + ini_set('session.use_trans_sid', 0); ini_set('session.use_strict_mode', 1); ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); @@ -799,7 +795,7 @@ class CI_Session { /** * Set flashdata * - * Legacy CI_Session compatibiliy method + * Legacy CI_Session compatibility method * * @param mixed $data Session data key or an associative array * @param mixed $value Value to store @@ -873,7 +869,7 @@ class CI_Session { public function set_tempdata($data, $value = NULL, $ttl = 300) { $this->set_userdata($data, $value); - $this->mark_as_temp($data, $ttl); + $this->mark_as_temp(is_array($data) ? array_keys($data) : $data, $ttl); } // ------------------------------------------------------------------------ diff --git a/system/libraries/Session/drivers/Session_database_driver.php b/system/libraries/Session/drivers/Session_database_driver.php index 20cec00fd..72b39d12d 100644 --- a/system/libraries/Session/drivers/Session_database_driver.php +++ b/system/libraries/Session/drivers/Session_database_driver.php @@ -93,6 +93,10 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan { throw new Exception('Configured database connection is persistent. Aborting.'); } + elseif ($this->_db->cache_on) + { + throw new Exception('Configured database connection has cache enabled. Aborting.'); + } $db_driver = $this->_db->dbdriver.(empty($this->_db->subdriver) ? '' : '_'.$this->_db->subdriver); if (strpos($db_driver, 'mysql') !== FALSE) @@ -122,7 +126,7 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan public function open($save_path, $name) { return empty($this->_db->conn_id) - ? ( ! $this->_db->autoinit && $this->_db->db_connect()) + ? (bool) $this->_db->db_connect() : TRUE; } @@ -155,6 +159,10 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan if (($result = $this->_db->get()->row()) === NULL) { + // PHP7 will reuse the same SessionHandler object after + // ID regeneration, so we need to explicitly set this to + // FALSE instead of relying on the default ... + $this->_row_exists = FALSE; $this->_fingerprint = md5(''); return ''; } @@ -319,7 +327,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 d3ef34acd..45da91c46 100644 --- a/system/libraries/Session/drivers/Session_files_driver.php +++ b/system/libraries/Session/drivers/Session_files_driver.php @@ -107,7 +107,7 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle * Sanitizes the save_path directory. * * @param string $save_path Path to session files' directory - * @param string $name Session cookie name, unused + * @param string $name Session cookie name * @return bool */ public function open($save_path, $name) @@ -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) { @@ -324,7 +326,7 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle */ public function gc($maxlifetime) { - if ( ! is_dir($this->_config['save_path']) OR ($files = scandir($this->_config['save_path'])) === FALSE) + if ( ! is_dir($this->_config['save_path']) OR ($directory = opendir($this->_config['save_path'])) === FALSE) { log_message('debug', "Session: Garbage collector couldn't list files under directory '".$this->_config['save_path']."'."); return FALSE; @@ -332,10 +334,16 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle $ts = time() - $maxlifetime; - foreach ($files as $file) + $pattern = sprintf( + '/^%s[0-9a-f]{%d}$/', + preg_quote($this->_config['cookie_name'], '/'), + ($this->_config['match_ip'] === TRUE ? 72 : 40) + ); + + while (($file = readdir($directory)) !== FALSE) { // If the filename doesn't match this pattern, it's either not a session file or is not ours - if ( ! preg_match('/(?:[0-9a-f]{32})?[0-9a-f]{40}$/i', $file) + if ( ! preg_match($pattern, $file) OR ! is_file($this->_config['save_path'].DIRECTORY_SEPARATOR.$file) OR ($mtime = filemtime($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)) === FALSE OR $mtime > $ts) @@ -346,6 +354,8 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle unlink($this->_config['save_path'].DIRECTORY_SEPARATOR.$file); } + closedir($directory); + return TRUE; } diff --git a/system/libraries/Session/drivers/Session_memcached_driver.php b/system/libraries/Session/drivers/Session_memcached_driver.php index 600b8ca66..97b860588 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,10 +322,11 @@ 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.'); + log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.'); return FALSE; } @@ -361,7 +349,7 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa { if ( ! $this->_memcached->delete($this->_lock_key) && $this->_memcached->getResultCode() !== Memcached::RES_NOTFOUND) { - log_message('error', 'Session: Error while trying to free lock for '.$this->_key_prefix.$session_id); + log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key); return FALSE; } diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php index c3c75b3b6..b098cc441 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; @@ -381,7 +367,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle { if ( ! $this->_redis->delete($this->_lock_key)) { - log_message('error', 'Session: Error while trying to free lock for '.$this->_key_prefix.$session_id); + log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key); return FALSE; } diff --git a/system/libraries/Session/drivers/index.html b/system/libraries/Session/drivers/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/Session/drivers/index.html +++ b/system/libraries/Session/drivers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Session/index.html b/system/libraries/Session/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/Session/index.html +++ b/system/libraries/Session/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Unit_test.php b/system/libraries/Unit_test.php index 7b744adc6..3f986f3e8 100644 --- a/system/libraries/Unit_test.php +++ b/system/libraries/Unit_test.php @@ -55,14 +55,14 @@ class CI_Unit_test { * * @var bool */ - public $active = TRUE; + public $active = TRUE; /** * Test results * * @var array */ - public $results = array(); + public $results = array(); /** * Strict comparison flag @@ -71,21 +71,21 @@ class CI_Unit_test { * * @var bool */ - public $strict = FALSE; + public $strict = FALSE; /** * Template * * @var string */ - protected $_template = NULL; + protected $_template = NULL; /** * Template rows * * @var string */ - protected $_template_rows = NULL; + protected $_template_rows = NULL; /** * List of visible test items @@ -93,13 +93,13 @@ class CI_Unit_test { * @var array */ protected $_test_items_visible = array( - 'test_name', - 'test_datatype', - 'res_datatype', - 'result', - 'file', - 'line', - 'notes' + 'test_name', + 'test_datatype', + 'res_datatype', + 'result', + 'file', + 'line', + 'notes' ); // -------------------------------------------------------------------- @@ -152,7 +152,7 @@ class CI_Unit_test { return FALSE; } - if (in_array($expected, array('is_object', 'is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null'), TRUE)) + if (in_array($expected, array('is_object', 'is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null', 'is_resource'), TRUE)) { $expected = str_replace('is_double', 'is_float', $expected); $result = $expected($test); @@ -167,14 +167,14 @@ class CI_Unit_test { $back = $this->_backtrace(); $report = array ( - 'test_name' => $test_name, - 'test_datatype' => gettype($test), - 'res_datatype' => $extype, - 'result' => ($result === TRUE) ? 'passed' : 'failed', - 'file' => $back['file'], - 'line' => $back['line'], - 'notes' => $notes - ); + 'test_name' => $test_name, + 'test_datatype' => gettype($test), + 'res_datatype' => $extype, + 'result' => ($result === TRUE) ? 'passed' : 'failed', + 'file' => $back['file'], + 'line' => $back['line'], + 'notes' => $notes + ); $this->results[] = $report; @@ -291,10 +291,12 @@ class CI_Unit_test { { continue; } - - if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val), FALSE))) + elseif (in_array($key, array('test_name', 'test_datatype', 'test_res_datatype', 'result'), TRUE)) { - $val = $line; + if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val), FALSE))) + { + $val = $line; + } } $temp[$CI->lang->line('ut_'.$key, FALSE)] = $val; @@ -334,9 +336,9 @@ class CI_Unit_test { { $back = debug_backtrace(); return array( - 'file' => (isset($back[1]['file']) ? $back[1]['file'] : ''), - 'line' => (isset($back[1]['line']) ? $back[1]['line'] : '') - ); + '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 f5534a7f7..8a2dec76a 100644 --- a/system/libraries/Upload.php +++ b/system/libraries/Upload.php @@ -397,7 +397,7 @@ class CI_Upload { if ( ! isset($_file)) { - $this->set_error('upload_no_file_selected'); + $this->set_error('upload_no_file_selected', 'debug'); return FALSE; } @@ -416,28 +416,28 @@ class CI_Upload { switch ($error) { case UPLOAD_ERR_INI_SIZE: - $this->set_error('upload_file_exceeds_limit'); + $this->set_error('upload_file_exceeds_limit', 'info'); break; case UPLOAD_ERR_FORM_SIZE: - $this->set_error('upload_file_exceeds_form_limit'); + $this->set_error('upload_file_exceeds_form_limit', 'info'); break; case UPLOAD_ERR_PARTIAL: - $this->set_error('upload_file_partial'); + $this->set_error('upload_file_partial', 'debug'); break; case UPLOAD_ERR_NO_FILE: - $this->set_error('upload_no_file_selected'); + $this->set_error('upload_no_file_selected', 'debug'); break; case UPLOAD_ERR_NO_TMP_DIR: - $this->set_error('upload_no_temp_directory'); + $this->set_error('upload_no_temp_directory', 'error'); break; case UPLOAD_ERR_CANT_WRITE: - $this->set_error('upload_unable_to_write_file'); + $this->set_error('upload_unable_to_write_file', 'error'); break; case UPLOAD_ERR_EXTENSION: - $this->set_error('upload_stopped_by_extension'); + $this->set_error('upload_stopped_by_extension', 'debug'); break; default: - $this->set_error('upload_no_file_selected'); + $this->set_error('upload_no_file_selected', 'debug'); break; } @@ -463,7 +463,7 @@ class CI_Upload { // Is the file type allowed to be uploaded? if ( ! $this->is_allowed_filetype()) { - $this->set_error('upload_invalid_filetype'); + $this->set_error('upload_invalid_filetype', 'debug'); return FALSE; } @@ -485,7 +485,7 @@ class CI_Upload { if ( ! $this->is_allowed_filetype(TRUE)) { - $this->set_error('upload_invalid_filetype'); + $this->set_error('upload_invalid_filetype', 'debug'); return FALSE; } } @@ -499,7 +499,7 @@ class CI_Upload { // Is the file size within the allowed maximum? if ( ! $this->is_allowed_filesize()) { - $this->set_error('upload_invalid_filesize'); + $this->set_error('upload_invalid_filesize', 'info'); return FALSE; } @@ -507,7 +507,7 @@ class CI_Upload { // Note: This can fail if the server has an open_basedir restriction. if ( ! $this->is_allowed_dimensions()) { - $this->set_error('upload_invalid_dimensions'); + $this->set_error('upload_invalid_dimensions', 'info'); return FALSE; } @@ -533,15 +533,9 @@ class CI_Upload { * If it returns false there was a problem. */ $this->orig_name = $this->file_name; - - if ($this->overwrite === FALSE) + if (FALSE === ($this->file_name = $this->set_filename($this->upload_path, $this->file_name))) { - $this->file_name = $this->set_filename($this->upload_path, $this->file_name); - - if ($this->file_name === FALSE) - { - return FALSE; - } + return FALSE; } /* @@ -552,7 +546,7 @@ class CI_Upload { */ if ($this->xss_clean && $this->do_xss_clean() === FALSE) { - $this->set_error('upload_unable_to_write_file'); + $this->set_error('upload_unable_to_write_file', 'error'); return FALSE; } @@ -567,7 +561,7 @@ class CI_Upload { { if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name)) { - $this->set_error('upload_destination_error'); + $this->set_error('upload_destination_error', 'error'); return FALSE; } } @@ -656,7 +650,7 @@ class CI_Upload { $filename = md5(uniqid(mt_rand())).$this->file_ext; } - if ( ! file_exists($path.$filename)) + if ($this->overwrite === TRUE OR ! file_exists($path.$filename)) { return $filename; } @@ -675,7 +669,7 @@ class CI_Upload { if ($new_filename === '') { - $this->set_error('upload_bad_filename'); + $this->set_error('upload_bad_filename', 'debug'); return FALSE; } else @@ -701,6 +695,22 @@ class CI_Upload { // -------------------------------------------------------------------- /** + * Set Maximum File Size + * + * An internal alias to set_max_filesize() to help with configuration + * as initialize() will look for a set_<property_name>() method ... + * + * @param int $n + * @return CI_Upload + */ + protected function set_max_size($n) + { + return $this->set_max_filesize($n); + } + + // -------------------------------------------------------------------- + + /** * Set Maximum File Name Length * * @param int $n @@ -875,7 +885,7 @@ class CI_Upload { if (empty($this->allowed_types) OR ! is_array($this->allowed_types)) { - $this->set_error('upload_no_file_types'); + $this->set_error('upload_no_file_types', 'debug'); return FALSE; } @@ -974,7 +984,7 @@ class CI_Upload { { if ($this->upload_path === '') { - $this->set_error('upload_no_filepath'); + $this->set_error('upload_no_filepath', 'error'); return FALSE; } @@ -985,13 +995,13 @@ class CI_Upload { if ( ! is_dir($this->upload_path)) { - $this->set_error('upload_no_filepath'); + $this->set_error('upload_no_filepath', 'error'); return FALSE; } if ( ! is_really_writable($this->upload_path)) { - $this->set_error('upload_not_writable'); + $this->set_error('upload_not_writable', 'error'); return FALSE; } @@ -1013,7 +1023,7 @@ class CI_Upload { if (count($x) === 1) { - return ''; + return ''; } $ext = ($this->file_ext_tolower) ? strtolower(end($x)) : end($x); @@ -1121,17 +1131,16 @@ class CI_Upload { * @param string $msg * @return CI_Upload */ - public function set_error($msg) + public function set_error($msg, $log_level = 'error') { $this->_CI->lang->load('upload'); is_array($msg) OR $msg = array($msg); - foreach ($msg as $val) { $msg = ($this->_CI->lang->line($val) === FALSE) ? $val : $this->_CI->lang->line($val); $this->error_msg[] = $msg; - log_message('error', $msg); + log_message($log_level, $msg); } return $this; diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php index 8fbc18f04..55555f56f 100644 --- a/system/libraries/Xmlrpc.php +++ b/system/libraries/Xmlrpc.php @@ -735,12 +735,32 @@ class XML_RPC_Client extends CI_Xmlrpc .'Content-Length: '.strlen($msg->payload).$r.$r .$msg->payload; - for ($written = 0, $length = strlen($op); $written < $length; $written += $result) + for ($written = $timestamp = 0, $length = strlen($op); $written < $length; $written += $result) { if (($result = fwrite($fp, substr($op, $written))) === FALSE) { break; } + // See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951 + elseif ($result === 0) + { + if ($timestamp === 0) + { + $timestamp = time(); + } + elseif ($timestamp < (time() - $this->timeout)) + { + $result = FALSE; + break; + } + + usleep(250000); + continue; + } + else + { + $timestamp = 0; + } } if ($result === FALSE) diff --git a/system/libraries/Xmlrpcs.php b/system/libraries/Xmlrpcs.php index af7041337..00d1feca6 100644 --- a/system/libraries/Xmlrpcs.php +++ b/system/libraries/Xmlrpcs.php @@ -223,7 +223,7 @@ class CI_Xmlrpcs extends CI_Xmlrpc { $CI =& get_instance(); if ($CI->input->method() === 'post') { - $data = http_build_query($CI->input->input_stream(NULL, FALSE)); + $data = $CI->input->raw_input_stream; } } @@ -233,14 +233,15 @@ class CI_Xmlrpcs extends CI_Xmlrpc { $parser = xml_parser_create($this->xmlrpc_defencoding); $parser_object = new XML_RPC_Message('filler'); - - $parser_object->xh[$parser] = array( - 'isf' => 0, - 'isf_reason' => '', - 'params' => array(), - 'stack' => array(), - 'valuestack' => array(), - 'method' => '' + $pname = (string) $parser; + + $parser_object->xh[$pname] = array( + 'isf' => 0, + 'isf_reason' => '', + 'params' => array(), + 'stack' => array(), + 'valuestack' => array(), + 'method' => '' ); xml_set_object($parser, $parser_object); @@ -263,7 +264,7 @@ class CI_Xmlrpcs extends CI_Xmlrpc { xml_get_current_line_number($parser))); xml_parser_free($parser); } - elseif ($parser_object->xh[$parser]['isf']) + elseif ($parser_object->xh[$pname]['isf']) { return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']); } @@ -271,17 +272,17 @@ class CI_Xmlrpcs extends CI_Xmlrpc { { xml_parser_free($parser); - $m = new XML_RPC_Message($parser_object->xh[$parser]['method']); + $m = new XML_RPC_Message($parser_object->xh[$pname]['method']); $plist = ''; - for ($i = 0, $c = count($parser_object->xh[$parser]['params']); $i < $c; $i++) + for ($i = 0, $c = count($parser_object->xh[$pname]['params']); $i < $c; $i++) { if ($this->debug === TRUE) { - $plist .= $i.' - '.print_r(get_object_vars($parser_object->xh[$parser]['params'][$i]), TRUE).";\n"; + $plist .= $i.' - '.print_r(get_object_vars($parser_object->xh[$pname]['params'][$i]), TRUE).";\n"; } - $m->addParam($parser_object->xh[$parser]['params'][$i]); + $m->addParam($parser_object->xh[$pname]['params'][$i]); } if ($this->debug === TRUE) diff --git a/system/libraries/Zip.php b/system/libraries/Zip.php index f2f17148b..3e98ac568 100644 --- a/system/libraries/Zip.php +++ b/system/libraries/Zip.php @@ -352,7 +352,7 @@ class CI_Zip { // Set the original directory root for child dir's to use as relative if ($root_path === NULL) { - $root_path = dirname($path).DIRECTORY_SEPARATOR; + $root_path = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, dirname($path)).DIRECTORY_SEPARATOR; } while (FALSE !== ($file = readdir($fp))) diff --git a/system/libraries/index.html b/system/libraries/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/index.html +++ b/system/libraries/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/tests/README.md b/tests/README.md index 47b5241d1..04dfbc3d8 100644 --- a/tests/README.md +++ b/tests/README.md @@ -6,7 +6,7 @@ Status : [![Build Status](https://secure.travis-ci.org/bcit-ci/CodeIgniter.png?b This is the preliminary CodeIgniter testing documentation. It will cover both internal as well as external APIs and the reasoning -behind their implemenation, where appropriate. As with all CodeIgniter +behind their implementation, where appropriate. As with all CodeIgniter documentation, this file should maintain a mostly human readable format to facilitate clean api design. [see http://arrenbrecht.ch/testing/] diff --git a/tests/codeigniter/core/Common_test.php b/tests/codeigniter/core/Common_test.php index 999b49cb3..81a185eaf 100644 --- a/tests/codeigniter/core/Common_test.php +++ b/tests/codeigniter/core/Common_test.php @@ -47,6 +47,11 @@ class Common_test extends CI_TestCase { html_escape('Here is a string containing "quoted" text.'), 'Here is a string containing "quoted" text.' ); + + $this->assertEquals( + html_escape(array('associative' => 'and', array('multi' => 'dimentional'))), + array('associative' => 'and', array('multi' => 'dimentional')) + ); } }
\ No newline at end of file diff --git a/tests/codeigniter/core/Config_test.php b/tests/codeigniter/core/Config_test.php index f125fc6e9..26a5f32f5 100644 --- a/tests/codeigniter/core/Config_test.php +++ b/tests/codeigniter/core/Config_test.php @@ -79,46 +79,33 @@ class Config_test extends CI_TestCase { $old_script_name = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : NULL; $old_script_filename = $_SERVER['SCRIPT_FILENAME']; $old_https = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : NULL; + $old_server_addr = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : NULL; - // Setup server vars for detection - $host = 'test.com'; - $path = '/'; - $script = 'base_test.php'; - $_SERVER['HTTP_HOST'] = $host; - $_SERVER['SCRIPT_NAME'] = $path.$script; - $_SERVER['SCRIPT_FILENAME'] = '/foo/bar/'.$script; - - // Rerun constructor + // The 'Host' header is user input and must not be trusted + $_SERVER['HTTP_HOST'] = 'test.com'; $this->config = new $cls; + $this->assertEquals('http://localhost/', $this->config->base_url()); - // Test plain detected (root) - $this->assertEquals('http://'.$host.$path, $this->config->base_url()); - - // Rerun constructor - $path = '/path/'; - $_SERVER['SCRIPT_NAME'] = $path.$script; - $_SERVER['SCRIPT_FILENAME'] = '/foo/bar/'.$path.$script; + // However, we may fallback to the server's IP address + $_SERVER['SERVER_ADDR'] = '127.0.0.1'; + $_SERVER['SCRIPT_NAME'] = '/base_test.php'; + $_SERVER['SCRIPT_FILENAME'] = '/foo/bar/base_test.php'; $this->config = new $cls; + $this->assertEquals('http://127.0.0.1/', $this->config->base_url()); - // Test plain detected (subfolder) - $this->assertEquals('http://'.$host.$path, $this->config->base_url()); - - // Rerun constructor + // Making sure that HTTPS and URI path are also detected $_SERVER['HTTPS'] = 'on'; + $_SERVER['SCRIPT_NAME'] = '/path/base_test.php'; + $_SERVER['SCRIPT_FILENAME'] = '/foo/bar/path/base_test.php'; $this->config = new $cls; - - // Test secure detected - $this->assertEquals('https://'.$host.$path, $this->config->base_url()); + $this->assertEquals('https://127.0.0.1/path/', $this->config->base_url()); // Restore server vars - if ($old_host === NULL) unset($_SERVER['HTTP_HOST']); - else $_SERVER['HTTP_HOST'] = $old_host; - if ($old_script_name === NULL) unset($_SERVER['SCRIPT_NAME']); - else $_SERVER['SCRIPT_NAME'] = $old_script_name; - if ($old_https === NULL) unset($_SERVER['HTTPS']); - else $_SERVER['HTTPS'] = $old_https; - + $_SERVER['HTTP_HOST'] = $old_host; + $_SERVER['SCRIPT_NAME'] = $old_script_name; $_SERVER['SCRIPT_FILENAME'] = $old_script_filename; + $_SERVER['HTTPS'] = $old_https; + $_SERVER['SERVER_ADDR'] = $old_server_addr; } // -------------------------------------------------------------------- diff --git a/tests/codeigniter/core/Input_test.php b/tests/codeigniter/core/Input_test.php index 21ff6d81f..c56900d22 100644 --- a/tests/codeigniter/core/Input_test.php +++ b/tests/codeigniter/core/Input_test.php @@ -134,6 +134,14 @@ class Input_test extends CI_TestCase { $this->assertEquals('bar', $foo); $this->assertEquals("Hello, i try to <script>alert('Hack');</script> your site", $harm); $this->assertEquals("Hello, i try to [removed]alert('Hack');[removed] your site", $harmless); + + $_SERVER['REQUEST_METHOD'] = 'POST'; + $_POST['foo']['bar'] = 'baz'; + $barArray = array('bar' => 'baz'); + + $this->assertEquals('baz', $this->input->post('foo[bar]')); + $this->assertEquals($barArray, $this->input->post('foo[]')); + $this->assertNull($this->input->post('foo[baz]')); } // -------------------------------------------------------------------- @@ -198,9 +206,22 @@ class Input_test extends CI_TestCase { $this->markTestSkipped('TODO: Find a way to test HTTP headers'); } + // -------------------------------------------------------------------- + + public function test_get_request_header() + { + $this->markTestSkipped('TODO: Find a way to test HTTP headers'); + } + + // -------------------------------------------------------------------- + public function test_ip_address() { + $this->input->ip_address = '127.0.0.1'; + $this->assertEquals('127.0.0.1', $this->input->ip_address()); + // 127.0.0.1 is set in our Bootstrap file + $this->input->ip_address = FALSE; $this->assertEquals('127.0.0.1', $this->input->ip_address()); // Invalid @@ -208,10 +229,47 @@ class Input_test extends CI_TestCase { $this->input->ip_address = FALSE; // reset cached value $this->assertEquals('0.0.0.0', $this->input->ip_address()); - // TODO: Add proxy_ips tests + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; - // Back to reality + // Proxy_ips tests + $this->input->ip_address = FALSE; + $this->ci_set_config('proxy_ips', '127.0.0.3, 127.0.0.4, 127.0.0.2'); + $_SERVER['HTTP_CLIENT_IP'] = '127.0.0.2'; + $this->assertEquals('127.0.0.1', $this->input->ip_address()); + + // Invalid spoof + $this->input->ip_address = FALSE; + $this->ci_set_config('proxy_ips', 'invalid_ip_address'); + $_SERVER['HTTP_CLIENT_IP'] = 'invalid_ip_address'; + $this->assertEquals('127.0.0.1', $this->input->ip_address()); + + $this->input->ip_address = FALSE; + $this->ci_set_config('proxy_ips', 'http://foo/bar/baz, 127.0.0.1/1'); + $_SERVER['HTTP_CLIENT_IP'] = '127.0.0.1'; + $this->assertEquals('127.0.0.1', $this->input->ip_address()); + + $this->input->ip_address = FALSE; + $this->ci_set_config('proxy_ips', 'http://foo/bar/baz, 127.0.0.2'); + $_SERVER['HTTP_CLIENT_IP'] = '127.0.0.2'; + $_SERVER['REMOTE_ADDR'] = '127.0.0.2'; + $this->assertEquals('127.0.0.2', $this->input->ip_address()); + + //IPv6 + $this->input->ip_address = FALSE; + $this->ci_set_config('proxy_ips', 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329/1, FE80:0000:0000:0000:0202:B3FF:FE1E:8300/2'); + $_SERVER['HTTP_CLIENT_IP'] = 'FE80:0000:0000:0000:0202:B3FF:FE1E:8300'; + $_SERVER['REMOTE_ADDR'] = 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329'; + $this->assertEquals('FE80:0000:0000:0000:0202:B3FF:FE1E:8300', $this->input->ip_address()); + + $this->input->ip_address = FALSE; $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; // back to reality } -}
\ No newline at end of file + // -------------------------------------------------------------------- + + public function test_user_agent() + { + $_SERVER['HTTP_USER_AGENT'] = 'test'; + $this->assertEquals('test', $this->input->user_agent()); + } +} diff --git a/tests/codeigniter/core/Lang_test.php b/tests/codeigniter/core/Lang_test.php index 87a71c885..4958f42e1 100644 --- a/tests/codeigniter/core/Lang_test.php +++ b/tests/codeigniter/core/Lang_test.php @@ -32,19 +32,57 @@ class Lang_test extends CI_TestCase { // A language other than english $this->ci_vfs_clone('system/language/english/email_lang.php', 'system/language/german/'); $this->assertTrue($this->lang->load('email', 'german')); - $this->assertEquals('german', $this->lang->is_loaded['email_lang.php'] ); + $this->assertEquals('german', $this->lang->is_loaded['email_lang.php']); + // Non-existent file + $this->setExpectedException( + 'RuntimeException', + 'CI Error: Unable to load the requested language file: language/english/nonexistent_lang.php' + ); + $this->lang->load('nonexistent'); + } + + // -------------------------------------------------------------------- + + public function test_non_alpha_idiom() + { // Non-alpha idiom (should act the same as unspecified language) + // test with existing file $this->ci_vfs_clone('system/language/english/number_lang.php'); - $this->assertTrue($this->lang->load('number')); + $this->ci_vfs_clone('system/language/english/number_lang.php', 'system/language/123funny/'); + $this->assertTrue($this->lang->load('number', '123funny')); $this->assertEquals('Bytes', $this->lang->language['bytes']); - // Non-existent file + // test without existing file + $this->ci_vfs_clone('system/language/english/email_lang.php'); + $this->assertTrue($this->lang->load('email', '456funny')); + $this->assertEquals('You did not specify a SMTP hostname.', $this->lang->language['email_no_hostname']); + } + + // -------------------------------------------------------------------- + + public function test_multiple_file_load() + { + // Multiple files + $this->ci_vfs_clone('system/language/english/profiler_lang.php'); + $files = array( + 0 => 'profiler', + 1 => 'nonexistent' + ); $this->setExpectedException( 'RuntimeException', 'CI Error: Unable to load the requested language file: language/english/nonexistent_lang.php' ); - $this->lang->load('nonexistent'); + $this->lang->load($files, 'english'); + } + + // -------------------------------------------------------------------- + + public function test_alternative_path_load() + { + // Alternative Path + $this->ci_vfs_clone('system/language/english/profiler_lang.php'); + $this->assertTrue($this->lang->load('profiler', 'english', FALSE, TRUE, 'vfs://system/')); } // -------------------------------------------------------------------- @@ -60,5 +98,4 @@ class Lang_test extends CI_TestCase { $this->assertFalse($this->lang->line('nonexistent_string')); $this->assertFalse($this->lang->line(NULL)); } - } diff --git a/tests/codeigniter/core/Loader_test.php b/tests/codeigniter/core/Loader_test.php index 9e2092e05..889ab92e4 100644 --- a/tests/codeigniter/core/Loader_test.php +++ b/tests/codeigniter/core/Loader_test.php @@ -22,6 +22,9 @@ class Loader_test extends CI_TestCase { public function test_library() { + // Test getting CI_Loader object + $this->assertInstanceOf('CI_Loader', $this->load->library(NULL)); + // Create library in VFS $lib = 'unit_test_lib'; $class = 'CI_'.ucfirst($lib); @@ -35,6 +38,13 @@ class Loader_test extends CI_TestCase { $this->assertTrue(class_exists($class), $class.' does not exist'); $this->assertAttributeInstanceOf($class, $lib, $this->ci_obj); + // Create library in VFS + $lib = array('unit_test_lib' => 'unit_test_lib'); + + // Test loading as an array (int). + $this->assertInstanceOf('CI_Loader', $this->load->library($lib)); + $this->assertTrue(class_exists($class), $class.' does not exist'); + // Test a string given to params $this->assertInstanceOf('CI_Loader', $this->load->library($lib, ' ')); @@ -219,7 +229,7 @@ class Loader_test extends CI_TestCase { $this->ci_obj->$obj = new stdClass(); $this->setExpectedException( 'RuntimeException', - 'CI Error: The model name you are loading is the name of a resource that is already being used: '.$obj + 'The model name you are loading is the name of a resource that is already being used: '.$obj ); $this->load->model('not_real', $obj); } @@ -230,7 +240,7 @@ class Loader_test extends CI_TestCase { { $this->setExpectedException( 'RuntimeException', - 'CI Error: Unable to locate the model you have specified: Ci_test_nonexistent_model.php' + 'Unable to locate the model you have specified: Ci_test_nonexistent_model.php' ); $this->load->model('ci_test_nonexistent_model.php'); @@ -319,6 +329,24 @@ class Loader_test extends CI_TestCase { // -------------------------------------------------------------------- + public function test_clear_vars() + { + $key1 = 'foo'; + $val1 = 'bar'; + $key2 = 'boo'; + $val2 = 'hoo'; + $this->assertInstanceOf('CI_Loader', $this->load->vars(array($key1 => $val1))); + $this->assertInstanceOf('CI_Loader', $this->load->vars($key2, $val2)); + $this->assertEquals($val1, $this->load->get_var($key1)); + $this->assertEquals(array($key1 => $val1, $key2 => $val2), $this->load->get_vars()); + + $this->assertInstanceOf('CI_Loader', $this->load->clear_vars()); + $this->assertEquals('', $this->load->get_var($key1)); + $this->assertEquals('', $this->load->get_var($key2)); + } + + // -------------------------------------------------------------------- + public function test_helper() { // Create helper in VFS @@ -443,6 +471,24 @@ class Loader_test extends CI_TestCase { // -------------------------------------------------------------------- + public function test_remove_package_path() + { + $dir = 'third-party'; + $path = APPPATH.$dir.'/'; + $path2 = APPPATH.'another/'; + $paths = $this->load->get_package_paths(TRUE); + + $this->assertInstanceOf('CI_Loader', $this->load->add_package_path($path)); + $this->assertInstanceOf('CI_Loader', $this->load->remove_package_path($path)); + $this->assertEquals($paths, $this->load->get_package_paths(TRUE)); + + $this->assertInstanceOf('CI_Loader', $this->load->add_package_path($path2)); + $this->assertInstanceOf('CI_Loader', $this->load->remove_package_path()); + $this->assertNotContains($path2, $this->load->get_package_paths(TRUE)); + } + + // -------------------------------------------------------------------- + public function test_load_config() { $cfg = 'someconfig'; @@ -511,5 +557,4 @@ class Loader_test extends CI_TestCase { // Verify config calls $this->assertEquals($cfg['config'], $this->ci_obj->config->loaded); } - -}
\ No newline at end of file +} diff --git a/tests/codeigniter/core/Security_test.php b/tests/codeigniter/core/Security_test.php index 402422ff8..2ef822863 100644 --- a/tests/codeigniter/core/Security_test.php +++ b/tests/codeigniter/core/Security_test.php @@ -71,10 +71,171 @@ class Security_test extends CI_TestCase { $this->assertEquals("Hello, i try to [removed]alert('Hack');[removed] your site", $harmless_string); } + // -------------------------------------------------------------------- + + public function test_xss_clean_string_array() + { + $harm_strings = array( + "Hello, i try to <script>alert('Hack');</script> your site", + "Simple clean string", + "Hello, i try to <script>alert('Hack');</script> your site" + ); + + $harmless_strings = $this->security->xss_clean($harm_strings); + + $this->assertEquals("Hello, i try to [removed]alert('Hack');[removed] your site", $harmless_strings[0]); + $this->assertEquals("Simple clean string", $harmless_strings[1]); + $this->assertEquals("Hello, i try to [removed]alert('Hack');[removed] your site", $harmless_strings[2]); + } + + // -------------------------------------------------------------------- + + public function test_xss_clean_image_valid() + { + $harm_string = '<img src="test.png">'; + + $xss_clean_return = $this->security->xss_clean($harm_string, TRUE); + +// $this->assertTrue($xss_clean_return); + } + + // -------------------------------------------------------------------- + + public function test_xss_clean_image_invalid() + { + $harm_string = '<img src=javascript:alert(String.fromCharCode(88,83,83))>'; + + $xss_clean_return = $this->security->xss_clean($harm_string, TRUE); + + $this->assertFalse($xss_clean_return); + } + + // -------------------------------------------------------------------- + public function test_xss_clean_entity_double_encoded() { $input = '<a href="&#106&#97&#118&#97&#115&#99&#114&#105&#112&#116&#58&#99&#111&#110&#102&#105&#114&#109&#40&#49&#41">Clickhere</a>'; - $this->assertEquals('<a >Clickhere</a>', $this->security->xss_clean($input)); + $this->assertEquals('<a>Clickhere</a>', $this->security->xss_clean($input)); + } + + // -------------------------------------------------------------------- + + public function text_xss_clean_js_link_removal() + { + // This one is to prevent a false positive + $this->assertEquals( + "<a href=\"javascrip\n<t\n:alert\n(1)\"\n>", + $this->security->xss_clean("<a href=\"javascrip\n<t\n:alert\n(1)\"\n>") + ); + } + + // -------------------------------------------------------------------- + + public function test_xss_clean_js_img_removal() + { + $input = '<img src="&#106&#97&#118&#97&#115&#99&#114&#105&#112&#116&#58&#99&#111&#110&#102&#105&#114&#109&#40&#49&#41">Clickhere'; + $this->assertEquals('<img>', $this->security->xss_clean($input)); + } + + // -------------------------------------------------------------------- + + public function test_xss_clean_sanitize_naughty_html_tags() + { + $this->assertEquals('<unclosedTag', $this->security->xss_clean('<unclosedTag')); + $this->assertEquals('<blink>', $this->security->xss_clean('<blink>')); + $this->assertEquals('<fubar>', $this->security->xss_clean('<fubar>')); + + $this->assertEquals( + '<img svg=""> src="x">', + $this->security->xss_clean('<img <svg=""> src="x">') + ); + + $this->assertEquals( + '<img src="b on=">on=">"x onerror="alert(1)">', + $this->security->xss_clean('<img src="b on="<x">on=">"x onerror="alert(1)">') + ); + } + + // -------------------------------------------------------------------- + + public function test_xss_clean_sanitize_naughty_html_attributes() + { + $this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo onAttribute="bar">')); + $this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo onAttributeNoQuotes=bar>')); + $this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo onAttributeWithSpaces = bar>')); + $this->assertEquals('<foo prefixOnAttribute="bar">', $this->security->xss_clean('<foo prefixOnAttribute="bar">')); + $this->assertEquals('<foo>onOutsideOfTag=test</foo>', $this->security->xss_clean('<foo>onOutsideOfTag=test</foo>')); + $this->assertEquals('onNoTagAtAll = true', $this->security->xss_clean('onNoTagAtAll = true')); + $this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo fscommand=case-insensitive>')); + $this->assertEquals('<foo xss=removed>', $this->security->xss_clean('<foo seekSegmentTime=whatever>')); + + $this->assertEquals( + '<foo bar=">" baz=\'>\' xss=removed>', + $this->security->xss_clean('<foo bar=">" baz=\'>\' onAfterGreaterThan="quotes">') + ); + $this->assertEquals( + '<foo bar=">" baz=\'>\' xss=removed>', + $this->security->xss_clean('<foo bar=">" baz=\'>\' onAfterGreaterThan=noQuotes>') + ); + + $this->assertEquals( + '<img src="x" on=""> on=<svg> onerror=alert(1)>', + $this->security->xss_clean('<img src="x" on=""> on=<svg> onerror=alert(1)>') + ); + + $this->assertEquals( + '<img src="on=\'">"<svg> onerror=alert(1) onmouseover=alert(1)>', + $this->security->xss_clean('<img src="on=\'">"<svg> onerror=alert(1) onmouseover=alert(1)>') + ); + + $this->assertEquals( + '<img src="x"> on=\'x\' onerror=``,alert(1)>', + $this->security->xss_clean('<img src="x"> on=\'x\' onerror=``,alert(1)>') + ); + + $this->assertEquals( + '<a xss=removed>', + $this->security->xss_clean('<a< onmouseover="alert(1)">') + ); + + $this->assertEquals( + '<img src="x"> on=\'x\' onerror=,xssm()>', + $this->security->xss_clean('<img src="x"> on=\'x\' onerror=,xssm()>') + ); + + $this->assertEquals( + '<image src="<>" xss=removed>', + $this->security->xss_clean('<image src="<>" onerror=\'alert(1)\'>') + ); + + $this->assertEquals( + '<b xss=removed>', + $this->security->xss_clean('<b "=<= onmouseover=alert(1)>') + ); + + $this->assertEquals( + '<b xss=removed xss=removed>1">', + $this->security->xss_clean('<b a=<=" onmouseover="alert(1),1>1">') + ); + + $this->assertEquals( + '<b x=" onmouseover=alert(1)//">', + $this->security->xss_clean('<b "="< x=" onmouseover=alert(1)//">') + ); + } + + // -------------------------------------------------------------------- + + /** + * @depends test_xss_clean_sanitize_naughty_html_tags + * @depends test_xss_clean_sanitize_naughty_html_attributes + */ + public function test_naughty_html_plus_evil_attributes() + { + $this->assertEquals( + '<svg<img src="x" xss=removed>', + $this->security->xss_clean('<svg<img > src="x" onerror="location=/javascript/.source+/:alert/.source+/(1)/.source">') + ); } // -------------------------------------------------------------------- @@ -91,6 +252,17 @@ class Security_test extends CI_TestCase { // -------------------------------------------------------------------- + public function test_get_random_bytes() + { + $length = "invalid"; + $this->assertFalse($this->security->get_random_bytes($length)); + + $length = 10; + $this->assertNotEmpty($this->security->get_random_bytes($length)); + } + + // -------------------------------------------------------------------- + public function test_entity_decode() { $encoded = '<div>Hello <b>Booya</b></div>'; @@ -115,4 +287,54 @@ class Security_test extends CI_TestCase { $this->assertEquals('foo', $safe_filename); } -}
\ No newline at end of file + // -------------------------------------------------------------------- + + 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])); + } + } + + // -------------------------------------------------------------------- + + public function test_csrf_set_hash() + { + // Set cookie for security test + $_COOKIE['ci_csrf_cookie'] = md5(uniqid(mt_rand(), TRUE)); + + // Set config for Security class + $this->ci_set_config('csrf_protection', TRUE); + $this->ci_set_config('csrf_token_name', 'ci_csrf_token'); + + // leave csrf_cookie_name as blank to test _csrf_set_hash function + $this->ci_set_config('csrf_cookie_name', ''); + + $this->security = new Mock_Core_Security(); + + $this->assertNotEmpty($this->security->get_csrf_hash()); + } +} diff --git a/tests/codeigniter/core/compat/password_test.php b/tests/codeigniter/core/compat/password_test.php index c37c6ac0c..8a507d14a 100644 --- a/tests/codeigniter/core/compat/password_test.php +++ b/tests/codeigniter/core/compat/password_test.php @@ -132,7 +132,7 @@ class password_test extends CI_TestCase { $this->assertFalse(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 10, 'foo' => 3))); // invalid: different (lower) cost - $this->assertTrue(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 09))); + $this->assertTrue(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 9))); // invalid: different (higher) cost $this->assertTrue(password_needs_rehash('$2y$10$MTIzNDU2Nzg5MDEyMzQ1Nej0NmcAWSLR.oP7XOR9HD/vjUuOj100y', PASSWORD_BCRYPT, array('cost' => 11))); 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 d5c0dea08..dc4fae986 100644 --- a/tests/codeigniter/database/DB_test.php +++ b/tests/codeigniter/database/DB_test.php @@ -15,7 +15,7 @@ class DB_test extends CI_TestCase { ), )); - $this->setExpectedException('InvalidArgumentException', 'CI Error: Invalid DB driver'); + $this->setExpectedException('RuntimeException', 'CI Error: Invalid DB driver'); Mock_Database_DB::DB($connection->set_dsn('undefined'), TRUE); } @@ -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); @@ -34,6 +42,11 @@ class DB_test extends CI_TestCase { // ------------------------------------------------------------------------ +/* + This test is unusable, because whoever wrote it apparently thought that + an E_WARNING should equal an Exception and based the whole test suite + around that bogus assumption. + public function test_db_failover() { $config = Mock_Database_DB::config(DB_DRIVER); @@ -43,5 +56,6 @@ class DB_test extends CI_TestCase { $this->assertTrue($db instanceof CI_DB); $this->assertTrue($db instanceof CI_DB_Driver); } +*/ }
\ 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 20b7a567c..46a7fa2eb 100644 --- a/tests/codeigniter/database/query_builder/where_test.php +++ b/tests/codeigniter/database/query_builder/where_test.php @@ -123,4 +123,12 @@ class Where_test extends CI_TestCase { $this->assertEquals('Musician', $jobs[1]['name']); } + // ------------------------------------------------------------------------ + + public function test_issue4093() + { + $input = 'bar and baz or qux'; + $sql = $this->db->where('foo', $input)->get_compiled_select('dummy'); + $this->assertEquals("'".$input."'", substr($sql, -20)); + } }
\ 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 53a206825..529e96910 100644 --- a/tests/codeigniter/helpers/email_helper_test.php +++ b/tests/codeigniter/helpers/email_helper_test.php @@ -13,6 +13,7 @@ class Email_helper_test extends CI_TestCase { $this->assertEquals(FALSE, valid_email('test@test@test.com')); $this->assertEquals(TRUE, valid_email('test@test.com')); $this->assertEquals(TRUE, valid_email('my.test@test.com')); + $this->assertEquals(TRUE, valid_email('my.test@subdomain.test.com')); } public function test_send_mail() diff --git a/tests/codeigniter/libraries/Driver_test.php b/tests/codeigniter/libraries/Driver_test.php index d98e8ab98..c62cbee45 100644 --- a/tests/codeigniter/libraries/Driver_test.php +++ b/tests/codeigniter/libraries/Driver_test.php @@ -4,6 +4,7 @@ * Driver library base class unit test */ class Driver_test extends CI_TestCase { + /** * Set up test framework */ @@ -13,11 +14,11 @@ class Driver_test extends CI_TestCase { $this->subclass = 'Mock_Libraries_'; $this->ci_set_config('subclass_prefix', $this->subclass); - // Mock Loader->get_package_paths - $paths = 'get_package_paths'; - $ldr = $this->getMock('CI_Loader', array($paths)); - $ldr->expects($this->any())->method($paths)->will($this->returnValue(array(APPPATH, BASEPATH))); - $this->ci_instance_var('load', $ldr); + // Mock Loader->get_package_paths + $paths = 'get_package_paths'; + $ldr = $this->getMock('CI_Loader', array($paths)); + $ldr->expects($this->any())->method($paths)->will($this->returnValue(array(APPPATH, BASEPATH))); + $this->ci_instance_var('load', $ldr); // Create mock driver library $this->name = 'Driver'; diff --git a/tests/codeigniter/libraries/Form_validation_test.php b/tests/codeigniter/libraries/Form_validation_test.php new file mode 100644 index 000000000..26d82ec93 --- /dev/null +++ b/tests/codeigniter/libraries/Form_validation_test.php @@ -0,0 +1,593 @@ +<?php + +class Form_validation_test extends CI_TestCase { + + public function set_up() + { + $_SERVER['REQUEST_METHOD'] = 'POST'; + + // Create a mock loader since load->helper() looks in the wrong directories for unit tests, + // We'll use CI_TestCase->helper() instead + $loader = $this->getMock('CI_Loader', array('helper')); + + // Same applies for lang + $lang = $this->getMock('CI_Lang', array('load')); + + $this->ci_set_config('charset', 'UTF-8'); + $utf8 = new Mock_Core_Utf8(); + $security = new Mock_Core_Security(); + $input = new Mock_Core_Input($security, $utf8); + + $this->ci_instance_var('lang', $lang); + $this->ci_instance_var('load', $loader); + $this->ci_instance_var('input', $input); + + $this->lang('form_validation'); + $this->helper('form'); + + $this->form_validation = new CI_Form_validation(); + } + + public function test_rule_required() + { + $rules = array(array('field' => 'foo', 'label' => 'foo_label', 'rules' => 'required')); + $this->assertTrue($this->run_rules($rules, array('foo' => 'bar'))); + + $this->assertFalse($this->run_rules($rules, array('foo' => ''))); + $this->assertFalse($this->run_rules($rules, array('foo' => ' '))); + } + + public function test_rule_matches() + { + $rules = array( + array('field' => 'foo', 'label' => 'label', 'rules' => 'required'), + array('field' => 'bar', 'label' => 'label2', 'rules' => 'matches[foo]') + ); + $values_base = array('foo' => 'sample'); + + $this->assertTrue($this->run_rules($rules, array_merge($values_base, array('bar' => '')))); + $this->assertTrue($this->run_rules($rules, array_merge($values_base, array('bar' => 'sample')))); + + $this->assertFalse($this->run_rules($rules, array_merge($values_base, array('bar' => 'Sample')))); + $this->assertFalse($this->run_rules($rules, array_merge($values_base, array('bar' => ' sample')))); + } + + public function test_rule_differs() + { + $rules = array( + array('field' => 'foo', 'label' => 'label', 'rules' => 'required'), + array('field' => 'bar', 'label' => 'label2', 'rules' => 'differs[foo]') + ); + $values_base = array('foo' => 'sample'); + + $this->assertTrue($this->run_rules($rules, array_merge($values_base, array('bar' => 'does_not_match')))); + $this->assertTrue($this->run_rules($rules, array_merge($values_base, array('bar' => 'Sample')))); + $this->assertTrue($this->run_rules($rules, array_merge($values_base, array('bar' => ' sample')))); + + $this->assertFalse($this->run_rules($rules, array_merge($values_base, array('bar' => 'sample')))); + } + + public function test_rule_min_length() + { + $this->assertTrue($this->form_validation->min_length('12345', '5')); + $this->assertTrue($this->form_validation->min_length('test', '0')); + + $this->assertFalse($this->form_validation->min_length('123', '4')); + $this->assertFalse($this->form_validation->min_length('should_fail', 'A')); + $this->assertFalse($this->form_validation->min_length('', '4')); + } + + public function test_rule_max_length() + { + $this->assertTrue($this->form_validation->max_length('', '4')); + $this->assertTrue($this->form_validation->max_length('1234', '4')); + + $this->assertFalse($this->form_validation->max_length('12345', '4')); + $this->assertFalse($this->form_validation->max_length('should_fail', 'A')); + } + + public function test_rule_exact_length() + { + $this->assertTrue($this->form_validation->exact_length('1234', '4')); + + $this->assertFalse($this->form_validation->exact_length('', '3')); + $this->assertFalse($this->form_validation->exact_length('12345', '4')); + $this->assertFalse($this->form_validation->exact_length('123', '4')); + $this->assertFalse($this->form_validation->exact_length('should_fail', 'A')); + } + + public function test_rule_greater_than() + { + $this->assertTrue($this->form_validation->greater_than('-10', '-11')); + $this->assertTrue($this->form_validation->greater_than('10', '9')); + + $this->assertFalse($this->form_validation->greater_than('10', '10')); + $this->assertFalse($this->form_validation->greater_than('10', 'a')); + $this->assertFalse($this->form_validation->greater_than('10a', '10')); + } + + public function test_rule_greater_than_equal_to() + { + $this->assertTrue($this->form_validation->greater_than_equal_to('0', '0')); + $this->assertTrue($this->form_validation->greater_than_equal_to('1', '0')); + + $this->assertFalse($this->form_validation->greater_than_equal_to('-1', '0')); + $this->assertFalse($this->form_validation->greater_than_equal_to('10a', '0')); + } + + public function test_rule_less_than() + { + $this->assertTrue($this->form_validation->less_than('4', '5')); + $this->assertTrue($this->form_validation->less_than('-1', '0')); + + $this->assertFalse($this->form_validation->less_than('4', '4')); + $this->assertFalse($this->form_validation->less_than('10a', '5')); + } + + public function test_rule_less_than_equal_to() + { + $this->assertTrue($this->form_validation->less_than_equal_to('-1', '0')); + $this->assertTrue($this->form_validation->less_than_equal_to('-1', '-1')); + $this->assertTrue($this->form_validation->less_than_equal_to('4', '4')); + + $this->assertFalse($this->form_validation->less_than_equal_to('0', '-1')); + $this->assertFalse($this->form_validation->less_than_equal_to('10a', '0')); + } + + public function test_rule_in_list() + { + $this->assertTrue($this->form_validation->in_list('red', 'red,Blue,123')); + $this->assertTrue($this->form_validation->in_list('Blue', 'red,Blue,123')); + $this->assertTrue($this->form_validation->in_list('123', 'red,Blue,123')); + + $this->assertFalse($this->form_validation->in_list('Red', 'red,Blue,123')); + $this->assertFalse($this->form_validation->in_list(' red', 'red,Blue,123')); + $this->assertFalse($this->form_validation->in_list('1234', 'red,Blue,123')); + } + + public function test_rule_alpha() + { + $this->assertTrue($this->form_validation->alpha('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ')); + + $this->assertFalse($this->form_validation->alpha('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ ')); + $this->assertFalse($this->form_validation->alpha('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ1')); + $this->assertFalse($this->form_validation->alpha('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ*')); + } + + public function test_rule_alpha_numeric() + { + $this->assertTrue($this->form_validation->alpha_numeric('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789')); + + $this->assertFalse($this->form_validation->alpha_numeric('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789\ ')); + $this->assertFalse($this->form_validation->alpha_numeric('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789_')); + } + + public function test_rule_alpha_numeric_spaces() + { + $this->assertTrue($this->form_validation->alpha_numeric_spaces(' abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789')); + + $this->assertFalse($this->form_validation->alpha_numeric_spaces(' abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789_')); + } + + public function test_rule_alpha_dash() + { + $this->assertTrue($this->form_validation->alpha_dash('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789-_')); + + $this->assertFalse($this->form_validation->alpha_dash('abcdefghijklmnopqrstuvwxyzABCDEFGHLIJKLMNOPQRSTUVWXYZ0123456789-_\ ')); + } + + public function test_rule_numeric() + { + $this->assertTrue($this->form_validation->numeric('0')); + $this->assertTrue($this->form_validation->numeric('12314')); + $this->assertTrue($this->form_validation->numeric('-42')); + + $this->assertFalse($this->form_validation->numeric('123a')); + $this->assertFalse($this->form_validation->numeric('--1')); + } + + public function test_rule_integer() + { + $this->assertTrue($this->form_validation->integer('0')); + $this->assertTrue($this->form_validation->integer('42')); + $this->assertTrue($this->form_validation->integer('-1')); + + $this->assertFalse($this->form_validation->integer('124a')); + $this->assertFalse($this->form_validation->integer('1.9')); + $this->assertFalse($this->form_validation->integer('--1')); + } + + public function test_rule_decimal() + { + $this->assertTrue($this->form_validation->decimal('1.0')); + $this->assertTrue($this->form_validation->decimal('-0.98')); + + $this->assertFalse($this->form_validation->decimal('0')); + $this->assertFalse($this->form_validation->decimal('1.0a')); + $this->assertFalse($this->form_validation->decimal('-i')); + $this->assertFalse($this->form_validation->decimal('--1')); + } + + public function test_rule_is_natural() + { + $this->assertTrue($this->form_validation->is_natural('0')); + $this->assertTrue($this->form_validation->is_natural('12')); + + $this->assertFalse($this->form_validation->is_natural('42a')); + $this->assertFalse($this->form_validation->is_natural('-1')); + } + + public function test_rule_is_natural_no_zero() + { + $this->assertTrue($this->form_validation->is_natural_no_zero('42')); + + $this->assertFalse($this->form_validation->is_natural_no_zero('0')); + $this->assertFalse($this->form_validation->is_natural_no_zero('42a')); + $this->assertFalse($this->form_validation->is_natural_no_zero('-1')); + } + + public function test_rule_valid_url() + { + $this->assertTrue($this->form_validation->valid_url('www.codeigniter.com')); + $this->assertTrue($this->form_validation->valid_url('http://codeigniter.eu')); + + $this->assertFalse($this->form_validation->valid_url('htt://www.codeIgniter.com')); + $this->assertFalse($this->form_validation->valid_url('')); + $this->assertFalse($this->form_validation->valid_url('code igniter')); + } + + public function test_rule_valid_email() + { + $this->assertTrue($this->form_validation->valid_email('email@sample.com')); + + $this->assertFalse($this->form_validation->valid_email('valid_email', '@sample.com')); + } + + public function test_rule_valid_emails() + { + $this->assertTrue($this->form_validation->valid_emails('1@sample.com,2@sample.com')); + $this->assertTrue($this->form_validation->valid_emails('email@sample.com')); + + $this->assertFalse($this->form_validation->valid_emails('valid_email', '@sample.com')); + $this->assertFalse($this->form_validation->valid_emails('@sample.com,2@sample.com,validemail@email.ca')); + } + + public function test_rule_valid_ip() + { + $this->assertTrue($this->form_validation->valid_ip('127.0.0.1')); + $this->assertTrue($this->form_validation->valid_ip('127.0.0.1', 'ipv4')); + $this->assertTrue($this->form_validation->valid_ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334')); + $this->assertTrue($this->form_validation->valid_ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334', 'ipv6')); + + $this->assertFalse($this->form_validation->valid_ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334', 'ipv4')); + $this->assertFalse($this->form_validation->valid_ip('127.0.0.1', 'ipv6')); + $this->assertFalse($this->form_validation->valid_ip('H001:0db8:85a3:0000:0000:8a2e:0370:7334')); + $this->assertFalse($this->form_validation->valid_ip('127.0.0.259')); + } + + public function test_rule_valid_base64() + { + $this->assertTrue($this->form_validation->valid_base64(base64_encode('string'))); + + $this->assertFalse($this->form_validation->valid_base64('FA08GG')); + } + + public function test_set_data() + { + // Reset test environment + $_POST = array(); + $this->form_validation->reset_validation(); + $data = array('field' => 'some_data'); + $this->form_validation->set_data($data); + $this->form_validation->set_rules('field', 'label', 'required'); + $this->assertTrue($this->form_validation->run()); + + // Test with empty array + $_POST = array(); + $this->form_validation->reset_validation(); + $data = array('field' => 'some_data'); + $this->form_validation->set_data($data); + // This should do nothing. Old data will still be used + $this->form_validation->set_data(array()); + $this->form_validation->set_rules('field', 'label', 'required'); + $this->assertTrue($this->form_validation->run()); + } + + public function test_set_message() + { + // Reset test environment + $_POST = array(); + $this->form_validation->reset_validation(); + $err_message = 'What a terrible error!'; + $rules = array( + array( + 'field' => 'req_field', + 'label' => 'label', + 'rules' => 'required' + ) + ); + $errorless_data = array('req_field' => 'some text'); + $erroneous_data = array('req_field' => ''); + + $this->form_validation->set_message('required', $err_message); + $this->form_validation->set_data($erroneous_data); + $this->form_validation->set_rules($rules); + $this->form_validation->run(); + $this->assertEquals('<p>'.$err_message.'</p>', $this->form_validation->error('req_field')); + + $this->form_validation->reset_validation(); + $this->form_validation->set_message('required', $err_message); + $this->form_validation->set_data($errorless_data); + $this->form_validation->set_rules($rules); + $this->form_validation->run(); + $this->assertEquals('', $this->form_validation->error('req_field')); + } + + public function test_set_error_delimiters() + { + $this->form_validation->reset_validation(); + $prefix = '<div class="error">'; + $suffix = '</div>'; + $this->form_validation->set_error_delimiters($prefix, $suffix); + $this->form_validation->set_rules('foo', 'label', 'required'); + $_POST = array('foo' => ''); + $this->form_validation->run(); + $error_msg = $this->form_validation->error('foo'); + + $this->assertTrue(strrpos($error_msg, $prefix) === 0); + $this->assertTrue(strrpos($error_msg, $suffix, -strlen($suffix)) === (strlen($error_msg) - strlen($suffix))); + } + + public function test_error_array() + { + $this->form_validation->reset_validation(); + $error_message = 'What a terrible error!'; + $this->form_validation->set_message('required', $error_message); + $this->form_validation->set_rules('foo', 'label', 'required'); + $_POST = array('foo' => ''); + $this->form_validation->run(); + $error_array = $this->form_validation->error_array(); + $this->assertEquals($error_message, $error_array['foo']); + } + + public function test_error_string() + { + $this->form_validation->reset_validation(); + $error_message = 'What a terrible error!'; + $prefix_default = '<foo>'; + $suffix_default = '</foo>'; + $prefix_test = '<bar>'; + $suffix_test = '</bar>'; + $this->form_validation->set_error_delimiters($prefix_default, $suffix_default); + $this->form_validation->set_message('required', $error_message); + $this->form_validation->set_rules('foo', 'label', 'required'); + $_POST = array('foo' => ''); + $this->form_validation->run(); + + $this->assertEquals($prefix_default.$error_message.$suffix_default."\n", $this->form_validation->error_string()); + $this->assertEquals($prefix_test.$error_message.$suffix_default."\n", $this->form_validation->error_string($prefix_test, '')); + $this->assertEquals($prefix_default.$error_message.$suffix_test."\n", $this->form_validation->error_string('', $suffix_test)); + $this->assertEquals($prefix_test.$error_message.$suffix_test."\n", $this->form_validation->error_string($prefix_test, $suffix_test)); + + $this->form_validation->reset_validation(); + $this->form_validation->set_rules('foo', 'label', 'required'); + $_POST = array('foo' => 'bar'); + $this->form_validation->run(); + $this->assertEquals('', $this->form_validation->error_string()); + } + + public function test_run() + { + // form_validation->run() is tested in many of the other unit tests + // This test will only test run(group='') when group is not empty + $config = array( + 'pass' => array( + array( + 'field' => 'username', + 'label' => 'user', + 'rules' => 'alpha_numeric' + ) + ), + 'fail' => array( + array( + 'field' => 'username', + 'label' => 'user', + 'rules' => 'alpha' + ) + ) + ); + $_POST = array('username' => 'foo42'); + $form_validation = new CI_Form_validation($config); + $this->assertTrue($form_validation->run('pass')); + + $form_validation = new CI_Form_validation($config); + $this->assertFalse($form_validation->run('fail')); + } + + public function test_has_rule() + { + $this->form_validation->reset_validation(); + $this->form_validation->set_rules('foo', 'label', 'required'); + + $this->assertTrue($this->form_validation->has_rule('foo')); + $this->assertFalse($this->form_validation->has_rule('bar')); + } + + public function test_set_value() + { + $this->form_validation->reset_validation(); + $default = 'default'; + $this->form_validation->set_rules('foo', 'label', 'required'); + $this->form_validation->set_rules('bar[]', 'label', 'required'); + + // No post data yet: should return the default value provided + $this->assertEquals($default, $this->form_validation->set_value('foo', $default)); + $_POST = array('foo' => 'foo', 'bar' => array('bar1', 'bar2')); + $this->form_validation->run(); + $this->assertEquals('foo', $this->form_validation->set_value('foo', $default)); + $this->assertEquals('bar1', $this->form_validation->set_value('bar[]', $default)); + $this->assertEquals('bar2', $this->form_validation->set_value('bar[]', $default)); + } + + public function test_set_select() + { + // Test 1: No options selected + $this->form_validation->reset_validation(); + $_POST = array(); + $this->form_validation->run(); + + $this->assertEquals('', $this->form_validation->set_select('select', 'foo')); + $this->assertEquals(' selected="selected"', $this->form_validation->set_select('select', 'bar', TRUE)); + + // Test 2: 1 option selected + $this->form_validation->reset_validation(); + $this->form_validation->set_rules('select', 'label', 'alpha_numeric'); + $_POST = array('select' => 'foo'); + $this->form_validation->run(); + + $this->assertEquals(' selected="selected"', $this->form_validation->set_select('select', 'foo')); + $this->assertEquals(' selected="selected"', $this->form_validation->set_select('select', 'foo', TRUE)); + $this->assertEquals('', $this->form_validation->set_select('select', 'bar')); + $this->assertEquals('', $this->form_validation->set_select('select', 'bar', TRUE)); + + // Test 3: Multiple options selected + $this->form_validation->reset_validation(); + $this->form_validation->set_rules('select[]', 'label', 'alpha_numeric'); + $_POST = array('select' => array('foo', 'bar')); + $this->form_validation->run(); + + $this->assertEquals(' selected="selected"', $this->form_validation->set_select('select[]', 'foo')); + $this->assertEquals(' selected="selected"', $this->form_validation->set_select('select[]', 'foo', TRUE)); + $this->assertEquals(' selected="selected"', $this->form_validation->set_select('select[]', 'bar')); + $this->assertEquals(' selected="selected"', $this->form_validation->set_select('select[]', 'bar', TRUE)); + $this->assertEquals('', $this->form_validation->set_select('select[]', 'foobar')); + $this->assertEquals('', $this->form_validation->set_select('select[]', 'foobar', TRUE)); + } + + public function test_set_radio() + { + // Test 1: No options selected + $this->form_validation->reset_validation(); + $_POST = array(); + $this->form_validation->run(); + + $this->assertEquals('', $this->form_validation->set_radio('select', 'foo')); + // Default should only work when no rules are set + $this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select', 'bar', TRUE)); + + // Test 2: 1 option selected + $this->form_validation->reset_validation(); + $this->form_validation->set_rules('select', 'label', 'alpha_numeric'); + $_POST = array('select' => 'foo'); + $this->form_validation->run(); + + $this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select', 'foo')); + $this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select', 'foo', TRUE)); + $this->assertEquals('', $this->form_validation->set_radio('select', 'bar')); + $this->assertEquals('', $this->form_validation->set_radio('select', 'bar', TRUE)); + + // Test 3: Multiple options checked + $this->form_validation->reset_validation(); + $this->form_validation->set_rules('select[]', 'label', 'alpha_numeric'); + $_POST = array('select' => array('foo', 'bar')); + $this->form_validation->run(); + + $this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select[]', 'foo')); + $this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select[]', 'foo', TRUE)); + $this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select[]', 'bar')); + $this->assertEquals(' checked="checked"', $this->form_validation->set_radio('select[]', 'bar', TRUE)); + $this->assertEquals('', $this->form_validation->set_radio('select[]', 'foobar')); + $this->assertEquals('', $this->form_validation->set_radio('select[]', 'foobar', TRUE)); + } + + public function test_set_checkbox() + { + // Test 1: No options selected + $this->form_validation->reset_validation(); + $_POST = array(); + $this->form_validation->run(); + + $this->assertEquals('', $this->form_validation->set_checkbox('select', 'foo')); + $this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select', 'bar', TRUE)); + + // Test 2: 1 option selected + $this->form_validation->reset_validation(); + $this->form_validation->set_rules('select', 'label', 'alpha_numeric'); + $_POST = array('select' => 'foo'); + $this->form_validation->run(); + + $this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select', 'foo')); + $this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select', 'foo', TRUE)); + $this->assertEquals('', $this->form_validation->set_checkbox('select', 'bar')); + $this->assertEquals('', $this->form_validation->set_checkbox('select', 'bar', TRUE)); + + // Test 3: Multiple options selected + $this->form_validation->reset_validation(); + $this->form_validation->set_rules('select[]', 'label', 'alpha_numeric'); + $_POST = array('select' => array('foo', 'bar')); + $this->form_validation->run(); + + $this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select[]', 'foo')); + $this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select[]', 'foo', TRUE)); + $this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select[]', 'bar')); + $this->assertEquals(' checked="checked"', $this->form_validation->set_checkbox('select[]', 'bar', TRUE)); + $this->assertEquals('', $this->form_validation->set_checkbox('select[]', 'foobar')); + $this->assertEquals('', $this->form_validation->set_checkbox('select[]', 'foobar', TRUE)); + } + + public function test_regex_match() + { + $regex = '/f[a-zA-Z]+/'; + $this->assertTrue($this->form_validation->regex_match('foo', $regex)); + $this->assertFalse($this->form_validation->regex_match('bar', $regex)); + } + + public function test_prep_for_form() + { + $this->form_validation->reset_validation(); + $error_msg_unprepped = '<error =\'foobar\'">'; + $error_msg_prepped = '<error ='foobar'">'; + $this->form_validation->set_rules('foo', 'label', 'required', array('required' => $error_msg_unprepped)); + $_POST = array('foo' => ''); + $this->form_validation->run(); + $error_arr = $this->form_validation->error_array(); + + $this->assertEquals('', $this->form_validation->prep_for_form('')); + $this->assertEquals(array('foo' => $error_msg_prepped), $this->form_validation->prep_for_form($error_arr)); + } + + public function test_prep_url() + { + $this->assertEquals('', $this->form_validation->prep_url('')); + $this->assertEquals('http://codeigniter.com', $this->form_validation->prep_url('codeigniter.com')); + $this->assertEquals('https://codeigniter.com', $this->form_validation->prep_url('https://codeigniter.com')); + $this->assertEquals('http://codeigniter.com', $this->form_validation->prep_url('http://codeigniter.com')); + $this->assertEquals('http://www.codeigniter.com', $this->form_validation->prep_url('www.codeigniter.com')); + } + + public function test_encode_php_tags() + { + $this->assertEquals("<?php", $this->form_validation->encode_php_tags('<?php')); + $this->assertEquals('?>', $this->form_validation->encode_php_tags('?>')); + } + + /** + * Run rules + * + * Helper method to set rules and run them at once, not + * an actual test case. + */ + public function run_rules($rules, $values) + { + $this->form_validation->reset_validation(); + $_POST = array(); + + $this->form_validation->set_rules($rules); + foreach ($values as $field => $value) + { + $_POST[$field] = $value; + } + + return $this->form_validation->run(); + } +} 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/tests/mocks/core/security.php b/tests/mocks/core/security.php index a21fc5cb3..6cff85860 100644 --- a/tests/mocks/core/security.php +++ b/tests/mocks/core/security.php @@ -16,6 +16,11 @@ class Mock_Core_Security extends CI_Security { return isset($this->{'_'.$property}) ? $this->{'_'.$property} : NULL; } + public function remove_evil_attributes($str, $is_image) + { + return $this->_remove_evil_attributes($str, $is_image); + } + // Override inaccessible protected method public function __call($method, $params) { diff --git a/tests/mocks/database/db.php b/tests/mocks/database/db.php index 968476dea..00dd884b0 100644 --- a/tests/mocks/database/db.php +++ b/tests/mocks/database/db.php @@ -56,8 +56,7 @@ class Mock_Database_DB { 'char_set' => 'utf8', 'dbcollat' => 'utf8_general_ci', 'swap_pre' => '', - 'autoinit' => TRUE, - 'stricton' => FALSE, + 'stricton' => FALSE ); $config = array_merge($this->config[$group], $params); @@ -134,7 +133,7 @@ class Mock_Database_DB { } catch (Exception $e) { - throw new InvalidArgumentException($e->getMessage()); + throw new RuntimeException($e->getMessage()); } return $db; diff --git a/tests/mocks/database/schema/skeleton.php b/tests/mocks/database/schema/skeleton.php index 5fe5b0f30..888236ff3 100644 --- a/tests/mocks/database/schema/skeleton.php +++ b/tests/mocks/database/schema/skeleton.php @@ -131,7 +131,7 @@ class Mock_Database_Schema_Skeleton { 'job' => array( 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' => 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( diff --git a/user_guide_src/source/_themes/sphinx_rtd_theme/breadcrumbs.html b/user_guide_src/source/_themes/sphinx_rtd_theme/breadcrumbs.html index ff0938e5c..60343661a 100644 --- a/user_guide_src/source/_themes/sphinx_rtd_theme/breadcrumbs.html +++ b/user_guide_src/source/_themes/sphinx_rtd_theme/breadcrumbs.html @@ -2,18 +2,21 @@ <ul class="wy-breadcrumbs"> <li><a href="{{ pathto(master_doc) }}">Docs</a> »</li> {% for doc in parents %} - <li><a href="{{ doc.link|e }}">{{ doc.title }}</a> »</li> + <li><a href="{{ doc.link|e }}">{{ doc.title }}</a> »</li> {% endfor %} <li>{{ title }}</li> - <li class="wy-breadcrumbs-aside"> - {% if display_github %} - <a href="https://github.com/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a> - {% elif display_bitbucket %} - <a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a> - {% elif show_source and has_source and sourcename %} - <a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a> - {% endif %} - </li> + <li class="wy-breadcrumbs-aside"> + {% if display_github %} + <a href="https://github.com/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a> + {% elif display_bitbucket %} + <a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a> + {% elif show_source and has_source and sourcename %} + <a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a> + {% endif %} + </li> + <div style="float:right;margin-left:5px;" id="closeMe"> + <img title="Classic Layout" alt="classic layout" src="data:image/gif;base64,R0lGODlhFAAUAJEAAAAAADMzM////wAAACH5BAUUAAIALAAAAAAUABQAAAImlI+py+0PU5gRBRDM3DxbWoXis42X13USOLauUIqnlsaH/eY6UwAAOw==" /> + </div> </ul> <hr/> </div> diff --git a/user_guide_src/source/_themes/sphinx_rtd_theme/layout.html b/user_guide_src/source/_themes/sphinx_rtd_theme/layout.html index 1203b2f34..20ede7d32 100644 --- a/user_guide_src/source/_themes/sphinx_rtd_theme/layout.html +++ b/user_guide_src/source/_themes/sphinx_rtd_theme/layout.html @@ -77,6 +77,8 @@ <body class="wy-body-for-nav" role="document"> + {% include "pulldown.html" %} + <div class="wy-grid-for-nav"> {# SIDE NAV, TOGGLES ON MOBILE #} diff --git a/user_guide_src/source/_themes/sphinx_rtd_theme/pulldown.html b/user_guide_src/source/_themes/sphinx_rtd_theme/pulldown.html new file mode 100644 index 000000000..7877346d8 --- /dev/null +++ b/user_guide_src/source/_themes/sphinx_rtd_theme/pulldown.html @@ -0,0 +1,17 @@ +<div id="nav"> + <div id="nav_inner"> + {% block ciNav %} + {% set toctree = toctree(maxdepth=2, collapse=False, includehidden=False) %} + {% if toctree %} + <div id="pulldown-menu" class="ciNav"> + {{ toctree }} + </div> + {% endif %} + {% endblock %} + </div> +</div> +<div id="nav2"> + <a href="#" id="openToc"> + <img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAARgAA/+4ADkFkb2JlAGTAAAAAAf/bAIQABAMDAwMDBAMDBAYEAwQGBwUEBAUHCAYGBwYGCAoICQkJCQgKCgwMDAwMCgwMDQ0MDBERERERFBQUFBQUFBQUFAEEBQUIBwgPCgoPFA4ODhQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgAKwCaAwERAAIRAQMRAf/EAHsAAQAABwEBAAAAAAAAAAAAAAABAwQFBgcIAgkBAQAAAAAAAAAAAAAAAAAAAAAQAAEDAwICBwYEAgsAAAAAAAIBAwQAEQUSBiEHkROTVNQWGDFBUVIUCHEiMtOUFWGBobHRQlMkZIRVEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwDSC+ygkOOaUoKigUCgUCgUCgUCgUCgUCgUCgkuGguIP9FBMFb0Hqg7We+3jlmIqqYFf4ub+/QYlnOR/LqIBKGFUbf8qWv971BytQXXE7Y3Lnm3HsFhp2TaZJAdchRXpIgSpdEJWxJEW3xoKV7F5OMy7JkQn2o7D6w33XGjEAkoiqrJEqIiOIiKuhePCgqp22dyYyS3CyWHnQ5joG61HkRnmnTbaFSMhExRVQRRVJU9iUHjE7ez+fJ0MFipmUNhBV8YUd2SoIV9KkjQla9ltegttBdPLW4/qocL+UTfrMiHW4+P9M71shuyrqaHTcxsl7jegpsji8nh5ZwMvDfgTm0RTjSmjYdFCS6KoOIipdFunCgmNYTMv457MMY6U7iI6oMieDDhRm1VbIhuoOkbqtuK0Hpzb+eZcYZexUxt6UyUqK2cd0SdjtgrhOgijcgERUlJOCIl6CpgbP3blRI8XgMjNARAyKNDfeRBdFDBVUAXgQrqH4pxoJTu2NysY97LP4ac1io5q1InHFeGO24LnVKJuKOkSQ/yKir+rh7aCLG1dzypZQI2FnvTgccYOM3FeN0XWERXAUEFVQgQkUktdLpegm+Td3/Xli/L+S/mYNJIOF9G/wBeLKrZHFb0akG6W1WtQWSg3Dyg5e7V3fipE3O4/wCrktyzYA+ufas2LbZIlmnAT2kvuoN1wft95augilglX/tzP3qCu9O3LL/wV/i5v79BvmTADq14UGu91467Z6U9y0HzH/ncj/U/sT/CgynZG7I2NezpZGUjIycJkYkZSG+uQ81pbBNKLxJfjwoMqZ3/ALYHl35AJ7/cuwHcu5k7r1Q5pHetBjquqVVJWGxj9Zrtcl/Ggy3dHMvauR3HFZj5nHNxSyW5JISYDMoIwx8tFIGHZhPNaykGapr6rUAiicEoMG21lMRj8buPAz8xhJrr7uOeiPTCyAwXUaGR1mgozbTusOsFLEiJ7fbQa/h7gcjy2H3V6xppwDNtUSxCJIqp7valBuWVzJ22xuCROXNNZiJkMtms0DbjUkAZjzoDrTMd9dDRI44ZC2YsrYdKWP2WDT2S3N9dNdlRYrGMYc06IURXSYb0igrpWS485xVNS6nF4rwslkoMwnbpgZLB7bmt5uMweAhDEl4B5uSLzzqTnnyVpW2jaJHRMSIjdDiiotvy3DOE5rYTEbkl5yFn28k7JyG4c7AU2HtLH1uKfaiMPI40CdYbpNtmLdwTSn5rewLNld+7TLdeal4WarWBkbVKBjgdElMJJwAAY5fl4kB3b1fp4XvagsGS3FjJfLzDNtS8aeXx7LzT7TyzByQE5PccRGRC0ZRUDRV6y62vbjagzLmJzS2vuPK43JY6aP1TW6Jz+RIWyFtyC06y3EkiiinAo7YCqfq1AqqnGgsOH3lhZO8d1pmcpB8j5XIm9OYlBJSQ/FSS4427DKO0RC8AlcEMhFdViRR1WDWR5t3WXVuL1d106kG9vdeye2g60+1FDyW0shIcXVpyroXt8I8dfd+NB1vioAdWnD3UF1+gD4UFc6CEKpagxXN43rwJLUHz7yX2c8zokt9uHlsPIhA4aRnnHJTLptIS6CNsY7iASpxUUMkReGpfbQW0vtN5pitvrsN28rwtBD0nc0+/Yft5XhaB6TuaXfsP28rwtA9J3NPv2H7eV4Wgek7mn37D9vK8LQPSdzT79h+3leFoHpO5pd+w/byvC0D0nc0u/Yft5XhaB6TuaXfsP28rwtA9J3NLv2H7eV4Wgek7ml37D9vK8LQPSdzS79h+3leFoHpO5p9+w/byvC0E9r7Reazy2HIYVPxkS/CUHVn26cosxyv2g7h89LYmZSXOenvLEQ1YaQ222RATcQCP8rSGqqA8S02W2pQ6FhMoAIlqCtsnwoCpdKClejI4i3Sgtb+GBxVuNBSFt1pV/RQefLjPyUDy4z8lA8uM/JQPLjPyUDy4z8lA8uM/JQPLjPyUDy4z8lA8uM/JQPLjPyUDy4z8lA8utJ/koJ7WCbBU/LQXOPAFq1koK8B0pag90CggtBBf6qB0UDooHRQOigdFA6KB0UDooHRQOigdFA6KB0UDooI0EaBQf//Z" title="Toggle Table of Contents" alt="Toggle Table of Contents" /> + </a> +</div> diff --git a/user_guide_src/source/_themes/sphinx_rtd_theme/static/css/citheme.css b/user_guide_src/source/_themes/sphinx_rtd_theme/static/css/citheme.css new file mode 100644 index 000000000..a2a3b3e91 --- /dev/null +++ b/user_guide_src/source/_themes/sphinx_rtd_theme/static/css/citheme.css @@ -0,0 +1,88 @@ +@import 'theme.css'; + +.highlighted { + padding: 0px !important; + font-weight: inherit !important; + background-color: #f1d40f !important; +} + +#nav { + background-color: #494949; + margin: 0; + padding: 0; + display: none; +} + +#nav2 { + background: url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAARgAA/+4ADkFkb2JlAGTAAAAAAf/bAIQABAMDAwMDBAMDBAYEAwQGBwUEBAUHCAYGBwYGCAoICQkJCQgKCgwMDAwMCgwMDQ0MDBERERERFBQUFBQUFBQUFAEEBQUIBwgPCgoPFA4ODhQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgAMgAzAwERAAIRAQMRAf/EAFkAAQADAQAAAAAAAAAAAAAAAAABBQcIAQEAAAAAAAAAAAAAAAAAAAAAEAABAgYDAAAAAAAAAAAAAAAAAVERAtMEFJRVBxgRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AMRAAAAAAAA7a87dZcCu3e1wHnbrLgV272uA87dZcCu3e1wHnbrLgV272uA87dZcCu3e1wHnbrLgV272uA87dZcCu3e1wN/wJGAYEjAMCRgGBIwDAkYBgSMAwJGAsoIwCCMAgjAIIwCCMAgjAIIwEgAAAAAAAAAAAAAAAAAAAAAAAH//2Q==) repeat-x scroll left top transparent; + margin: 0; + padding: 0 310px 0 0; + text-align: right; + display: none; +} + +#nav_inner { + background-color: transparent; + font-family: Lucida Grande,Verdana,Geneva,sans-serif; + font-size: 11px; + margin: 0; + padding: 8px 12px 0 20px; +} + +div#pulldown-menu { + -moz-column-count: 5; + -moz-column-gap: 20px; + -webkit-column-count: 5; + -webkit-column-gap: 20px; + column-count: 5; + column-gap: 20px; + -webkit-column-rule: 1px groove #b8b8b8; + -moz-column-rule: 1px groove #b8b8b8; + column-rule: 1px groove #b8b8b8; +} + +#pulldown-menu > ul { + padding-top: 10px; + padding-bottom: 10px; + -webkit-column-break-inside: avoid; /*Chrome, Safari*/ + display: table; /*Firefox*/ + break-inside: avoid; /*IE 10+ theoretically*/ +} + +#pulldown-menu ul li.toctree-l2 { + font-size: 0.82em; + margin-left: 20px; + list-style-image: url(data:image/gif;base64,R0lGODlhCwAJALMJAO7u7uTk5PLy8unp6fb29t7e3vj4+Li4uIWFheTk5AAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAkALAAAAAALAAkAAAQoMJ1JqTQ4Z3SI98jHCWSJkByArCyiHkMsIzEX3DeCc0Xv+4hEa5iIAAA7); +} + +#pulldown-menu ul li.toctree-l1 a { + color: #ffffff; + text-decoration: none; + font-size: 12px; + font-family: "Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif; + font-weight: 700; +} + +#pulldown-menu ul li.toctree-l2 a { + text-decoration: none; + font-size: 11px; + line-height: 1.4em; + font-weight: 300; + font-family: Lucida Grande,Verdana,Geneva,sans-serif; + color: #aaaaaa; +} + +/*hide pulldown menu on mobile devices*/ +@media (max-width: 768px) { /*tablet size defined by theme*/ + #closeMe { + display: none; + } + + #pulldown { + display: none; + } + + #openToc { + display: none; + } +}
\ No newline at end of file diff --git a/user_guide_src/source/_themes/sphinx_rtd_theme/static/js/theme.js b/user_guide_src/source/_themes/sphinx_rtd_theme/static/js/theme.js index 66edf2fa2..081d77bdf 100644 --- a/user_guide_src/source/_themes/sphinx_rtd_theme/static/js/theme.js +++ b/user_guide_src/source/_themes/sphinx_rtd_theme/static/js/theme.js @@ -1,118 +1,131 @@ -$( document ).ready(function() { +$(document).ready(function () { // Shift nav in mobile when clicking the menu. - $(document).on('click', "[data-toggle='wy-nav-top']", function() { - $("[data-toggle='wy-nav-shift']").toggleClass("shift"); - $("[data-toggle='rst-versions']").toggleClass("shift"); + $(document).on('click', "[data-toggle='wy-nav-top']", function () { + $("[data-toggle='wy-nav-shift']").toggleClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); }); // Close menu when you click a link. - $(document).on('click', ".wy-menu-vertical .current ul li a", function() { - $("[data-toggle='wy-nav-shift']").removeClass("shift"); - $("[data-toggle='rst-versions']").toggleClass("shift"); + $(document).on('click', ".wy-menu-vertical .current ul li a", function () { + $("[data-toggle='wy-nav-shift']").removeClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); + }); + $(document).on('click', "[data-toggle='rst-current-version']", function () { + $("[data-toggle='rst-versions']").toggleClass("shift-up"); }); - $(document).on('click', "[data-toggle='rst-current-version']", function() { - $("[data-toggle='rst-versions']").toggleClass("shift-up"); - }); // Make tables responsive - $("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>"); - // --- - // START DOC MODIFICATION BY RUFNEX - // v1.0 04.02.2015 - // Add ToogleButton to get FullWidth-View by Johannes Gamperl codeigniter.de - var ciNav = '<style >#nav { background-color: #494949; margin: 0; padding: 0;display:none;}#nav2 { background: url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAARgAA/+4ADkFkb2JlAGTAAAAAAf/bAIQABAMDAwMDBAMDBAYEAwQGBwUEBAUHCAYGBwYGCAoICQkJCQgKCgwMDAwMCgwMDQ0MDBERERERFBQUFBQUFBQUFAEEBQUIBwgPCgoPFA4ODhQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgAMgAzAwERAAIRAQMRAf/EAFkAAQADAQAAAAAAAAAAAAAAAAABBQcIAQEAAAAAAAAAAAAAAAAAAAAAEAABAgYDAAAAAAAAAAAAAAAAAVERAtMEFJRVBxgRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AMRAAAAAAAA7a87dZcCu3e1wHnbrLgV272uA87dZcCu3e1wHnbrLgV272uA87dZcCu3e1wHnbrLgV272uA87dZcCu3e1wN/wJGAYEjAMCRgGBIwDAkYBgSMAwJGAsoIwCCMAgjAIIwCCMAgjAIIwEgAAAAAAAAAAAAAAAAAAAAAAAH//2Q==) repeat-x scroll left top transparent; margin: 0; padding: 0 310px 0 0; text-align: right;display:none;}#nav_inner { background-color: transparent; font-family: Lucida Grande,Verdana,Geneva,sans-serif; font-size: 11px; margin: 0; padding: 8px 12px 0 20px;}table.ciNav { background-color: #494949; width: 100%; }table.ciNav ul { margin: 10px; margin-top:0; padding: 5px; }table.ciNav td li { font-size:0.82em; margin-left: 20px; list-style-image: url(data:image/gif;base64,R0lGODlhCwAJALMJAO7u7uTk5PLy8unp6fb29t7e3vj4+Li4uIWFheTk5AAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAkALAAAAAALAAkAAAQoMJ1JqTQ4Z3SI98jHCWSJkByArCyiHkMsIzEX3DeCc0Xv+4hEa5iIAAA7); }table.ciNav h3 { margin:0; margin-left: 10px; }table.ciNav h3.first { margin-bottom: 20px; }table.ciNav h3 a { color:#fff;text-decoration: none; font-size:12px; }table.ciNav td li a { color:#fff;text-decoration: none; font-size:11px; line-height:1.4em; font-weight: 300; color: #aaa; }table.ciNav td.td_sep {padding-left:20px; background: url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAARgAA/+4ADkFkb2JlAGTAAAAAAf/bAIQABAMDAwMDBAMDBAYEAwQGBwUEBAUHCAYGBwYGCAoICQkJCQgKCgwMDAwMCgwMDQ0MDBERERERFBQUFBQUFBQUFAEEBQUIBwgPCgoPFA4ODhQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgACAACAwERAAIRAQMRAf/EAEsAAQAAAAAAAAAAAAAAAAAAAAcBAQAAAAAAAAAAAAAAAAAAAAAQAQEAAAAAAAAAAAAAAAAAAADVEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwAesg//2Q==) repeat-y scroll left top transparent;}</style>'; - ciNav += '<div style="background:#494949;"><div id="nav"><div id="nav_inner">'; - ciNav += '<table class="ciNav"><tr><td valign="top"><h3 class="first"><a href="../general/welcome.html">Welcome to CodeIgniter</a></h3><h3><a href="../installation/index.html">Installation Instructions</a></h3><ul><li><a href="../installation/downloads.html">Downloading CodeIgniter</a></li><li><a href="../installation/index.html">Installation Instructions</a></li><li><a href="../installation/upgrading.html">Upgrading From a Previous Version</a></li><li><a href="../installation/troubleshooting.html">Troubleshooting</a></li></ul><h3><a href="../overview/index.html">CodeIgniter Overview</a></h3><ul><li><a href="../overview/getting_started.html">Getting Started</a></li><li><a href="../overview/at_a_glance.html">CodeIgniter at a Glance</a></li><li><a href="../overview/features.html">Supported Features</a></li><li><a href="../overview/appflow.html">Application Flow Chart</a></li><li><a href="../overview/mvc.html">Model-View-Controller</a></li><li><a href="../overview/goals.html">Architectural Goals</a></li></ul><h3><a href="../tutorial/index.html">Tutorial</a></h3><ul><li><a href="../tutorial/static_pages.html">Static pages</a></li><li><a href="../tutorial/news_section.html">News section</a></li><li><a href="../tutorial/create_news_items.html">Create news items</a></li><li><a href="../tutorial/conclusion.html">Conclusion</a></li></ul><h3><a href="../contributing/index.html">Contributing to CodeIgniter</a></h3><ul><li class="toctree-l2"><a href="../documentation/index.html">Writing CodeIgniter Documentation</a></li><li class="toctree-l2"><a href="../DCO.html">Developer’s Certificate of Origin 1.1</a></li></ul></td><td valign="top" class="td_sep"><h3><a href="../general/index.html">General Topics</a></h3><ul><li><a href="../general/urls.html">CodeIgniter URLs</a></li><li><a href="../general/controllers.html">Controllers</a></li><li><a href="../general/reserved_names.html">Reserved Names</a></li><li><a href="../general/views.html">Views</a></li><li><a href="../general/models.html">Models</a></li><li><a href="../general/helpers.html">Helpers</a></li><li><a href="../general/libraries.html">Using CodeIgniter Libraries</a></li><li><a href="../general/creating_libraries.html">Creating Libraries</a></li><li><a href="../general/drivers.html">Using CodeIgniter Drivers</a></li><li><a href="../general/creating_drivers.html">Creating Drivers</a></li><li><a href="../general/core_classes.html">Creating Core System Classes</a></li><li><a href="../general/ancillary_classes.html">Creating Ancillary Classes</a></li><li><a href="../general/hooks.html">Hooks - Extending the Framework Core</a></li><li><a href="../general/autoloader.html">Auto-loading Resources</a></li><li><a href="../general/common_functions.html">Common Functions</a></li><li><a href="../general/compatibility_functions.html">Compatibility Functions</a></li><li><a href="../general/routing.html">URI Routing</a></li><li><a href="../general/errors.html">Error Handling</a></li><li><a href="../general/caching.html">Caching</a></li><li><a href="../general/profiling.html">Profiling Your Application</a></li><li><a href="../general/cli.html">Running via the CLI</a></li><li><a href="../general/managing_apps.html">Managing your Applications</a></li><li><a href="../general/environments.html">Handling Multiple Environments</a></li><li><a href="../general/alternative_php.html">Alternate PHP Syntax for View Files</a></li><li><a href="../general/security.html">Security</a></li><li><a href="../general/styleguide.html">PHP Style Guide</a></li></ul></td><td valign="top" class="td_sep"><h3><a href="../libraries/index.html">Libraries</a></h3><ul><li><a href="../libraries/benchmark.html">Benchmarking Class</a></li><li><a href="../libraries/caching.html">Caching Driver</a></li><li><a href="../libraries/calendar.html">Calendaring Class</a></li><li><a href="../libraries/cart.html">Shopping Cart Class</a></li><li><a href="../libraries/config.html">Config Class</a></li><li><a href="../libraries/email.html">Email Class</a></li><li><a href="../libraries/encrypt.html">Encrypt Class</a></li><li><a href="../libraries/encryption.html">Encryption Library</a></li><li><a href="../libraries/file_uploading.html">File Uploading Class</a></li><li><a href="../libraries/form_validation.html">Form Validation</a></li><li><a href="../libraries/ftp.html">FTP Class</a></li><li><a href="../libraries/image_lib.html">Image Manipulation Class</a></li><li><a href="../libraries/input.html">Input Class</a></li><li><a href="../libraries/javascript.html">Javascript Class</a></li><li><a href="../libraries/language.html">Language Class</a></li><li><a href="../libraries/loader.html">Loader Class</a></li><li><a href="../libraries/migration.html">Migrations Class</a></li><li><a href="../libraries/output.html">Output Class</a></li><li><a href="../libraries/pagination.html">Pagination Class</a></li><li><a href="../libraries/parser.html">Template Parser Class</a></li><li><a href="../libraries/security.html">Security Class</a></li><li><a href="../libraries/sessions.html">Session Library</a></li><li><a href="../libraries/table.html">HTML Table Class</a></li><li><a href="../libraries/trackback.html">Trackback Class</a></li><li><a href="../libraries/typography.html">Typography Class</a></li><li><a href="../libraries/unit_testing.html">Unit Testing Class</a></li><li><a href="../libraries/uri.html">URI Class</a></li><li><a href="../libraries/user_agent.html">User Agent Class</a></li><li><a href="../libraries/xmlrpc.html">XML-RPC and XML-RPC Server Classes</a></li><li><a href="../libraries/zip.html">Zip Encoding Class</a></li></ul></td><td valign="top" class="td_sep"><h3><a href="../database/index.html">Database Reference</a></h3><ul><li><a href="../database/examples.html">Quick Start: Usage Examples</a></li><li><a href="../database/configuration.html">Database Configuration</a></li><li><a href="../database/connecting.html">Connecting to a Database</a></li><li><a href="../database/queries.html">Running Queries</a></li><li><a href="../database/results.html">Generating Query Results</a></li><li><a href="../database/helpers.html">Query Helper Functions</a></li><li><a href="../database/query_builder.html">Query Builder Class</a></li><li><a href="../database/transactions.html">Transactions</a></li><li><a href="../database/metadata.html">Getting MetaData</a></li><li><a href="../database/call_function.html">Custom Function Calls</a></li><li><a href="../database/caching.html">Query Caching</a></li><li><a href="../database/forge.html">Database Manipulation with Database Forge</a></li><li><a href="../database/utilities.html">Database Utilities Class</a></li><li><a href="../database/db_driver_reference.html">Database Driver Reference</a></li></ul></td><td valign="top" class="td_sep"><h3><a href="../helpers/index.html">Helpers</a></h3><ul><li><a href="../helpers/array_helper.html">Array Helper</a></li><li><a href="../helpers/captcha_helper.html">CAPTCHA Helper</a></li><li><a href="../helpers/cookie_helper.html">Cookie Helper</a></li><li><a href="../helpers/date_helper.html">Date Helper</a></li><li><a href="../helpers/directory_helper.html">Directory Helper</a></li><li><a href="../helpers/download_helper.html">Download Helper</a></li><li><a href="../helpers/email_helper.html">Email Helper</a></li><li><a href="../helpers/file_helper.html">File Helper</a></li><li><a href="../helpers/form_helper.html">Form Helper</a></li><li><a href="../helpers/html_helper.html">HTML Helper</a></li><li><a href="../helpers/inflector_helper.html">Inflector Helper</a></li><li><a href="../helpers/language_helper.html">Language Helper</a></li><li><a href="../helpers/number_helper.html">Number Helper</a></li><li><a href="../helpers/path_helper.html">Path Helper</a></li><li><a href="../helpers/security_helper.html">Security Helper</a></li><li><a href="../helpers/smiley_helper.html">Smiley Helper</a></li><li><a href="../helpers/string_helper.html">String Helper</a></li><li><a href="../helpers/text_helper.html">Text Helper</a></li><li><a href="../helpers/typography_helper.html">Typography Helper</a></li><li><a href="../helpers/url_helper.html">URL Helper</a></li><li><a href="../helpers/xml_helper.html">XML Helper</a></li></ul></td></tr></table>'; - ciNav += '</div></div><div id="nav2"><a name="top"></a><a href="#" id="openToc"><img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAARgAA/+4ADkFkb2JlAGTAAAAAAf/bAIQABAMDAwMDBAMDBAYEAwQGBwUEBAUHCAYGBwYGCAoICQkJCQgKCgwMDAwMCgwMDQ0MDBERERERFBQUFBQUFBQUFAEEBQUIBwgPCgoPFA4ODhQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgAKwCaAwERAAIRAQMRAf/EAHsAAQAABwEBAAAAAAAAAAAAAAABAwQFBgcIAgkBAQAAAAAAAAAAAAAAAAAAAAAQAAEDAwICBwYEAgsAAAAAAAIBAwQAEQUSBiEHkROTVNQWGDFBUVIUCHEiMtOUFWGBobHRQlMkZIRVEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwDSC+ygkOOaUoKigUCgUCgUCgUCgUCgUCgUCgkuGguIP9FBMFb0Hqg7We+3jlmIqqYFf4ub+/QYlnOR/LqIBKGFUbf8qWv971BytQXXE7Y3Lnm3HsFhp2TaZJAdchRXpIgSpdEJWxJEW3xoKV7F5OMy7JkQn2o7D6w33XGjEAkoiqrJEqIiOIiKuhePCgqp22dyYyS3CyWHnQ5joG61HkRnmnTbaFSMhExRVQRRVJU9iUHjE7ez+fJ0MFipmUNhBV8YUd2SoIV9KkjQla9ltegttBdPLW4/qocL+UTfrMiHW4+P9M71shuyrqaHTcxsl7jegpsji8nh5ZwMvDfgTm0RTjSmjYdFCS6KoOIipdFunCgmNYTMv457MMY6U7iI6oMieDDhRm1VbIhuoOkbqtuK0Hpzb+eZcYZexUxt6UyUqK2cd0SdjtgrhOgijcgERUlJOCIl6CpgbP3blRI8XgMjNARAyKNDfeRBdFDBVUAXgQrqH4pxoJTu2NysY97LP4ac1io5q1InHFeGO24LnVKJuKOkSQ/yKir+rh7aCLG1dzypZQI2FnvTgccYOM3FeN0XWERXAUEFVQgQkUktdLpegm+Td3/Xli/L+S/mYNJIOF9G/wBeLKrZHFb0akG6W1WtQWSg3Dyg5e7V3fipE3O4/wCrktyzYA+ufas2LbZIlmnAT2kvuoN1wft95augilglX/tzP3qCu9O3LL/wV/i5v79BvmTADq14UGu91467Z6U9y0HzH/ncj/U/sT/CgynZG7I2NezpZGUjIycJkYkZSG+uQ81pbBNKLxJfjwoMqZ3/ALYHl35AJ7/cuwHcu5k7r1Q5pHetBjquqVVJWGxj9Zrtcl/Ggy3dHMvauR3HFZj5nHNxSyW5JISYDMoIwx8tFIGHZhPNaykGapr6rUAiicEoMG21lMRj8buPAz8xhJrr7uOeiPTCyAwXUaGR1mgozbTusOsFLEiJ7fbQa/h7gcjy2H3V6xppwDNtUSxCJIqp7valBuWVzJ22xuCROXNNZiJkMtms0DbjUkAZjzoDrTMd9dDRI44ZC2YsrYdKWP2WDT2S3N9dNdlRYrGMYc06IURXSYb0igrpWS485xVNS6nF4rwslkoMwnbpgZLB7bmt5uMweAhDEl4B5uSLzzqTnnyVpW2jaJHRMSIjdDiiotvy3DOE5rYTEbkl5yFn28k7JyG4c7AU2HtLH1uKfaiMPI40CdYbpNtmLdwTSn5rewLNld+7TLdeal4WarWBkbVKBjgdElMJJwAAY5fl4kB3b1fp4XvagsGS3FjJfLzDNtS8aeXx7LzT7TyzByQE5PccRGRC0ZRUDRV6y62vbjagzLmJzS2vuPK43JY6aP1TW6Jz+RIWyFtyC06y3EkiiinAo7YCqfq1AqqnGgsOH3lhZO8d1pmcpB8j5XIm9OYlBJSQ/FSS4427DKO0RC8AlcEMhFdViRR1WDWR5t3WXVuL1d106kG9vdeye2g60+1FDyW0shIcXVpyroXt8I8dfd+NB1vioAdWnD3UF1+gD4UFc6CEKpagxXN43rwJLUHz7yX2c8zokt9uHlsPIhA4aRnnHJTLptIS6CNsY7iASpxUUMkReGpfbQW0vtN5pitvrsN28rwtBD0nc0+/Yft5XhaB6TuaXfsP28rwtA9J3NPv2H7eV4Wgek7mn37D9vK8LQPSdzT79h+3leFoHpO5pd+w/byvC0D0nc0u/Yft5XhaB6TuaXfsP28rwtA9J3NLv2H7eV4Wgek7ml37D9vK8LQPSdzS79h+3leFoHpO5p9+w/byvC0E9r7Reazy2HIYVPxkS/CUHVn26cosxyv2g7h89LYmZSXOenvLEQ1YaQ222RATcQCP8rSGqqA8S02W2pQ6FhMoAIlqCtsnwoCpdKClejI4i3Sgtb+GBxVuNBSFt1pV/RQefLjPyUDy4z8lA8uM/JQPLjPyUDy4z8lA8uM/JQPLjPyUDy4z8lA8uM/JQPLjPyUDy4z8lA8utJ/koJ7WCbBU/LQXOPAFq1koK8B0pag90CggtBBf6qB0UDooHRQOigdFA6KB0UDooHRQOigdFA6KB0UDooI0EaBQf//Z" title="Toggle Table of Contents" alt="Toggle Table of Contents" /></a></div></div>'; - $('body').prepend(ciNav); - // - var a = ['Index', 'CodeIgniter User Guide¶', 'Change Log¶', 'Developer’s Certificate of Origin 1.1¶', 'The MIT License (MIT)¶']; - if ($.inArray($('h1').text(), a) > 0 || $('h2').text() == 'Search Results') - { - $('table.ciNav a').each(function(){ - $(this).attr('href', $(this).attr("href").replace('../', '')); - }); - console.log(1111); - } - // - $('#openToc').click(function(){ - $('#nav').slideToggle(); - }); - $('.wy-breadcrumbs').append('<div style="float:right;"><div style="text-decoration:underline;color:blue;margin-left:5px;" id="closeMe"><img title="toc" alt="toc" src="data:image/gif;base64,R0lGODlhFAAUAJEAAAAAADMzM////wAAACH5BAUUAAIALAAAAAAUABQAAAImlI+py+0PU5gRBRDM3DxbWoXis42X13USOLauUIqnlsaH/eY6UwAAOw==" /></div></div>'); - $('#closeMe').toggle( - function() - { - setCookie('ciNav', true, 365); - $('#nav2').show(); - $('#topMenu').remove(); - $('body').css({ background:'none' }); - $('.wy-nav-content-wrap').css({ background:'none', 'margin-left':0 }); - $('.wy-breadcrumbs').append('<div style="float:right;"><div style="float:left;" id="topMenu">'+$('.wy-form').parent().html()+'</div></div>');$('.wy-nav-side').toggle(); - }, - function() - { - setCookie('ciNav', false, 365); - $('#topMenu').remove(); - $('#nav').hide(); - $('#nav2').hide(); - $('body').css({ background:'#edf0f2;' }); - $('.wy-nav-content-wrap').css({ background:'none repeat scroll 0 0 #fcfcfc;', 'margin-left':'300px' }); - $('.wy-nav-side').show(); - } - ); - if (getCookie('ciNav') == 'true') - { - $('#closeMe').trigger('click'); - //$('#nav').slideToggle(); - } - // END MODIFICATION --- + $("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>"); + // --- + // START DOC MODIFICATION BY RUFNEX + // v1.0 04.02.2015 + // Add ToogleButton to get FullWidth-View by Johannes Gamperl codeigniter.de + + $('#openToc').click(function () { + $('#nav').slideToggle(); + }); + $('#closeMe').toggle( + function () + { + setCookie('ciNav', 'yes', 365); + $('#nav2').show(); + $('#topMenu').remove(); + $('body').css({background: 'none'}); + $('.wy-nav-content-wrap').css({background: 'none', 'margin-left': 0}); + $('.wy-breadcrumbs').append('<div style="float:right;"><div style="float:left;" id="topMenu">' + $('.wy-form').parent().html() + '</div></div>'); + $('.wy-nav-side').toggle(); + }, + function () + { + setCookie('ciNav', 'no', 365); + $('#topMenu').remove(); + $('#nav').hide(); + $('#nav2').hide(); + $('body').css({background: '#edf0f2;'}); + $('.wy-nav-content-wrap').css({background: 'none repeat scroll 0 0 #fcfcfc;', 'margin-left': '300px'}); + $('.wy-nav-side').show(); + } + ); + if (getCookie('ciNav') == 'yes') + { + $('#closeMe').trigger('click'); + //$('#nav').slideToggle(); + } + // END MODIFICATION --- + }); // Rufnex Cookie functions -function setCookie(cname,cvalue,exdays) { +function setCookie(cname, cvalue, exdays) { + // expire the old cookie if existed to avoid multiple cookies with the same name + if (getCookie(cname)) { + document.cookie = cname + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT"; + } var d = new Date(); - d.setTime(d.getTime() + (exdays*24*60*60*1000)); + d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); var expires = "expires=" + d.toGMTString(); - document.cookie = cname+"="+cvalue+"; "+expires; + document.cookie = cname + "=" + cvalue + "; " + expires + "; path=/"; } function getCookie(cname) { var name = cname + "="; var ca = document.cookie.split(';'); - for(var i=0; i<ca.length; i++) { + for (var i = 0; i < ca.length; i++) { var c = ca[i]; - while (c.charAt(0)==' ') c = c.substring(1); + while (c.charAt(0) == ' ') + c = c.substring(1); if (c.indexOf(name) == 0) { return c.substring(name.length, c.length); } } - return false; + return ''; } // End +// resize window +$(window).on('resize', function(){ + // show side nav on small screens when pulldown is enabled + if (getCookie('ciNav') == 'yes' && $(window).width() <= 768) { // 768px is the tablet size defined by the theme + $('.wy-nav-side').show(); + } + // changing css with jquery seems to override the default css media query + // change margin + else if (getCookie('ciNav') == 'no' && $(window).width() <= 768) { + $('.wy-nav-content-wrap').css({'margin-left': 0}); + } + // hide side nav on large screens when pulldown is enabled + else if (getCookie('ciNav') == 'yes' && $(window).width() > 768) { + $('.wy-nav-side').hide(); + } + // change margin + else if (getCookie('ciNav') == 'no' && $(window).width() > 768) { + $('.wy-nav-content-wrap').css({'margin-left': '300px'}); + } +}); + window.SphinxRtdTheme = (function (jquery) { var stickyNav = (function () { var navBar, - win, - stickyNavCssClass = 'stickynav', - applyStickNav = function () { - if (navBar.height() <= win.height()) { - navBar.addClass(stickyNavCssClass); - } else { - navBar.removeClass(stickyNavCssClass); - } - }, - enable = function () { - applyStickNav(); - win.on('resize', applyStickNav); - }, - init = function () { - navBar = jquery('nav.wy-nav-side:first'); - win = jquery(window); - }; + win, + stickyNavCssClass = 'stickynav', + applyStickNav = function () { + if (navBar.height() <= win.height()) { + navBar.addClass(stickyNavCssClass); + } else { + navBar.removeClass(stickyNavCssClass); + } + }, + enable = function () { + applyStickNav(); + win.on('resize', applyStickNav); + }, + init = function () { + navBar = jquery('nav.wy-nav-side:first'); + win = jquery(window); + }; jquery(init); return { - enable : enable + enable: enable }; }()); return { - StickyNav : stickyNav + StickyNav: stickyNav }; }($)); diff --git a/user_guide_src/source/_themes/sphinx_rtd_theme/theme.conf b/user_guide_src/source/_themes/sphinx_rtd_theme/theme.conf index dcfbf8c22..5814ac963 100644 --- a/user_guide_src/source/_themes/sphinx_rtd_theme/theme.conf +++ b/user_guide_src/source/_themes/sphinx_rtd_theme/theme.conf @@ -1,6 +1,6 @@ [theme] inherit = basic -stylesheet = css/theme.css +stylesheet = css/citheme.css [options] typekit_id = hiw1hhg diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst index 5c5cd5e54..f28fd8930 100644 --- a/user_guide_src/source/changelog.rst +++ b/user_guide_src/source/changelog.rst @@ -2,11 +2,161 @@ Change Log ########## -Version 3.0 (planned) -======================= +Version 3.1.0 +============= Release Date: Not Released + +Version 3.0.3 +============= + +Release Date: October 31, 2015 + +- **Security** + + - Fixed an XSS attack vector in :doc:`Security Library <libraries/security>` method ``xss_clean()``. + - Changed :doc:`Config Library <libraries/config>` method ``base_url()`` to fallback to ``$_SERVER['SERVER_ADDR']`` when ``$config['base_url']`` is empty in order to avoid *Host* header injections. + - Changed :doc:`CAPTCHA Helper <helpers/captcha_helper>` to use the operating system's PRNG when possible. + +- Database + + - Optimized :doc:`Database Utility <database/utilities>` method ``csv_from_result()`` for speed with larger result sets. + - Added proper return values to :doc:`Database Transactions <database/transactions>` method ``trans_start()``. + +Bug fixes for 3.0.3 +------------------- + +- Fixed a bug (#4170) - :doc:`Database <database/index>` method ``insert_id()`` could return an identity from the wrong scope with the 'sqlsrv' driver. +- Fixed a bug (#4179) - :doc:`Session Library <libraries/sessions>` doesn't properly maintain its state after ID regeneration with the 'database' driver on PHP7. +- Fixed a bug (#4173) - :doc:`Database Forge <database/forge>` method ``add_key()`` didn't allow creation of non-PRIMARY composite keys after the "bugfix" for #3968. +- Fixed a bug (#4171) - :doc:`Database Transactions <database/transactions>` didn't work with nesting in methods ``trans_begin()``, ``trans_commit()``, ``trans_rollback()``. +- Fixed a bug where :doc:`Database Transaction <database/transactions>` methods ``trans_begin()``, ``trans_commit()``, ``trans_rollback()`` ignored failures. +- Fixed a bug where all :doc:`Database Transaction <database/transactions>` methods returned TRUE while transactions are actually disabled. +- Fixed a bug where :doc:`common function <general/common_functions>` :php:func:`html_escape()` modified keys of its array inputs. +- Fixed a bug (#4192) - :doc:`Email Library <libraries/email>` wouldn't always have proper Quoted-printable encoding due to a bug in PHP's own ``mb_mime_encodeheader()`` function. + +Version 3.0.2 +============= + +Release Date: October 8, 2015 + +- **Security** + + - Fixed a number of XSS attack vectors in :doc:`Security Library <libraries/security>` method ``xss_clean()`` (thanks to Frans Rosén from `Detectify <https://detectify.com/>`_). + +- General Changes + + - Updated the *application/config/constants.php* file to check if constants aren't already defined before doing that. + - Changed :doc:`Loader Library <libraries/loader>` method ``model()`` to only apply ``ucfirst()`` and not ``strtolower()`` to the requested class name. + - Changed :doc:`Config Library <libraries/config>` methods ``base_url()``, ``site_url()`` to allow protocol-relative URLs by passing an empty string as the protocol. + +Bug fixes for 3.0.2 +------------------- + +- Fixed a bug (#2284) - :doc:`Database <database/index>` method ``protect_identifiers()`` breaks when :doc:`Query Builder <database/query_builder>` isn't enabled. +- Fixed a bug (#4052) - :doc:`Routing <general/routing>` with anonymous functions didn't work for routes that don't use regular expressions. +- Fixed a bug (#4056) - :doc:`Input Library <libraries/input>` method ``get_request_header()`` could not return a value unless ``request_headers()`` was called beforehand. +- Fixed a bug where the :doc:`Database Class <database/index>` entered an endless loop if it fails to connect with the 'sqlsrv' driver. +- Fixed a bug (#4065) - :doc:`Database <database/index>` method ``protect_identifiers()`` treats a traling space as an alias separator if the input doesn't contain ' AS '. +- Fixed a bug (#4066) - :doc:`Cache Library <libraries/caching>` couldn't fallback to a backup driver if the primary one is Memcache(d) or Redis. +- Fixed a bug (#4073) - :doc:`Email Library <libraries/email>` method ``send()`` could return TRUE in case of an actual failure when an SMTP command fails. +- Fixed a bug (#4086) - :doc:`Query Builder <database/query_builder>` didn't apply *dbprefix* to LIKE conditions if the pattern included spaces. +- Fixed a bug (#4091) - :doc:`Cache Library <libraries/caching>` 'file' driver could be tricked into accepting empty cache item IDs. +- Fixed a bug (#4093) - :doc:`Query Builder <database/query_builder>` modified string values containing 'AND', 'OR' while compiling WHERE conditions. +- Fixed a bug (#4096) - :doc:`Query Builder <database/query_builder>` didn't apply *dbprefix* when compiling BETWEEN conditions. +- Fixed a bug (#4105) - :doc:`Form Validation Library <libraries/form_validation>` didn't allow pipe characters inside "bracket parameters" when using a string ruleset. +- Fixed a bug (#4109) - :doc:`Routing <general/routing>` to *default_controller* didn't work when *enable_query_strings* is set to TRUE. +- Fixed a bug (#4044) - :doc:`Cache Library <libraries/caching>` 'redis' driver didn't catch ``RedisException`` that could be thrown during authentication. +- Fixed a bug (#4120) - :doc:`Database <database/index>` method ``error()`` didn't return error info when called after ``query()`` with the 'mssql' driver. +- Fixed a bug (#4116) - :doc:`Pagination Library <libraries/pagination>` set the wrong page number on the "data-ci-pagination-page" attribute in generated links. +- Fixed a bug where :doc:`Pagination Library <libraries/pagination>` added the 'rel="start"' attribute to the first displayed link even if it's not actually linking the first page. +- Fixed a bug (#4137) - :doc:`Error Handling <general/errors>` breaks for the new ``Error`` exceptions under PHP 7. +- Fixed a bug (#4126) - :doc:`Form Validation Library <libraries/form_validation>` method ``reset_validation()`` discarded validation rules from config files. + +Version 3.0.1 +============= + +Release Date: August 7, 2015 + +- Core + + - Added DoS mitigation to :php:func:`hash_pbkdf2()` :doc:`compatibility function <general/compatibility_functions>`. + +- Database + + - Added ``list_fields()`` support for SQLite ('sqlite3' and 'pdo_sqlite' drivers). + - Added SSL connection support for the 'mysqli' and 'pdo_mysql' drivers. + +- Libraries + + - :doc:`File Uploading Library <libraries/file_uploading>` changes: + + - Changed method ``set_error()`` to accept a custom log level (defaults to 'error'). + - Errors "no_file_selected", "file_partial", "stopped_by_extension", "no_file_types", "invalid_filetype", "bad_filename" are now logged at the 'debug' level. + - Errors "file_exceeds_limit", "file_exceeds_form_limit", "invalid_filesize", "invalid_dimensions" are now logged at the 'info' level. + + - Added 'is_resource' to the available expectations in :doc:`Unit Testing Library <libraries/unit_testing>`. + +- Helpers + + - Added Unicode support to :doc:`URL Helper <helpers/url_helper>` function :php:func:`url_title()`. + - Added support for passing the "extra" parameter as an array to all :doc:`Form Helper <helpers/form_helper>` functions that use it. + +- Core + + - Added support for defining a list of specific query parameters in ``$config['cache_query_string']`` for the :doc:`Output Library <libraries/output>`. + - Added class existence and inheritance checks to ``CI_Loader::model()`` in order to ease debugging in case of name collisions. + +Bug fixes for 3.0.1 +------------------- + +- Fixed a bug (#3733) - Autoloading of libraries with aliases didn't work, although it was advertised to. +- Fixed a bug (#3744) - Redis :doc:`Caching <libraries/caching>` driver didn't handle authentication failures properly. +- Fixed a bug (#3761) - :doc:`URL Helper <helpers/url_helper>` function :php:func:`anchor()` didn't work with array inputs. +- Fixed a bug (#3773) - ``db_select()`` didn't work for MySQL with the PDO :doc:`Database <database/index>` driver. +- Fixed a bug (#3771) - :doc:`Form Validation Library <libraries/form_validation>` was looking for a 'form_validation\_' prefix when trying to translate field name labels. +- Fixed a bug (#3787) - :doc:`FTP Library <libraries/ftp>` method ``delete_dir()`` failed when the target has subdirectories. +- Fixed a bug (#3801) - :doc:`Output Library <libraries/output>` method ``_display_cache()`` incorrectly looked for the last modified time of a directory instead of the cache file. +- Fixed a bug (#3816) - :doc:`Form Validation Library <libraries/form_validation>` treated empty string values as non-existing ones. +- Fixed a bug (#3823) - :doc:`Session Library <libraries/sessions>` drivers Redis and Memcached didn't properly handle locks that are blocking the request for more than 30 seconds. +- Fixed a bug (#3846) - :doc:`Image Manipulation Library <libraries/image_lib>` method `image_mirror_gd()` didn't properly initialize its variables. +- Fixed a bug (#3854) - `field_data()` didn't work properly with the Oracle (OCI8) database driver. +- Fixed a bug in the :doc:`Database Utility Class <database/utilities>` method ``csv_from_result()`` didn't work with a whitespace CSV delimiter. +- Fixed a bug (#3890) - :doc:`Input Library <libraries/input>` method ``get_request_header()`` treated header names as case-sensitive. +- Fixed a bug (#3903) - :doc:`Form Validation Library <libraries/form_validation>` ignored "unnamed" closure validation rules. +- Fixed a bug (#3904) - :doc:`Form Validation Library <libraries/form_validation>` ignored "named" callback rules when the field is empty and there's no 'required' rule. +- Fixed a bug (#3922) - :doc:`Email <libraries/email>` and :doc:`XML-RPC <libraries/xmlrpc>` libraries could enter an infinite loop due to `PHP bug #39598 <https://bugs.php.net/bug.php?id=39598>`_. +- Fixed a bug (#3913) - :doc:`Cache Library <libraries/caching>` didn't work with the direct ``$this->cache->$driver_name->method()`` syntax with Redis and Memcache(d). +- Fixed a bug (#3932) - :doc:`Query Builder <database/query_builder>` didn't properly compile WHERE and HAVING conditions for field names that end with "and", "or". +- Fixed a bug in :doc:`Query Builder <database/query_builder>` where ``delete()`` didn't properly work on multiple tables with a WHERE condition previously set via ``where()``. +- Fixed a bug (#3952) - :doc:`Database <database/index>` method ``list_fields()`` didn't work with SQLite3. +- Fixed a bug (#3955) - :doc:`Cache Library <libraries/caching>` methods ``increment()`` and ``decrement()`` ignored the 'key_prefix' setting. +- Fixed a bug (#3963) - :doc:`Unit Testing Library <libraries/unit_testing>` wrongly tried to translate filenames, line numbers and notes values in test results. +- Fixed a bug (#3965) - :doc:`File Uploading Library <libraries/file_uploading>` ignored the "encrypt_name" setting when "overwrite" is enabled. +- Fixed a bug (#3968) - :doc:`Database Forge <database/forge>` method ``add_key()`` didn't treat array inputs as composite keys unless it's a PRIMARY KEY. +- Fixed a bug (#3715) - :doc:`Pagination Library <libraries/pagination>` could generate broken link when a protocol-relative base URL is used. +- Fixed a bug (#3828) - :doc:`Output Library <libraries/output>` method ``delete_cache()`` couldn't delete index page caches. +- Fixed a bug (#3704) - :doc:`Database <database/index>` method ``stored_procedure()`` in the 'oci8' driver didn't properly bind parameters. +- Fixed a bug (#3778) - :doc:`Download Helper <helpers/download_helper>` function :php:func:`force_download()` incorrectly sent a *Pragma* response header. +- Fixed a bug (#3752) - ``$routing['directory']`` overrides were not properly handled and always resulted in a 404 "Not Found" error. +- Fixed a bug (#3279) - :doc:`Query Builder <database/query_builder>` methods ``update()`` and ``get_compiled_update()`` did double escaping on the table name if it was provided via ``from()``. +- Fixed a bug (#3991) - ``$config['rewrite_short_tags']`` never worked due to ``function_exists('eval')`` always returning FALSE. +- Fixed a bug where the :doc:`File Uploading Library <libraries/file_uploading>` library will not properly configure its maximum file size unless the input value is of type integer. +- Fixed a bug (#4000) - :doc:`Pagination Library <libraries/pagination>` didn't enable "rel" attributes by default if no attributes-related config options were used. +- Fixed a bug (#4004) - :doc:`URI Class <libraries/uri>` didn't properly parse the request URI if it contains a colon followed by a digit. +- Fixed a bug in :doc:`Query Builder <database/query_builder>` where the ``$escape`` parameter for some methods only affected field names. +- Fixed a bug (#4012) - :doc:`Query Builder <database/query_builder>` methods ``where_in()``, ``or_where_in()``, ``where_not_in()``, ``or_where_not_in()`` didn't take into account previously cached WHERE conditions when query cache is in use. +- Fixed a bug (#4015) - :doc:`Email Library <libraries/email>` method ``set_header()`` didn't support method chaining, although it was advertised. +- Fixed a bug (#4027) - :doc:`Routing <general/routing>` with HTTP verbs only worked if the route request method was declared in all-lowercase letters. +- Fixed a bug (#4026) - :doc:`Database Transactions <database/transactions>` always rollback if any previous ``query()`` call fails. +- Fixed a bug (#4023) - :doc:`String Helper <helpers/string_helper>` function ``increment_string()`` didn't escape its ``$separator`` parameter. + +Version 3.0.0 +============= + +Release Date: March 30, 2015 + - License - CodeIgniter has been relicensed with the `MIT License <http://opensource.org/licenses/MIT>`_, eliminating its old proprietary licensing. @@ -56,7 +206,7 @@ Release Date: Not Released - Added availability checks where usage of dangerous functions like ``eval()`` and ``exec()`` is required. - Added support for changing the file extension of log files using ``$config['log_file_extension']``. - Added support for turning newline standardization on/off via ``$config['standardize_newlines']`` and set it to FALSE by default. - - Added configuration setting ``$config['composer_autoload']`` to enable loading of a `Composer <https://getcomposer.org/>`_ auto-loader. + - Added configuration setting ``$config['composer_autoload']`` to enable loading of a `Composer <https://getcomposer.org>`_ auto-loader. - Removed the automatic conversion of 'programmatic characters' to HTML entities from the :doc:`URI Library <libraries/uri>`. - Changed log messages that say a class or file was loaded to "info" level instead of "debug", so that they don't pollute log files when ``$config['log_threshold']`` is set to 2 (debug). @@ -67,7 +217,7 @@ Release Date: Not Released - Added an optional third parameter to :php:func:`timespan()` that constrains the number of time units displayed. - Added an optional parameter to :php:func:`timezone_menu()` that allows more attributes to be added to the generated select tag. - Added function :php:func:`date_range()` that generates a list of dates between a specified period. - - Deprecated ``standard_date()``, which now just uses the native ``date()`` with `DateTime constants <http://www.php.net/manual/en/class.datetime.php#datetime.constants.types>`_. + - Deprecated ``standard_date()``, which now just uses the native ``date()`` with `DateTime constants <http://php.net/manual/en/class.datetime.php#datetime.constants.types>`_. - Changed :php:func:`now()` to work with all timezone strings supported by PHP. - Changed :php:func:`days_in_month()` to use the native ``cal_days_in_month()`` PHP function, if available. @@ -155,6 +305,8 @@ Release Date: Not Released - DEPRECATED the 'mysql', 'sqlite', 'mssql' and 'pdo/dblib' (also known as 'pdo/mssql' or 'pdo/sybase') drivers. - Added **dsn** configuration setting for drivers that support DSN strings (PDO, PostgreSQL, Oracle, ODBC, CUBRID). - Added **schema** configuration setting (defaults to *public*) for drivers that might need it (currently used by PostgreSQL and ODBC). + - Added **save_queries** configuration setting to *application/config/database.php* (defaults to ``TRUE``). + - Removed **autoinit** configuration setting as it doesn't make sense to instantiate the database class but not connect to the database. - Added subdrivers support (currently only used by PDO). - Added an optional database name parameter to ``db_select()``. - Removed ``protect_identifiers()`` and renamed internal method ``_protect_identifiers()`` to it instead - it was just an alias. @@ -173,7 +325,6 @@ Release Date: Not Released - Added support for SQLite3 database driver. - Added Interbase/Firebird database support via the *ibase* driver. - Added ODBC support for ``create_database()``, ``drop_database()`` and ``drop_table()`` in :doc:`Database Forge <database/forge>`. - - Added **save_queries** configuration setting to *application/config/database.php* (defaults to ``TRUE``). - Added support to binding arrays as ``IN()`` sets in ``query()``. - :doc:`Query Builder <database/query_builder>` changes include: @@ -191,6 +342,7 @@ Release Date: Not Released - Methods ``insert_batch()`` and ``update_batch()`` now return an integer representing the number of rows affected by them. - Methods ``where()``, ``or_where()``, ``having()`` and ``or_having()`` now convert trailing ``=`` and ``<>``, ``!=`` SQL operators to ``IS NULL`` and ``IS NOT NULL`` respectively when the supplied comparison value is ``NULL``. - Added method chaining support to ``reset_query()``, ``start_cache()``, ``stop_cache()`` and ``flush_cache()``. + - Added an optional second parameter to ``count_all_results()`` to disable resetting of QB values. - :doc:`Database Results <database/results>` changes include: @@ -361,6 +513,7 @@ Release Date: Not Released - Added support for custom error messages per field rule. - Added support for callable rules when they are passed as an array. - Added support for non-ASCII domains in **valid_email** rule, depending on the Intl extension. + - Changed the debug message about an error message not being set to include the rule name it is about. - :doc:`Caching Library <libraries/caching>` changes include: @@ -459,7 +612,7 @@ Release Date: Not Released - Renamed internal method ``_detect_uri()`` to ``_parse_request_uri()``. - Changed ``_parse_request_uri()`` to accept absolute URIs for compatibility with HTTP/1.1 as per `RFC2616 <http://www.ietf.org/rfc/rfc2616.txt>`. - Added protected method ``_parse_query_string()`` to URI paths in the the **QUERY_STRING** value, like ``_parse_request_uri()`` does. - - Changed URI string detection logic to try the **PATH_INFO** variable first when auto-detecting. + - Changed URI string detection logic to always default to **REQUEST_URI** unless configured otherwise or under CLI. - Removed methods ``_remove_url_suffix()``, ``_explode_segments()`` and moved their logic into ``_set_uri_string()``. - Removed method ``_fetch_uri_string()`` and moved its logic into the class constructor. - Removed method ``_reindex_segments()``. @@ -479,6 +632,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. @@ -492,6 +646,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: @@ -507,7 +662,8 @@ Release Date: Not Released - Changed internal function ``load_class()`` to accept a constructor parameter instead of (previously unused) class name prefix. - Removed default parameter value of :php:func:`is_php()`. - Added a second argument ``$double_encode`` to :php:func:`html_escape()`. - - Changed function ``config_item()`` to return NULL instead of FALSE when no value is found. + - Changed function :php:func:`config_item()` to return NULL instead of FALSE when no value is found. + - Changed function :php:func:`set_status_header()` to return immediately when run under CLI. - :doc:`Output Library <libraries/output>` changes include: @@ -559,7 +715,7 @@ Release Date: Not Released - Changed the library constructor to try to create the **log_path** directory if it doesn't exist. - Added support for microseconds ("u" date format character) in ``$config['log_date_format']``. - - Added `compatibility layers <general/compatibility_functions>` for: + - Added :doc:`compatibility layers <general/compatibility_functions>` for: - `Multibyte String <http://php.net/mbstring>`_ (limited support). - `Hash <http://php.net/hash>`_ (``hash_equals()``, ``hash_pbkdf2()``). @@ -573,7 +729,7 @@ Release Date: Not Released Bug fixes for 3.0 ------------------- +----------------- - Fixed a bug where ``unlink()`` raised an error if cache file did not exist when you try to delete it. - Fixed a bug (#181) - a typo in the form validation language file. @@ -660,7 +816,6 @@ Bug fixes for 3.0 - 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>` method ``from()`` didn't escape table aliases. - Fixed a bug (#520) - :doc:`Date Helper <helpers/date_helper>` function :php:func:``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 Library <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 whether a query string exists in it. @@ -705,7 +860,6 @@ Bug fixes for 3.0 - Fixed a bug (#50) - :doc:`Session Library <libraries/sessions>` unnecessarily stripped slashed from serialized data, making it impossible to read objects in a namespace. - Fixed a bug (#658) - :doc:`Routing <general/routing>` wildcard **:any** didn't work as advertised and matched multiple URI segments instead of all characters within a single segment. - Fixed a bug (#1938) - :doc:`Email Library <libraries/email>` removed multiple spaces inside a pre-formatted plain text message. -- Fixed a bug (#388, #705) - :doc:`URI Library <libraries/uri>` didn't apply URL-decoding to URI segments that it got from **REQUEST_URI** and/or **QUERY_STRING**. - Fixed a bug (#122) - :doc:`URI Library <libraries/uri>` method ``ruri_string()`` didn't include a directory if one is used. - Fixed a bug - :doc:`Routing Library <general/routing>` didn't properly handle *default_controller* in a subdirectory when a method is also specified. - Fixed a bug (#953) - :doc:`post_controller_constructor hook <general/hooks>` wasn't called with a *404_override*. @@ -767,6 +921,32 @@ Bug fixes for 3.0 - Fixed a bug in :doc:`Zip Library <libraries/zip>` internal method ``_get_mod_time()`` where it was not parsing result returned by ``filemtime()``. - 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) - ``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.3 +============= + +Release Date: July 14, 2015 + +- Security + + - Removed a fallback to ``mysql_escape_string()`` in the 'mysql' database driver (``escape_str()`` method) when there's no active database connection. + +Version 2.2.2 +============= + +Release Date: April 15, 2015 + +- General Changes + + - Added HTTP "Host" header character validation to prevent cache poisoning attacks when *base_url* auto-detection is used. + - Added *FSCommand* and *seekSegmentTime* to the "evil attributes" list in ``CI_Security::xss_clean()``. + +Bug fixes for 2.2.2 +------------------- + +- Fixed a bug (#3665) - ``CI_Security::entity_decode()`` triggered warnings under some circumstances. Version 2.2.1 ============= @@ -1131,12 +1311,8 @@ Bug fixes for 2.0.2 class <libraries/input>`. - Added form_validation_lang entries for decimal, less_than and greater_than. -- `Fixed issue - #153 <https://bitbucket.org/ellislab/codeigniter-reactor/issue/153/escape-str-bug-in-mssql-driver>`_ - Escape Str Bug in MSSQL driver. -- `Fixed issue - #172 <https://bitbucket.org/ellislab/codeigniter-reactor/issue/172/bug-in-chrome-and-form_open-in-201>`_ - Google Chrome 11 posts incorrectly when action is empty. +- Fixed issue #153 Escape Str Bug in MSSQL driver. +- Fixed issue #172 Google Chrome 11 posts incorrectly when action is empty. Version 2.0.1 ============= @@ -1229,8 +1405,7 @@ Hg Tag: v2.0.0 libraries, models, config files, etc. in a single "package" directory. See the :doc:`Loader class <libraries/loader>` documentation for more details. - - In-development code is now hosted at - `BitBucket <http://bitbucket.org/ellislab/codeigniter-reactor/>`_. + - In-development code is now hosted at BitBucket . - Removed the deprecated Validation Class. - Added CI\_ Prefix to all core classes. - Package paths can now be set in application/config/autoload.php. @@ -1372,7 +1547,7 @@ Hg Tag: v2.0.0 precision. - Added alpha, and sha1 string types to random_string() in the :doc:`String Helper <helpers/string_helper>`. - - Modified prep_url() so as to not prepend http:// if the supplied + - Modified prep_url() so as to not prepend http:// if the supplied string already has a scheme. - Modified get_file_info in the file helper, changing filectime() to filemtime() for dates. @@ -2112,7 +2287,7 @@ Bugfixes for 1.6.2 instantiating new Language and Exception objects, and not using the error heading. - Fixed a bug (#4413) where a URI containing slashes only e.g. - 'http://example.com/index.php?//' would result in PHP errors + 'http://example.com/index.php?//' would result in PHP errors - Fixed an array to string conversion error in the Validation library (#4425) - Fixed bug (#4451, #4299, #4339) where failed transactions will not @@ -2764,8 +2939,7 @@ Release Date: September 17, 2006 the core files. - Added the ability to organize controller files :doc:`into sub-folders <general/controllers>`. Kudos to Marco for - `suggesting <http://codeigniter.com/forums/viewthread/627/>`_ this - (and the next two) feature. + suggesting this (and the next two) feature. - Added regular expressions support for :doc:`routing rules <./general/routing>`. - Added the ability to :doc:`remap function diff --git a/user_guide_src/source/conf.py b/user_guide_src/source/conf.py index d65fe0dfd..de55513bf 100644 --- a/user_guide_src/source/conf.py +++ b/user_guide_src/source/conf.py @@ -48,9 +48,9 @@ copyright = u'2014 - 2015, British Columbia Institute of Technology' # built documents. # # The short X.Y version. -version = '3.0' +version = '3.1.0-dev' # The full version, including alpha/beta/rc tags. -release = '3.0-dev' +release = '3.1.0-dev' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/user_guide_src/source/contributing/index.rst b/user_guide_src/source/contributing/index.rst index e88147753..5966070d1 100644 --- a/user_guide_src/source/contributing/index.rst +++ b/user_guide_src/source/contributing/index.rst @@ -10,9 +10,8 @@ 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 `CodeIgniter repository -<https://github.com/bcit-ci/CodeIgniter>`_ on GitHub. +of Issues or `Pull Requests <https://help.github.com/articles/using-pull-requests/>`_ +on the `CodeIgniter repository <https://github.com/bcit-ci/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: @@ -30,12 +29,24 @@ own copy. This will require you to use the version control system called Git. Support ******* -Note that GitHub is not for general support questions! +Please note that GitHub is not for general support questions! If you are +having trouble using a feature of CodeIgniter, ask for help on our +`forums <http://forum.codeigniter.com/>`_ instead. -If you are having trouble using a feature of CodeIgniter, ask for help on the forum. +If you are not sure whether you are using something correctly or if you +have found a bug, again - please ask on the forums first. -If you are wondering if you are using -something correctly or if you have found a bug, ask on the forum first. +******** +Security +******** + +Did you find a security issue in CodeIgniter? + +Please *don't* disclose it publicly, but e-mail us at security@codeigniter.com, +or report it via our page on `HackerOne <https://hackerone.com/codeigniter>`_. + +If you've found a critical vulnerability, we'd be happy to credit you in our +`ChangeLog <../changelog>`. **************************** Tips for a Good Issue Report @@ -75,7 +86,7 @@ PHP Style ========= All code must meet the `Style Guide -<http://codeigniter.com/user_guide/general/styleguide.html>`_, which is +<http://www.codeigniter.com/userguide3/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 diff --git a/user_guide_src/source/database/configuration.rst b/user_guide_src/source/database/configuration.rst index 074725664..8026be63a 100644 --- a/user_guide_src/source/database/configuration.rst +++ b/user_guide_src/source/database/configuration.rst @@ -27,7 +27,6 @@ prototype:: 'char_set' => 'utf8', 'dbcollat' => 'utf8_general_ci', 'swap_pre' => '', - 'autoinit' => TRUE, 'encrypt' => FALSE, 'compress' => FALSE, 'stricton' => FALSE, @@ -70,7 +69,6 @@ These failovers can be specified by setting the failover for a connection like t 'char_set' => 'utf8', 'dbcollat' => 'utf8_general_ci', 'swap_pre' => '', - 'autoinit' => TRUE, 'encrypt' => FALSE, 'compress' => FALSE, 'stricton' => FALSE @@ -89,7 +87,6 @@ These failovers can be specified by setting the failover for a connection like t 'char_set' => 'utf8', 'dbcollat' => 'utf8_general_ci', 'swap_pre' => '', - 'autoinit' => TRUE, 'encrypt' => FALSE, 'compress' => FALSE, 'stricton' => FALSE @@ -120,7 +117,6 @@ example, to set up a "test" environment you would do this:: 'char_set' => 'utf8', 'dbcollat' => 'utf8_general_ci', 'swap_pre' => '', - 'autoinit' => TRUE, 'compress' => FALSE, 'encrypt' => FALSE, 'stricton' => FALSE, @@ -156,9 +152,9 @@ when the database classes are initialized. Explanation of Values: ---------------------- -====================== ================================================================================================== +====================== =========================================================================================================== Name Config Description -====================== ================================================================================================== +====================== =========================================================================================================== **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. @@ -181,10 +177,19 @@ Explanation of Values: **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 customizable by the end user. -**autoinit** Whether or not to automatically connect to the database when the library loads. If set to false, - the connection will take place prior to executing the first query. **schema** The database schema, defaults to 'public'. Used by PostgreSQL and ODBC drivers. **encrypt** Whether or not to use an encrypted connection. + + - 'mysql' (deprecated), 'sqlsrv' and 'pdo/sqlsrv' drivers accept TRUE/FALSE + - 'mysqli' and 'pdo/mysql' drivers accept an array with the following options: + + - 'ssl_key' - Path to the private key file + - 'ssl_cert' - Path to the public key certificate file + - 'ssl_ca' - Path to the certificate authority file + - 'ssl_capath' - Path to a directory containing trusted CA certificats in PEM format + - 'ssl_cipher' - List of *allowed* ciphers to be used for the encryption, separated by colons (':') + - 'ssl_verify' - TRUE/FALSE; Whether to verify the server certificate or not ('mysqli' only) + **compress** Whether or not to use client compression (MySQL only). **stricton** TRUE/FALSE (boolean) - Whether to force "Strict Mode" connections, good for ensuring strict SQL while developing an application. @@ -192,10 +197,11 @@ Explanation of Values: :: $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.
\ No newline at end of file + you are using MySQL. diff --git a/user_guide_src/source/database/db_driver_reference.rst b/user_guide_src/source/database/db_driver_reference.rst index 005e6b3dc..8fc26c01b 100644 --- a/user_guide_src/source/database/db_driver_reference.rst +++ b/user_guide_src/source/database/db_driver_reference.rst @@ -124,8 +124,8 @@ This article is intended to be a reference for them. Enable/disable transaction "strict" mode. When strict mode is enabled, if you are running multiple - groups of transactions and one group fails, all groups - will be rolled back. + groups of transactions and one group fails, all subsequent + groups will be rolled back. If strict mode is disabled, each group is treated autonomously, meaning a failure of one group will not @@ -140,13 +140,15 @@ This article is intended to be a reference for them. .. php:method:: trans_start([$test_mode = FALSE]) :param bool $test_mode: Test mode flag - :rtype: void + :returns: TRUE on success, FALSE on failure + :rtype: bool Start a transaction. .. php:method:: trans_complete() - :rtype: void + :returns: TRUE on success, FALSE on failure + :rtype: bool Complete Transaction. diff --git a/user_guide_src/source/database/examples.rst b/user_guide_src/source/database/examples.rst index 8b3cc4701..5fd7fccfa 100644 --- a/user_guide_src/source/database/examples.rst +++ b/user_guide_src/source/database/examples.rst @@ -55,23 +55,6 @@ Standard Query With Multiple Results (Array Version) The above result_array() function returns an array of standard array indexes. Example: $row['title'] -Testing for Results -=================== - -If you run queries that might **not** produce a result, you are -encouraged to test for a result first using the num_rows() function:: - - $query = $this->db->query("YOUR QUERY"); - if ($query->num_rows() > 0) - { - foreach ($query->result() as $row) - { - echo $row->title; - echo $row->name; - echo $row->body; - } - } - Standard Query With Single Result ================================= diff --git a/user_guide_src/source/database/forge.rst b/user_guide_src/source/database/forge.rst index 89fac023e..646e3a56e 100644 --- a/user_guide_src/source/database/forge.rst +++ b/user_guide_src/source/database/forge.rst @@ -143,13 +143,15 @@ string into the field definitions with add_field() $this->dbforge->add_field("label varchar(100) NOT NULL DEFAULT 'default label'"); +.. note:: Passing raw strings as fields cannot be followed by ``add_key()`` calls on those fields. + .. note:: Multiple calls to add_field() are cumulative. Creating an id field -------------------- There is a special exception for creating id fields. A field with type -id will automatically be assinged as an INT(9) auto_incrementing +id will automatically be assigned as an INT(9) auto_incrementing Primary Key. :: @@ -227,7 +229,7 @@ Execute a DROP TABLE statement and optionally add an IF EXISTS clause. $this->dbforge->drop_table('table_name'); // Produces: DROP TABLE IF EXISTS table_name - $this->dbforge->drop_table('table_name'); + $this->dbforge->drop_table('table_name',TRUE); Renaming a table @@ -405,4 +407,4 @@ Class Reference :returns: TRUE on success, FALSE on failure :rtype: bool - Renames a table. Usage: See `Renaming a table`_.
\ No newline at end of file + Renames a table. Usage: See `Renaming a table`_. diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst index fa1e90353..5d9ae4592 100644 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -520,13 +520,19 @@ The second parameter lets you set a result offset. Permits you to determine the number of rows in a particular Active Record query. Queries will accept Query Builder restrictors such as -where(), or_where(), like(), or_like(), etc. Example:: +``where()``, ``or_where()``, ``like()``, ``or_like()``, etc. Example:: echo $this->db->count_all_results('my_table'); // Produces an integer, like 25 $this->db->like('title', 'match'); $this->db->from('my_table'); echo $this->db->count_all_results(); // Produces an integer, like 17 +However, this method also resets any field values that you may have passed +to ``select()``. If you need to keep them, you can pass ``FALSE`` as the +second parameter:: + + echo $this->db->count_all_results('my_table', FALSE); + **$this->db->count_all()** Permits you to determine the number of rows in a particular table. @@ -634,18 +640,18 @@ Example:: $sql = $this->db->set($data)->get_compiled_insert('mytable'); echo $sql; - // Produces string: INSERT INTO mytable (title, name, date) VALUES ('My title', 'My name', 'My date') + // Produces string: INSERT INTO mytable (`title`, `name`, `date`) VALUES ('My title', 'My name', 'My date') The second parameter enables you to set whether or not the query builder query will be reset (by default it will be--just like $this->db->insert()):: echo $this->db->set('title', 'My Title')->get_compiled_insert('mytable', FALSE); - // Produces string: INSERT INTO mytable (title) VALUES ('My Title') + // Produces string: INSERT INTO mytable (`title`) VALUES ('My Title') echo $this->db->set('content', 'My Content')->get_compiled_insert(); - // Produces string: INSERT INTO mytable (title, content) VALUES ('My Title', 'My Content') + // Produces string: INSERT INTO mytable (`title`, `content`) VALUES ('My Title', 'My Content') The key thing to notice in the above example is that the second query did not utlize `$this->db->from()` nor did it pass a table name into the first @@ -724,7 +730,7 @@ or update functions:** :: $this->db->set('name', $name); - $this->db->insert('mytable'); // Produces: INSERT INTO mytable (name) VALUES ('{$name}') + $this->db->insert('mytable'); // Produces: INSERT INTO mytable (`name`) VALUES ('{$name}') If you use multiple function called they will be assembled properly based on whether you are doing an insert or an update:: @@ -734,18 +740,20 @@ based on whether you are doing an insert or an update:: $this->db->set('status', $status); $this->db->insert('mytable'); -**set()** will also accept an optional third parameter ($escape), that +**set()** will also accept an optional third parameter (``$escape``), that will prevent data from being escaped if set to FALSE. To illustrate the -difference, here is set() used both with and without the escape +difference, here is ``set()`` used both with and without the escape parameter. :: $this->db->set('field', 'field+1', FALSE); - $this->db->insert('mytable'); // gives INSERT INTO mytable (field) VALUES (field+1) - $this->db->set('field', 'field+1'); - $this->db->insert('mytable'); // gives INSERT INTO mytable (field) VALUES ('field+1') + $this->db->where('id', 2); + $this->db->update('mytable'); // gives UPDATE mytable SET field = field+1 WHERE id = 2 + $this->db->set('field', 'field+1'); + $this->db->where('id', 2); + $this->db->update('mytable'); // gives UPDATE `mytable` SET `field` = 'field+1' WHERE `id` = 2 You can also pass an associative array to this function:: @@ -786,7 +794,11 @@ is an example using an array:: $this->db->where('id', $id); $this->db->update('mytable', $data); - // Produces: // UPDATE mytable // SET title = '{$title}', name = '{$name}', date = '{$date}' // WHERE id = $id + // Produces: + // + // UPDATE mytable + // SET title = '{$title}', name = '{$name}', date = '{$date}' + // WHERE id = $id Or you can supply an object:: @@ -801,7 +813,11 @@ Or you can supply an object:: $object = new Myclass; $this->db->where('id', $id); $this->db->update('mytable', $object); - // Produces: // UPDATE mytable // SET title = '{$title}', name = '{$name}', date = '{$date}' // WHERE id = $id + // Produces: + // + // UPDATE `mytable` + // SET `title` = '{$title}', `name` = '{$name}', `date` = '{$date}' + // WHERE id = `$id` .. note:: All values are escaped automatically producing safer queries. @@ -1002,7 +1018,7 @@ Here's a usage example:: .. note:: The following statements can be cached: select, from, join, - where, like, group_by, having, order_by, set + where, like, group_by, having, order_by *********************** @@ -1091,9 +1107,10 @@ Class Reference Prepends a database prefix, if one exists in configuration. - .. php:method:: count_all_results([$table = '']) + .. php:method:: count_all_results([$table = '', [$reset = TRUE]]) :param string $table: Table name + :param bool $reset: Whether to reset values for SELECTs :returns: Number of rows in the query result :rtype: int @@ -1199,7 +1216,7 @@ Class Reference :param mixed $key: Name of field to compare, or associative array :param mixed $value: If a single key, compared to this value - :param boolean $escape: Whether to escape values and identifiers + :param bool $escape: Whether to escape values and identifiers :returns: DB_query_builder instance :rtype: object @@ -1210,7 +1227,7 @@ Class Reference :param mixed $key: Name of field to compare, or associative array :param mixed $value: If a single key, compared to this value - :param boolean $escape: Whether to escape values and identifiers + :param bool $escape: Whether to escape values and identifiers :returns: DB_query_builder instance :rtype: object @@ -1221,7 +1238,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 bool $escape: Whether to escape values and identifiers :returns: DB_query_builder instance :rtype: object @@ -1232,7 +1249,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 bool $escape: Whether to escape values and identifiers :returns: DB_query_builder instance :rtype: object @@ -1243,7 +1260,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 bool $escape: Whether to escape values and identifiers :returns: DB_query_builder instance :rtype: object @@ -1254,7 +1271,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 bool $escape: Whether to escape values and 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..6fea7c6be 100644 --- a/user_guide_src/source/database/results.rst +++ b/user_guide_src/source/database/results.rst @@ -19,7 +19,7 @@ This method returns the query result as an array of **objects**, or loop, like this:: $query = $this->db->query("YOUR QUERY"); - + foreach ($query->result() as $row) { echo $row->title; @@ -29,22 +29,7 @@ loop, like this:: The above method is an alias of ``result_object()``. -If you run queries that might **not** produce a result, you are -encouraged to test the result first:: - - $query = $this->db->query("YOUR QUERY"); - - if ($query->num_rows() > 0) - { - foreach ($query->result() as $row) - { - echo $row->title; - echo $row->name; - echo $row->body; - } - } - -You can also pass a string to result() which represents a class to +You can also pass a string to ``result()`` which represents a class to instantiate for each result object (note: this class must be loaded) :: @@ -64,7 +49,7 @@ array when no result is produced. Typically you'll use this in a foreach loop, like this:: $query = $this->db->query("YOUR QUERY"); - + foreach ($query->result_array() as $row) { echo $row['title']; @@ -83,11 +68,11 @@ one row, it returns only the first row. The result is returned as an **object**. Here's a usage example:: $query = $this->db->query("YOUR QUERY"); - - if ($query->num_rows() > 0) + + $row = $query->row(); + + if (isset($row)) { - $row = $query->row(); - echo $row->title; echo $row->name; echo $row->body; @@ -102,7 +87,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 @@ -113,11 +98,11 @@ Identical to the above ``row()`` method, except it returns an array. Example:: $query = $this->db->query("YOUR QUERY"); - - if ($query->num_rows() > 0) + + $row = $query->row_array(); + + if (isset($row)) { - $row = $query->row_array(); - echo $row['title']; echo $row['name']; echo $row['body']; @@ -157,7 +142,7 @@ it returns the current row and moves the internal data pointer ahead. :: $query = $this->db->query("YOUR QUERY"); - + while ($row = $query->unbuffered_row()) { echo $row->title; @@ -173,6 +158,94 @@ the returned value's type:: $query->unbuffered_row('array'); // associative array ********************* +Custom Result Objects +********************* + +You can have the results returned as an instance of a custom class instead +of a ``stdClass`` or array, as the ``result()`` and ``result_array()`` +methods allow. This requires that the class is already loaded into memory. +The object will have all values returned from the database set as properties. +If these have been declared and are non-public then you should provide a +``__set()`` method to allow them to be set. + +Example:: + + class User { + + public $id; + public $email; + public $username; + + protected $last_login; + + public function last_login($format) + { + return $this->last_login->format($format); + } + + public function __set($name, $value) + { + if ($name === 'last_login') + { + $this->last_login = DateTime::createFromFormat('U', $value); + } + } + + public function __get($name) + { + if (isset($this->$name)) + { + return $this->$name; + } + } + } + +In addition to the two methods listed below, the following methods also can +take a class name to return the results as: ``first_row()``, ``last_row()``, +``next_row()``, and ``previous_row()``. + +**custom_result_object()** + +Returns the entire result set as an array of instances of the class requested. +The only parameter is the name of the class to instantiate. + +Example:: + + $query = $this->db->query("YOUR QUERY"); + + $rows = $query->custom_result_object('User'); + + foreach ($rows as $row) + { + echo $row->id; + echo $row->email; + echo $row->last_login('Y-m-d'); + } + +**custom_row_object()** + +Returns a single row from your query results. The first parameter is the row +number of the results. The second parameter is the class name to instantiate. + +Example:: + + $query = $this->db->query("YOUR QUERY"); + + $row = $query->custom_row_object(0, 'User'); + + if (isset($row)) + { + echo $row->email; // access attributes + echo $row->last_login('Y-m-d'); // access class methods + } + +You can also use the ``row()`` method in exactly the same way. + +Example:: + + $row = $query->custom_row_object(0, 'User'); + +********************* Result Helper Methods ********************* @@ -182,7 +255,7 @@ The number of rows returned by the query. Note: In this example, $query is the variable that the query result object is assigned to:: $query = $this->db->query('SELECT * FROM my_table'); - + echo $query->num_rows(); .. note:: Not all database drivers have a native way of getting the total @@ -196,7 +269,7 @@ The number of FIELDS (columns) returned by the query. Make sure to call the method using your query result object:: $query = $this->db->query('SELECT * FROM my_table'); - + echo $query->num_fields(); **free_result()** @@ -210,7 +283,7 @@ result has been generated in order to cut down on memory consumption. Example:: $query = $this->db->query('SELECT title FROM my_table'); - + foreach ($query->result() as $row) { echo $row->title; @@ -431,4 +504,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/database/transactions.rst b/user_guide_src/source/database/transactions.rst index e9190e59a..2e6d4b477 100644 --- a/user_guide_src/source/database/transactions.rst +++ b/user_guide_src/source/database/transactions.rst @@ -21,7 +21,7 @@ approach because it greatly simplifies the process of running transactions. In most cases all that is required are two lines of code. Traditionally, transactions have required a fair amount of work to -implement since they demand that you to keep track of your queries and +implement since they demand that you keep track of your queries and determine whether to commit or rollback based on the success or failure of your queries. This is particularly cumbersome with nested queries. In contrast, we've implemented a smart transaction system that does all diff --git a/user_guide_src/source/database/utilities.rst b/user_guide_src/source/database/utilities.rst index bafa08ed5..114291f85 100644 --- a/user_guide_src/source/database/utilities.rst +++ b/user_guide_src/source/database/utilities.rst @@ -18,7 +18,7 @@ Initializing the Utility Class Load the Utility Class as follows:: - $this->load->dbutil() + $this->load->dbutil(); You can also pass another database object to the DB Utility loader, in case the database you want to manage isn't the default one:: @@ -35,7 +35,7 @@ assigning it directly to ``$this->dbutil``. Once initialized you will access the methods using the ``$this->dbutil`` object:: - $this->dbutil->some_method() + $this->dbutil->some_method(); **************************** Using the Database Utilities @@ -65,7 +65,7 @@ Returns a boolean TRUE/FALSE. Usage example:: // some code... } -.. note:: Replace *database_name* with the name of the table you are +.. note:: Replace *database_name* with the name of the database you are looking for. This method is case sensitive. Optimize a Table @@ -111,7 +111,7 @@ FALSE on failure. print_r($result); } -.. note:: Not all database platforms support table optimization. It +.. note:: Not all database platforms support database optimization. It it is mostly for use with MySQL. Export a Query Result as a CSV File @@ -180,7 +180,7 @@ backup data can be compressed in either Zip or Gzip format. .. note:: For Interbase/Firebird databases, the backup file name is the only parameter. - Eg. $this->dbutil->backup('db_backup_filename'); + $this->dbutil->backup('db_backup_filename'); .. note:: Due to the limited execution time and memory available to PHP, backing up very large databases may not be possible. If your database is @@ -197,7 +197,7 @@ Usage Example $this->load->dbutil(); // Backup your entire database and assign it to a variable - $backup =& $this->dbutil->backup(); + $backup = $this->dbutil->backup(); // Load the file helper and write the file to your server $this->load->helper('file'); @@ -295,7 +295,7 @@ Class Reference Repairs a database table. - .. php:method:: csv_from_results($query[, $delim = ','[, $newline = "\n"[, $enclosure = '"']]]) + .. php:method:: csv_from_result($query[, $delim = ','[, $newline = "\n"[, $enclosure = '"']]]) :param object $query: A database result object :param string $delim: The CSV field delimiter to use @@ -306,11 +306,11 @@ Class Reference Translates a database result object into a CSV document. - .. php:method:: xml_from_results($query[, $params = array()]) + .. php:method:: xml_from_result($query[, $params = array()]) :param object $query: A database result object :param array $params: An associative array of preferences :returns: The generated XML document as a string :rtype: string - Translates a database result object into an XML document.
\ No newline at end of file + Translates a database result object into an XML document. diff --git a/user_guide_src/source/general/ancillary_classes.rst b/user_guide_src/source/general/ancillary_classes.rst index 6a64742ce..f1285d931 100644 --- a/user_guide_src/source/general/ancillary_classes.rst +++ b/user_guide_src/source/general/ancillary_classes.rst @@ -11,7 +11,8 @@ get_instance() .. php:function:: get_instance() - :returns: object of class CI_Controller + :returns: Reference to your controller's instance + :rtype: CI_Controller **Any class that you instantiate within your controller methods can access CodeIgniter's native resources** simply by using the @@ -44,17 +45,9 @@ Once you've assigned the object to a variable, you'll use that variable $CI->config->item('base_url'); // etc. -.. note:: You'll notice that the above get_instance() ``function`` is being - passed by reference:: - - $CI =& get_instance(); - - This is very important. Assigning by reference allows you to use the - original CodeIgniter object rather than creating a copy of it. - -Furthermore, if you'll be using ``get_instance()`` inside another class, -then it would be better if you assign it to a property. This way, you -won't need to call ``get_instance()`` in every single method. +If you'll be using ``get_instance()`` inside another class, then it would +be better if you assign it to a property. This way, you won't need to call +``get_instance()`` in every single method. Example:: @@ -80,9 +73,8 @@ Example:: { $this->CI->config->item('base_url'); } - } In the above example, both methods ``foo()`` and ``bar()`` will work after you instantiate the Example class, without the need to call -``get_instance()`` in each of them.
\ No newline at end of file +``get_instance()`` in each of them. diff --git a/user_guide_src/source/general/controllers.rst b/user_guide_src/source/general/controllers.rst index bc8319dd8..5a111d8dc 100644 --- a/user_guide_src/source/general/controllers.rst +++ b/user_guide_src/source/general/controllers.rst @@ -138,11 +138,14 @@ present, as will be the case when only your site root URL is requested. To specify a default controller, open your **application/config/routes.php** file and set this variable:: - $route['default_controller'] = 'Blog'; + $route['default_controller'] = 'blog'; -Where Blog is the name of the controller class you want used. If you now +Where 'blog' is the name of the controller class you want used. If you now load your main index.php file without specifying any URI segments you'll -see your Hello World message by default. +see your "Hello World" message by default. + +For more information, please refer to the "Reserved Routes" section of the +:doc:`URI Routing <routing>` documentation. Remapping Method Calls ====================== @@ -263,12 +266,12 @@ Trying to access it via the URL, like this, will not work:: Organizing Your Controllers into Sub-directories ================================================ -If you are building a large application you might find it convenient to -organize your controllers into sub-directories. CodeIgniter permits you -to do this. +If you are building a large application you might want to hierarchically +organize or structure your controllers into sub-directories. CodeIgniter +permits you to do this. -Simply create folders within your *application/controllers/* directory -and place your controller classes within them. +Simply create sub-directories under the main *application/controllers/* +one and place your controller classes within them. .. note:: When using this feature the first segment of your URI must specify the folder. For example, let's say you have a controller located @@ -281,8 +284,9 @@ and place your controller classes within them. example.com/index.php/products/shoes/show/123 Each of your sub-directories may contain a default controller which will be -called if the URL contains only the sub-folder. Simply name your default -controller as specified in your *application/config/routes.php* file. +called if the URL contains *only* the sub-directory. Simply put a controller +in there that matches the name of your 'default_controller' as specified in +your *application/config/routes.php* file. CodeIgniter also permits you to remap your URIs using its :doc:`URI Routing <routing>` feature. diff --git a/user_guide_src/source/general/credits.rst b/user_guide_src/source/general/credits.rst index d22e3a9bc..d0f14b3bd 100644 --- a/user_guide_src/source/general/credits.rst +++ b/user_guide_src/source/general/credits.rst @@ -2,17 +2,17 @@ Credits ####### -CodeIgniter was originally developed by `Rick Ellis <http://www.ellislab.com/>`_ -(CEO of `EllisLab, Inc. <http://ellislab.com/>`_). The framework was written for +CodeIgniter was originally developed by `Rick Ellis <https://ellislab.com/>`_ +(CEO of `EllisLab, Inc. <https://ellislab.com/>`_). The framework was written for performance in the real world, with many of the class libraries, helpers, and sub-systems borrowed from the code-base of `ExpressionEngine -<http://www.expressionengine.com/>`_. +<https://ellislab.com/expressionengine>`_. It was, for years, developed and maintained by EllisLab, the ExpressionEngine Development Team and a group of community members called the Reactor Team. In 2014, CodeIgniter was acquired by the `British Columbia Institute of Technology -<http://bcit.ca/>`_ and was then officially announced as a community-maintained +<http://www.bcit.ca/>`_ and was then officially announced as a community-maintained project. Bleeding edge development is spearheaded by the handpicked contributors diff --git a/user_guide_src/source/general/environments.rst b/user_guide_src/source/general/environments.rst index 1ce4fde3a..ac6f3235e 100644 --- a/user_guide_src/source/general/environments.rst +++ b/user_guide_src/source/general/environments.rst @@ -48,5 +48,5 @@ Configuration Files Optionally, you can have CodeIgniter load environment-specific configuration files. This may be useful for managing things like differing API keys across multiple environments. This is described in -more detail in the environment section of the `Config -Class <../libraries/config.html#environments>`_ documentation.
\ No newline at end of file +more detail in the environment section of the :doc:`Config Class +<../libraries/config>` documentation.
\ 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 e3f73dba8..f90cdd30d 100644 --- a/user_guide_src/source/general/requirements.rst +++ b/user_guide_src/source/general/requirements.rst @@ -2,7 +2,7 @@ Server Requirements ################### -`PHP <http://www.php.net/>`_ version 5.4 or newer is recommended. +`PHP <http://php.net/>`_ version 5.4 or newer is recommended. It should work on 5.2.4 as well, but we strongly advise you NOT to run such old versions of PHP, because of potential security and performance diff --git a/user_guide_src/source/general/reserved_names.rst b/user_guide_src/source/general/reserved_names.rst index a7b0c3465..5d745cba6 100644 --- a/user_guide_src/source/general/reserved_names.rst +++ b/user_guide_src/source/general/reserved_names.rst @@ -75,6 +75,7 @@ Constants - FOPEN_READ_WRITE_CREATE - FOPEN_WRITE_CREATE_STRICT - FOPEN_READ_WRITE_CREATE_STRICT +- SHOW_DEBUG_BACKTRACE - EXIT_SUCCESS - EXIT_ERROR - EXIT_CONFIG diff --git a/user_guide_src/source/general/routing.rst b/user_guide_src/source/general/routing.rst index 766e0b2ab..b2c9873ab 100644 --- a/user_guide_src/source/general/routing.rst +++ b/user_guide_src/source/general/routing.rst @@ -170,11 +170,16 @@ There are three reserved routes:: $route['default_controller'] = 'welcome'; -This route indicates which controller class should be loaded if the URI -contains no data, which will be the case when people load your root URL. -In the above example, the "welcome" class would be loaded. You are -encouraged to always have a default route otherwise a 404 page will -appear by default. +This route points to the action that should be executed if the URI contains +no data, which will be the case when people load your root URL. +The setting accepts a **controller/method** value and ``index()`` would be +the default method if you don't specify one. In the above example, it is +``Welcome::index()`` that would be called. + +.. note:: You can NOT use a directory as a part of this setting! + +You are encouraged to always have a default route as otherwise a 404 page +will appear by default. :: @@ -182,11 +187,13 @@ appear by default. This route indicates which controller class should be loaded if the requested controller is not found. It will override the default 404 -error page. It won't affect to the ``show_404()`` function, which will +error page. Same per-directory rules as with 'default_controller' +apply here as well. + +It won't affect to the ``show_404()`` function, which will continue loading the default *error_404.php* file at *application/views/errors/error_404.php*. - :: $route['translate_uri_dashes'] = FALSE; diff --git a/user_guide_src/source/general/security.rst b/user_guide_src/source/general/security.rst index 0c58f96b4..8afdaca31 100644 --- a/user_guide_src/source/general/security.rst +++ b/user_guide_src/source/general/security.rst @@ -5,6 +5,9 @@ Security This page describes some "best practices" regarding web security, and details CodeIgniter's internal security features. +.. note:: If you came here looking for a security contact, please refer to + our `Contribution Guide <../contributing/index>`. + URI Security ============ @@ -61,7 +64,7 @@ data from the SERVER array, you are encouraged to practice this three step approach: #. Validate the data to ensure it conforms to the correct type, length, - size, etc. (sometimes this step can replace step one) + size, etc. #. Filter the data as if it were tainted. #. Escape the data before submitting it into your database or outputting it to a browser. @@ -133,17 +136,25 @@ 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" feature, just randomly generate a new, one-time (this is also important) password and send that instead. -- DO NOT put artificial limits on your users' passwords. +- DO NOT put unnecessary limits on your users' passwords. + + If you're using a hashing algorithm other than BCrypt (which has a limit + of 72 characters), you should set a relatively high limit on password + lengths in order to mitigate DoS attacks - say, 1024 characters. - There's no point in forcing a rule that a password can only be up to - a number of characters, or that it can't contain a certain set of - special characters. + Other than that however, there's no point in forcing a rule that a + password can only be up to a number of characters, or that it can't + contain a certain set of special characters. Not only does this **reduce** security instead of improving it, but there's literally no reason to do it. No technical limitations and @@ -191,4 +202,4 @@ file to restrict access to those resources. CodeIgniter will have an index.html file in all of its directories in an attempt to hide some of this data, but have it in mind that this is not enough to prevent a serious -attacker.
\ No newline at end of file +attacker. diff --git a/user_guide_src/source/helpers/date_helper.rst b/user_guide_src/source/helpers/date_helper.rst index bed3b32a2..a85da26a4 100644 --- a/user_guide_src/source/helpers/date_helper.rst +++ b/user_guide_src/source/helpers/date_helper.rst @@ -50,7 +50,7 @@ The following functions are available: :returns: MySQL-formatted date :rtype: string - This function is identical to PHP's `date() <http://www.php.net/date>`_ + This function is identical to PHP's `date() <http://php.net/manual/en/function.date.php>`_ function, except that it lets you use MySQL style date codes, where each code letter is preceded with a percent sign, e.g. `%Y %m %d` @@ -84,7 +84,7 @@ The following functions are available: .. 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>`_ + <http://php.net/manual/en/class.datetime.php#datetime.constants.types>`_ instead:: echo date(DATE_RFC822, time()); @@ -400,7 +400,7 @@ 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 +UM7 (UTC - 07: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 diff --git a/user_guide_src/source/helpers/email_helper.rst b/user_guide_src/source/helpers/email_helper.rst index 685226951..1ee97d902 100644 --- a/user_guide_src/source/helpers/email_helper.rst +++ b/user_guide_src/source/helpers/email_helper.rst @@ -62,7 +62,7 @@ The following functions are available: :returns: TRUE if the mail was successfully sent, FALSE in case of an error :rtype: bool - Sends an email using PHP's native `mail() <http://www.php.net/function.mail>`_ + Sends an email using PHP's native `mail() <http://php.net/function.mail>`_ function. .. note:: All that this function does is to use PHP's native ``mail`` diff --git a/user_guide_src/source/helpers/file_helper.rst b/user_guide_src/source/helpers/file_helper.rst index 92cb31a82..833cddea4 100644 --- a/user_guide_src/source/helpers/file_helper.rst +++ b/user_guide_src/source/helpers/file_helper.rst @@ -76,7 +76,7 @@ The following functions are available: write_file('./path/to/file.php', $data, 'r+'); - The default mode is 'wb'. Please see the `PHP user guide <http://php.net/fopen>`_ + The default mode is 'wb'. Please see the `PHP user guide <http://php.net/manual/en/function.fopen.php>`_ for mode options. .. note: In order for this function to write data to a file, its permissions must diff --git a/user_guide_src/source/helpers/form_helper.rst b/user_guide_src/source/helpers/form_helper.rst index 4706ee706..d3ee3ffb6 100644 --- a/user_guide_src/source/helpers/form_helper.rst +++ b/user_guide_src/source/helpers/form_helper.rst @@ -191,7 +191,7 @@ The following functions are available: :param array $data: Field attributes data :param string $value: Field value - :param string $extra: Extra attributes to be added to the tag *as is* + :param mixed $extra: Extra attributes to be added to the tag either as an array or a literal string :returns: An HTML text input field tag :rtype: string @@ -226,11 +226,16 @@ The following functions are available: $js = 'onClick="some_function()"'; echo form_input('username', 'johndoe', $js); + Or you can pass it as an array:: + + $js = array('onClick' => 'some_function();'); + echo form_input('username', 'johndoe', $js); + .. php:function:: form_password([$data = ''[, $value = ''[, $extra = '']]]) :param array $data: Field attributes data :param string $value: Field value - :param string $extra: Extra attributes to be added to the tag *as is* + :param mixed $extra: Extra attributes to be added to the tag either as an array or a literal string :returns: An HTML password input field tag :rtype: string @@ -242,7 +247,7 @@ The following functions are available: :param array $data: Field attributes data :param string $value: Field value - :param string $extra: Extra attributes to be added to the tag *as is* + :param mixed $extra: Extra attributes to be added to the tag either as an array or a literal string :returns: An HTML file upload input field tag :rtype: string @@ -255,7 +260,7 @@ The following functions are available: :param array $data: Field attributes data :param string $value: Field value - :param string $extra: Extra attributes to be added to the tag *as is* + :param mixed $extra: Extra attributes to be added to the tag either as an array or a literal string :returns: An HTML textarea tag :rtype: string @@ -270,7 +275,7 @@ The following functions are available: :param string $name: Field name :param array $options: An associative array of options to be listed :param array $selected: List of fields to mark with the *selected* attribute - :param string $extra: Extra attributes to be added to the tag *as is* + :param mixed $extra: Extra attributes to be added to the tag either as an array or a literal string :returns: An HTML dropdown select field tag :rtype: string @@ -324,6 +329,14 @@ The following functions are available: $js = 'id="shirts" onChange="some_function();"'; echo form_dropdown('shirts', $options, 'large', $js); + Or you can pass it as an array:: + + $js = array( + 'id' => 'shirts', + 'onChange' => 'some_function();' + ); + echo form_dropdown('shirts', $options, 'large', $js); + If the array passed as ``$options`` is a multidimensional array, then ``form_dropdown()`` will produce an <optgroup> with the array key as the label. @@ -334,7 +347,7 @@ The following functions are available: :param string $name: Field name :param array $options: An associative array of options to be listed :param array $selected: List of fields to mark with the *selected* attribute - :param string $extra: Extra attributes to be added to the tag *as is* + :param mixed $extra: Extra attributes to be added to the tag either as an array or a literal string :returns: An HTML dropdown multiselect field tag :rtype: string @@ -417,7 +430,7 @@ The following functions are available: :param array $data: Field attributes data :param string $value: Field value :param bool $checked: Whether to mark the checkbox as being *checked* - :param string $extra: Extra attributes to be added to the tag *as is* + :param mixed $extra: Extra attributes to be added to the tag either as an array or a literal string :returns: An HTML checkbox input tag :rtype: string @@ -450,13 +463,18 @@ The following functions are available: $js = 'onClick="some_function()"'; echo form_checkbox('newsletter', 'accept', TRUE, $js) + Or you can pass it as an array:: + + $js = array('onClick' => 'some_function();'); + echo form_checkbox('newsletter', 'accept', TRUE, $js) + .. php:function:: form_radio([$data = ''[, $value = ''[, $checked = FALSE[, $extra = '']]]]) :param array $data: Field attributes data :param string $value: Field value :param bool $checked: Whether to mark the radio button as being *checked* - :param string $extra: Extra attributes to be added to the tag *as is* + :param mixed $extra: Extra attributes to be added to the tag either as an array or a literal string :returns: An HTML radio input tag :rtype: string @@ -495,7 +513,7 @@ The following functions are available: :param string $data: Button name :param string $value: Button value - :param string $extra: Extra attributes to be added to the tag *as is* + :param mixed $extra: Extra attributes to be added to the tag either as an array or a literal string :returns: An HTML input submit tag :rtype: string @@ -513,7 +531,7 @@ The following functions are available: :param string $data: Button name :param string $value: Button value - :param string $extra: Extra attributes to be added to the tag *as is* + :param mixed $extra: Extra attributes to be added to the tag either as an array or a literal string :returns: An HTML input reset button tag :rtype: string @@ -525,7 +543,7 @@ The following functions are available: :param string $data: Button name :param string $content: Button label - :param string $extra: Extra attributes to be added to the tag *as is* + :param mixed $extra: Extra attributes to be added to the tag either as an array or a literal string :returns: An HTML button tag :rtype: string @@ -571,10 +589,11 @@ The following functions are available: // Would produce: </form> </div></div> -.. php:function:: set_value($field[, $default = '']) +.. php:function:: set_value($field[, $default = ''[, $html_escape = TRUE]]) :param string $field: Field name :param string $default: Default value + :param bool $html_escape: Whether to turn off HTML escaping of the value :returns: Field value :rtype: string @@ -591,6 +610,11 @@ The following functions are available: The above form will show "0" when loaded for the first time. + .. note:: If you've loaded the :doc:`Form Validation Library <../libraries/form_validation>` and + have set a validation rule for the field name in use with this helper, then it will + forward the call to the :doc:`Form Validation Library <../libraries/form_validation>`'s + own ``set_value()`` method. Otherwise, this function looks in ``$_POST`` for the + field value. .. php:function:: set_select($field[, $value = ''[, $default = FALSE]]) diff --git a/user_guide_src/source/helpers/html_helper.rst b/user_guide_src/source/helpers/html_helper.rst index d35be396a..2c748bea0 100644 --- a/user_guide_src/source/helpers/html_helper.rst +++ b/user_guide_src/source/helpers/html_helper.rst @@ -29,7 +29,7 @@ The following functions are available: :param string $data: Content :param string $h: Heading level - :param array $attributes: HTML attributes + :param mixed $attributes: HTML attributes :returns: HTML heading tag :rtype: string @@ -41,15 +41,18 @@ The following functions are available: The above would produce: <h3>Welcome!</h3> Additionally, in order to add attributes to the heading tag such as HTML - classes, ids or inline styles, a third parameter is available:: + classes, ids or inline styles, a third parameter accepts either a string + or an array:: - echo heading('Welcome!', 3, 'class="pink"') + echo heading('Welcome!', 3, 'class="pink"'); + echo heading('How are you?', 4, array('id' => 'question', 'class' => 'green')); The above code produces: .. code-block:: html <h3 class="pink">Welcome!<h3> + <h4 id="question" class="green">How are you?</h4> .. php:function:: img([$src = ''[, $index_page = FALSE[, $attributes = '']]]) @@ -142,7 +145,7 @@ The following functions are available: :returns: HTML-formatted unordered list :rtype: string - Permits you to generate ordered or unordered HTML lists from simple or + Permits you to generate unordered HTML lists from simple or multi-dimensional arrays. Example:: $list = array( @@ -401,4 +404,4 @@ The following functions are available: .. note:: This function is DEPRECATED. Use the native ``str_repeat()`` - in combination with `` `` instead.
\ No newline at end of file + in combination with `` `` instead. diff --git a/user_guide_src/source/helpers/smiley_helper.rst b/user_guide_src/source/helpers/smiley_helper.rst index 978d11e5f..3e7669942 100644 --- a/user_guide_src/source/helpers/smiley_helper.rst +++ b/user_guide_src/source/helpers/smiley_helper.rst @@ -43,7 +43,7 @@ download and install the smiley images, then create a controller and the View as described. .. important:: Before you begin, please `download the smiley images - <http://ellislab.com/asset/ci_download_files/smileys.zip>`_ + <https://ellislab.com/asset/ci_download_files/smileys.zip>`_ and put them in a publicly accessible place on your server. This helper also assumes you have the smiley replacement array located at `application/config/smileys.php` diff --git a/user_guide_src/source/helpers/string_helper.rst b/user_guide_src/source/helpers/string_helper.rst index 9d0d890b3..53052557e 100644 --- a/user_guide_src/source/helpers/string_helper.rst +++ b/user_guide_src/source/helpers/string_helper.rst @@ -5,6 +5,9 @@ String Helper The String Helper file contains functions that assist in working with strings. +.. important:: Please note that these functions are NOT intended, nor + suitable to be used for any kind of security-related logic. + .. contents:: :local: @@ -124,7 +127,7 @@ The following functions are available: :rtype: string Converts double slashes in a string to a single slash, except those - found in URL protocol prefixes (e.g. http://). + found in URL protocol prefixes (e.g. http://). Example:: diff --git a/user_guide_src/source/helpers/text_helper.rst b/user_guide_src/source/helpers/text_helper.rst index d1723c58a..ef47882fb 100644 --- a/user_guide_src/source/helpers/text_helper.rst +++ b/user_guide_src/source/helpers/text_helper.rst @@ -193,7 +193,11 @@ The following functions are available: $string = "Here is a simple string of text that will help us demonstrate this function."; echo word_wrap($string, 25); - // Would produce: Here is a simple string of text that will help us demonstrate this function + // Would produce: + // Here is a simple string + // of text that will help us + // demonstrate this + // function. .. php:function:: ellipsize($str, $max_length[, $position = 1[, $ellipsis = '…']]) diff --git a/user_guide_src/source/helpers/url_helper.rst b/user_guide_src/source/helpers/url_helper.rst index 83864d9d3..64deae240 100644 --- a/user_guide_src/source/helpers/url_helper.rst +++ b/user_guide_src/source/helpers/url_helper.rst @@ -144,7 +144,7 @@ Available Functions be a string or an array. .. note:: If you are building links that are internal to your application - do not include the base URL (http://...). This will be added + do not include the base URL (http://...). This will be added automatically from the information specified in your config file. Include only the URI segments you wish appended to the URL. @@ -317,7 +317,7 @@ Available Functions :returns: Protocol-prefixed URL string :rtype: string - This function will add http:// in the event that a protocol prefix + This function will add http:// in the event that a protocol prefix is missing from a URL. Pass the URL string to the function like this:: diff --git a/user_guide_src/source/index.rst b/user_guide_src/source/index.rst index 8d8aa9438..a13ec983e 100644 --- a/user_guide_src/source/index.rst +++ b/user_guide_src/source/index.rst @@ -54,6 +54,16 @@ Tutorial tutorial/index +*************************** +Contributing to CodeIgniter +*************************** + +.. toctree:: + :glob: + :titlesonly: + + contributing/index + ************** General Topics ************** @@ -94,16 +104,6 @@ Helper Reference helpers/index -*************************** -Contributing to CodeIgniter -*************************** - -.. toctree:: - :glob: - :titlesonly: - - contributing/index - .. toctree:: :glob: :titlesonly: diff --git a/user_guide_src/source/installation/downloads.rst b/user_guide_src/source/installation/downloads.rst index 3b4ebe64b..c5ce2e836 100644 --- a/user_guide_src/source/installation/downloads.rst +++ b/user_guide_src/source/installation/downloads.rst @@ -2,14 +2,22 @@ Downloading CodeIgniter ####################### -- `CodeIgniter v3.0.0 (Current version) <https://github.com/bcit-ci/CodeIgniter/archive/develop.zip>`_ -- `CodeIgniter v2.2.1 <https://github.com/bcit-ci/CodeIgniter/archive/2.2.1.zip>`_ -- `CodeIgniter v2.2.0 <https://github.com/bcit-ci/CodeIgniter/archive/2.2.0.zip>`_ -- `CodeIgniter v2.1.4 <https://github.com/bcit-ci/CodeIgniter/archive/2.1.4.zip>`_ -- `CodeIgniter v2.1.3 <https://github.com/bcit-ci/CodeIgniter/archive/2.1.3.zip>`_ -- `CodeIgniter v2.1.2 <https://github.com/bcit-ci/CodeIgniter/archive/2.1.2.zip>`_ -- `CodeIgniter v2.1.1 <https://github.com/bcit-ci/CodeIgniter/archive/2.1.1.zip>`_ -- `CodeIgniter v2.1.0 <https://github.com/bcit-ci/CodeIgniter/archive/v2.1.0.zip>`_ +<<<<<<< HEAD +- `CodeIgniter v3.1.0-dev (Current version) <https://codeload.github.com/bcit-ci/CodeIgniter/zip/develop>`_ +- `CodeIgniter v3.0.4-dev <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0-stable>`_ +- `CodeIgniter v3.0.3 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.3>`_ +- `CodeIgniter v3.0.2 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.2>`_ +- `CodeIgniter v3.0.1 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.1>`_ +- `CodeIgniter v3.0.0 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.0>`_ +- `CodeIgniter v2.2.3 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.2.3>`_ +- `CodeIgniter v2.2.2 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.2.2>`_ +- `CodeIgniter v2.2.1 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.2.1>`_ +- `CodeIgniter v2.2.0 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.2.0>`_ +- `CodeIgniter v2.1.4 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.1.4>`_ +- `CodeIgniter v2.1.3 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.1.3>`_ +- `CodeIgniter v2.1.2 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.1.2>`_ +- `CodeIgniter v2.1.1 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/2.1.1>`_ +- `CodeIgniter v2.1.0 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/v2.1.0>`_ ****** GitHub diff --git a/user_guide_src/source/installation/troubleshooting.rst b/user_guide_src/source/installation/troubleshooting.rst index 0dfd4083f..e874bb0ec 100644 --- a/user_guide_src/source/installation/troubleshooting.rst +++ b/user_guide_src/source/installation/troubleshooting.rst @@ -4,16 +4,15 @@ Troubleshooting If you find that no matter what you put in your URL only your default page is loading, it might be that your server does not support the -PATH_INFO variable needed to serve search-engine friendly URLs. As a +REQUEST_URI variable needed to serve search-engine friendly URLs. As a first step, open your application/config/config.php file and look for the URI Protocol information. It will recommend that you try a couple alternate settings. If it still doesn't work after you've tried this you'll need to force CodeIgniter to add a question mark to your URLs. To -do this open your application/config/config.php file and change this:: +do this open your **application/config/config.php** file and change this:: $config['index_page'] = "index.php"; To this:: $config['index_page'] = "index.php?"; - diff --git a/user_guide_src/source/installation/upgrade_200.rst b/user_guide_src/source/installation/upgrade_200.rst index ca2c6c1e0..03b8ff4ac 100644 --- a/user_guide_src/source/installation/upgrade_200.rst +++ b/user_guide_src/source/installation/upgrade_200.rst @@ -64,8 +64,8 @@ string using the improved methods. This will enable you to easily replace stale encrypted data with fresh in your applications, either on the fly or en masse. -Please read `how to use this -method <../libraries/encrypt.html#legacy>`_ in the Encrypt library +Please read :doc:`how to use this +method <../libraries/encrypt>` in the Encrypt library documentation. Step 5: Remove loading calls for the compatibility helper. diff --git a/user_guide_src/source/installation/upgrade_220.rst b/user_guide_src/source/installation/upgrade_220.rst index b2e943223..91f9e00cd 100644 --- a/user_guide_src/source/installation/upgrade_220.rst +++ b/user_guide_src/source/installation/upgrade_220.rst @@ -1,5 +1,5 @@ ############################# -Upgrading from 2.1.4 to 2.2.0 +Upgrading from 2.1.4 to 2.2.x ############################# .. note:: The :doc:`Encrypt Class </libraries/encrypt>` now requires the diff --git a/user_guide_src/source/installation/upgrade_222.rst b/user_guide_src/source/installation/upgrade_222.rst new file mode 100644 index 000000000..9dcc61d0e --- /dev/null +++ b/user_guide_src/source/installation/upgrade_222.rst @@ -0,0 +1,14 @@ +############################# +Upgrading from 2.2.1 to 2.2.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. + +.. note:: If you have any custom developed files in these folders please + make copies of them first.
\ No newline at end of file diff --git a/user_guide_src/source/installation/upgrade_223.rst b/user_guide_src/source/installation/upgrade_223.rst new file mode 100644 index 000000000..252318ae1 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_223.rst @@ -0,0 +1,14 @@ +############################# +Upgrading from 2.2.2 to 2.2.3 +############################# + +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. + +.. note:: If you have any custom developed files in these folders please + make copies of them first.
\ 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 8983f3d18..a29f400f8 100644 --- a/user_guide_src/source/installation/upgrade_300.rst +++ b/user_guide_src/source/installation/upgrade_300.rst @@ -1,18 +1,20 @@ ############################# -Upgrading from 2.2.1 to 3.0.0 +Upgrading from 2.2.x to 3.0.x ############################# -.. 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. ************************************* 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/* directory 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. + +.. important:: You have to delete the old *system/* directory first and + then put the new one in its place. A simple copy-paste may cause + issues. .. note:: If you have any custom developed files in these folders please make copies of them first. @@ -36,12 +38,12 @@ For example, if you have the following library file: The same goes for driver libraries and extensions and/or overrides of CodeIgniter's own libraries and core classes. - application/libraries/MY_email.php + application/libraries/MY_email.php application/core/MY_log.php The above files should respectively be renamed to the following: - application/libraries/MY_Email.php + application/libraries/MY_Email.php application/core/MY_Log.php Controllers: @@ -78,9 +80,9 @@ Step 5: Move your Log class overrides or extensions The Log Class is considered as a "core" class and is now located in the **system/core/** directory. Therefore, in order for your Log class overrides -or extensions to work, you need to move them to **application/core/**:: +or extensions to work, you need to move them to **application/core/**: - application/libraries/Log.php -> application/core/Log.php + application/libraries/Log.php -> application/core/Log.php application/libraries/MY_Log.php -> application/core/MY_Log.php ***************************************** @@ -212,26 +214,62 @@ is suitable for the command line. This of course requires another level of separ It is safe to move your old templates from _application/errors* to _application/views/errors/html*, but you'll have to copy the new _application/views/errors/cli* directory from the CodeIgniter archive. -******************************************************* -Step 9: Update your config/routes.php containing (:any) -******************************************************* +****************************************** +Step 9: Update your config/routes.php file +****************************************** + +Routes containing :any +====================== + +Historically, CodeIgniter has always provided the **:any** wildcard in +routing, with the intention of providing a way to match any character +**within** an URI segment. -Historically, CodeIgniter has always provided the **:any** wildcard in routing, -with the intention of providing a way to match any character **within** an URI segment. +However, the **:any** wildcard is actually just an alias for a regular +expression and used to be executed in that manner as **.+**. This is +considered a bug, as it also matches the / (forward slash) character, which +is the URI segment delimiter and that was never the intention. -However, the **:any** wildcard is actually just an alias for a regular expression -and used to be executed in that manner as **.+**. This is considered a bug, as it -also matches the / (forward slash) character, which is the URI segment delimiter -and that was never the intention. In CodeIgniter 3, the **:any** wildcard will now -represent **[^/]+**, so that it will not match a forward slash. +In CodeIgniter 3, the **:any** wildcard will now represent **[^/]+**, so +that it will not match a forward slash. -There are certainly many developers that have utilized this bug as an actual feature. -If you're one of them and want to match a forward slash, please use the **.+** -regular expression:: +There are certainly many developers that have utilized this bug as an actual +feature. If you're one of them and want to match a forward slash, please use +the **.+** regular expression:: (.+) // matches ANYTHING (:any) // matches any character, except for '/' +Directories and 'default_controller', '404_override' +==================================================== + +As you should know, the ``$route['default_controller']`` and +``$route['404_override']`` settings accept not only a controller name, but +also *controller/method* pairs. However, a bug in the routing logic has +made it possible for some users to use that as *directory/controller* +instead. + +As already said, this behavior was incidental and was never intended, nor +documented. If you've relied on it, your application will break with +CodeIgniter 3.0. + +Another notable change in version 3 is that 'default_controller' and +'404_override' are now applied *per directory*. To explain what this means, +let's take the following example:: + + $route['default_controller'] = 'main'; + +Now, assuming that your website is located at *example.com*, you already +know that if a user visits ``http://example.com/``, the above setting will +cause your 'Main' controller to be loaded. + +However, what happens if you have an *application/controllers/admin/* +directory and the user visits ``http://example.com/admin/``? +In CodeIgniter 3, the router will look for a 'Main' controller under the +admin/ directory as well. If not found, a Not Found (404) will be triggered. + +The same rule applies to the '404_override' setting. + ************************************************************************* Step 10: Many functions now return NULL instead of FALSE on missing items ************************************************************************* @@ -426,8 +464,51 @@ files and error messages format: Therefore you're encouraged to update its usage sooner rather than later. +************************************************************ +Step 19: Make sure your 'base_url' config value is not empty +************************************************************ + +When ``$config['base_url']`` is not set, CodeIgniter tries to automatically +detect what your website's base URL is. This is done purely for convenience +when you are starting development of a new application. + +Auto-detection is never reliable and also has security implications, which +is why you should **always** have it manually configured! + +One of the changes in CodeIgniter 3.0.3 is how this auto-detection works, +and more specifically it now falls back to the server's IP address instead +of the hostname requested by the client. Therefore, if you've ever relied +on auto-detection, it will change how your website works now. + +In case you need to allow e.g. multiple domains, or both http:// and +https:// prefixes to be dynamically used depending on the request, +remember that *application/config/config.php* is still a PHP script, in +which you can create this logic with a few lines of code. For example:: + + $allowed_domains = array('domain1.tld', 'domain2.tld'); + $default_domain = 'domain1.tld'; + + if (in_array($_SERVER['HTTP_HOST'], $allowed_domains, TRUE)) + { + $domain = $_SERVER['HTTP_HOST']; + } + else + { + $domain = $default_domain; + } + + if ( ! empty($_SERVER['HTTPS'])) + { + $config['base_url'] = 'https://'.$domain; + } + else + { + $config['base_url'] = 'http://'.$domain; + } + + **************************************************************** -Step 19: Remove usage of (previously) deprecated functionalities +Step 20: Remove usage of (previously) deprecated functionalities **************************************************************** In addition to the ``$autoload['core']`` configuration setting, there's a @@ -514,6 +595,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() ======================= @@ -574,7 +671,7 @@ 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>`_, +to the availability of native PHP `constants <http://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 its usage: @@ -758,7 +855,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/upgrade_301.rst b/user_guide_src/source/installation/upgrade_301.rst new file mode 100644 index 000000000..450cdb288 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_301.rst @@ -0,0 +1,19 @@ +############################# +Upgrading from 3.0.0 to 3.0.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/* directory. + +.. note:: If you have any custom developed files in these directories, + please make copies of them first. + +Step 2: Update your CLI error templates +======================================= + +Replace all files under your *application/views/errors/cli/* directory. diff --git a/user_guide_src/source/installation/upgrade_302.rst b/user_guide_src/source/installation/upgrade_302.rst new file mode 100644 index 000000000..93d87ac41 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_302.rst @@ -0,0 +1,24 @@ +############################# +Upgrading from 3.0.1 to 3.0.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/* directory. + +.. note:: If you have any custom developed files in these directories, + please make copies of them first. + +Step 2: Update your application/config/constants.php file +========================================================= + +The *application/config/constants.php* file has been updated to check +if constants aren't already defined before doing that, making it easier +to add an environment-specific configuration. + +.. note:: If you've made modifications to this file, please make a + backup first and cross-check the differences first.
\ No newline at end of file diff --git a/user_guide_src/source/installation/upgrade_303.rst b/user_guide_src/source/installation/upgrade_303.rst new file mode 100644 index 000000000..d13a0fe46 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_303.rst @@ -0,0 +1,55 @@ +############################# +Upgrading from 3.0.2 to 3.0.3 +############################# + +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/* directory. + +.. note:: If you have any custom developed files in these directories, + please make copies of them first. + +Step 2: Make sure your 'base_url' config value is not empty +=========================================================== + +When ``$config['base_url']`` is not set, CodeIgniter tries to automatically +detect what your website's base URL is. This is done purely for convenience +when you are starting development of a new application. + +Auto-detection is never reliable and also has security implications, which +is why you should **always** have it manually configured! + +One of the changes in CodeIgniter 3.0.3 is how this auto-detection works, +and more specifically it now falls back to the server's IP address instead +of the hostname requested by the client. Therefore, if you've ever relied +on auto-detection, it will change how your website works now. + +In case you need to allow e.g. multiple domains, or both http:// and +https:// prefixes to be dynamically used depending on the request, +remember that *application/config/config.php* is still a PHP script, in +which you can create this logic with a few lines of code. For example:: + + $allowed_domains = array('domain1.tld', 'domain2.tld'); + $default_domain = 'domain1.tld'; + + if (in_array($_SERVER['HTTP_HOST'], $allowed_domains, TRUE)) + { + $domain = $_SERVER['HTTP_HOST']; + } + else + { + $domain = $default_domain; + } + + if ( ! empty($_SERVER['HTTPS'])) + { + $config['base_url'] = 'https://'.$domain; + } + else + { + $config['base_url'] = 'http://'.$domain; + } diff --git a/user_guide_src/source/installation/upgrade_304.rst b/user_guide_src/source/installation/upgrade_304.rst new file mode 100644 index 000000000..4d5bd2bb0 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_304.rst @@ -0,0 +1,14 @@ +############################# +Upgrading from 3.0.3 to 3.0.4 +############################# + +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/* directory. + +.. note:: If you have any custom developed files in these directories, + please make copies of them first. diff --git a/user_guide_src/source/installation/upgrade_310.rst b/user_guide_src/source/installation/upgrade_310.rst new file mode 100644 index 000000000..7060ebc4c --- /dev/null +++ b/user_guide_src/source/installation/upgrade_310.rst @@ -0,0 +1,14 @@ +############################# +Upgrading from 3.0.x to 3.1.x +############################# + +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/* directory. + +.. note:: If you have any custom developed files in these directories, + please make copies of them first. diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst index ab36e9bfd..2a3b0b047 100644 --- a/user_guide_src/source/installation/upgrading.rst +++ b/user_guide_src/source/installation/upgrading.rst @@ -8,9 +8,16 @@ upgrading from. .. toctree:: :titlesonly: - Upgrading from 2.2.1 to 3.0.0 <upgrade_300> + Upgrading from 3.0.x to 3.1.x <upgrade_310> + Upgrading from 3.0.3 to 3.0.4 <upgrade_304> + Upgrading from 3.0.2 to 3.0.3 <upgrade_303> + Upgrading from 3.0.1 to 3.0.2 <upgrade_302> + Upgrading from 3.0.0 to 3.0.1 <upgrade_301> + Upgrading from 2.2.x to 3.0.x <upgrade_300> + Upgrading from 2.2.2 to 2.2.3 <upgrade_223> + Upgrading from 2.2.1 to 2.2.2 <upgrade_222> 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.4 to 2.2.x <upgrade_220> Upgrading from 2.1.3 to 2.1.4 <upgrade_214> Upgrading from 2.1.2 to 2.1.3 <upgrade_213> Upgrading from 2.1.1 to 2.1.2 <upgrade_212> diff --git a/user_guide_src/source/libraries/caching.rst b/user_guide_src/source/libraries/caching.rst index 86439b4ee..a7081ec6b 100644 --- a/user_guide_src/source/libraries/caching.rst +++ b/user_guide_src/source/libraries/caching.rst @@ -18,7 +18,7 @@ requirements are not met. Example Usage ************* -The following example will load the cache driver, specify `APC <#apc>`_ +The following example will load the cache driver, specify `APC <#alternative-php-cache-apc-caching>`_ as the driver to use, and fall back to file-based caching if APC is not available in the hosting environment. @@ -66,7 +66,7 @@ Class Reference hosting environment. :: - if ($this->cache->apc->is_supported() + if ($this->cache->apc->is_supported()) { if ($data = $this->cache->apc->get('my_cache')) { @@ -250,8 +250,7 @@ Redis Caching ============= Redis is an in-memory key-value store which can operate in LRU cache mode. -To use it, you need Redis server and phpredis PHP extension -`https://github.com/nicolasff/phpredis <https://github.com/nicolasff/phpredis>`_. +To use it, you need `Redis server and phpredis PHP extension <https://github.com/phpredis/phpredis>`_. Config options to connect to redis server must be stored in the application/config/redis.php file. Available options are:: diff --git a/user_guide_src/source/libraries/calendar.rst b/user_guide_src/source/libraries/calendar.rst index 52883d297..ea0f4d108 100644 --- a/user_guide_src/source/libraries/calendar.rst +++ b/user_guide_src/source/libraries/calendar.rst @@ -179,7 +179,7 @@ will be placed within a pair of pseudo-variables as shown here:: {cal_cell_blank} {/cal_cell_blank} - {cal_cell_other}{day}{cal_cel_other} + {cal_cell_other}{day}{/cal_cel_other} {cal_cell_end}</td>{/cal_cell_end} {cal_cell_end_today}</td>{/cal_cell_end_today} @@ -304,4 +304,4 @@ Class Reference :rtype: CI_Calendar Harvests the data within the template ``{pseudo-variables}`` used to - display the calendar.
\ No newline at end of file + display the calendar. diff --git a/user_guide_src/source/libraries/config.rst b/user_guide_src/source/libraries/config.rst index 3138e3403..a45cacdf5 100644 --- a/user_guide_src/source/libraries/config.rst +++ b/user_guide_src/source/libraries/config.rst @@ -92,9 +92,9 @@ Fetching Config Items To retrieve an item from your config file, use the following function:: - $this->config->item('item name'); + $this->config->item('item_name'); -Where item name is the $config array index you want to retrieve. For +Where item_name is the $config array index you want to retrieve. For example, to fetch your language choice you'll do this:: $lang = $this->config->item('language'); diff --git a/user_guide_src/source/libraries/email.rst b/user_guide_src/source/libraries/email.rst index 54fb53f44..eadfcfd5c 100644 --- a/user_guide_src/source/libraries/email.rst +++ b/user_guide_src/source/libraries/email.rst @@ -373,7 +373,7 @@ Class Reference foreach ($list as $address) { $this->email->to($address); - $cid = $this->email->attach_cid($filename); + $cid = $this->email->attachment_cid($filename); $this->email->message('<img src='cid:". $cid ."' alt="photo1" />'); $this->email->send(); } diff --git a/user_guide_src/source/libraries/encryption.rst b/user_guide_src/source/libraries/encryption.rst index d445bf42f..cac4b7921 100644 --- a/user_guide_src/source/libraries/encryption.rst +++ b/user_guide_src/source/libraries/encryption.rst @@ -2,6 +2,11 @@ Encryption Library ################## +.. important:: DO NOT use this or any other *encryption* library for + user password storage! Passwords must be *hashed* instead, and you + should do that via PHP's own `Password Hashing extension + <http://php.net/password>`_. + The Encryption Library provides two-way data encryption. To do so in a cryptographically secure way, it utilizes PHP extensions that are unfortunately not always available on all systems. @@ -70,7 +75,7 @@ process that allows you to be the only one who is able to decrypt data that you've decided to hide from the eyes of the public. After one key is used to encrypt data, that same key provides the **only** means to decrypt it, so not only must you chose one carefully, but you -must not lose it or you will also use the encrypted data. +must not lose it or you will also lose access to the data. It must be noted that to ensure maximum security, such key *should* not only be as strong as possible, but also often changed. Such behavior @@ -106,6 +111,18 @@ and set:: $config['encryption_key'] = 'YOUR KEY'; +You'll notice that the ``create_key()`` method outputs binary data, which +is hard to deal with (i.e. a copy-paste may damage it), so you may use +``bin2hex()``, ``hex2bin()`` or Base64-encoding to work with the key in +a more friendly manner. For example:: + + // Get a hex-encoded representation of the key: + $key = bin2hex($this->encryption->create_key(16)); + + // Put the same value in your config with hex2bin(), + // so that it is still passed as binary to the library: + $config['encryption_key'] = hex2bin(<your hex-encoded key>); + .. _ciphers-and-modes: Supported encryption ciphers and modes @@ -465,7 +482,7 @@ The reason for not including other popular algorithms, such as MD5 or SHA1 is that they are no longer considered secure enough and as such, we don't want to encourage their usage. If you absolutely need to use them, it is easy to do so via PHP's -native `hash_hmac() <http://php.net/hash_hmac()>`_ function. +native `hash_hmac() <http://php.net/manual/en/function.hash-hmac.php>`_ function. Stronger algorithms of course will be added in the future as they appear and become widely available. @@ -525,6 +542,15 @@ Class Reference Please refer to the :ref:`custom-parameters` secrion for information on the optional parameters. + .. php:method:: create_key($length) + + :param int $length: Output length + :returns: A pseudo-random cryptographic key with the specified length, or FALSE on failure + :rtype: string + + Creates a cryptographic key by fetching random data from + the operating system's sources (i.e. /dev/urandom). + .. php:method:: hkdf($key[, $digest = 'sha512'[, $salt = NULL[, $length = NULL[, $info = '']]]]) :param string $key: Input key material diff --git a/user_guide_src/source/libraries/file_uploading.rst b/user_guide_src/source/libraries/file_uploading.rst index ea2fef7f2..6d2106be8 100644 --- a/user_guide_src/source/libraries/file_uploading.rst +++ b/user_guide_src/source/libraries/file_uploading.rst @@ -118,7 +118,7 @@ this code and save it to your **application/controllers/** directory:: $this->load->library('upload', $config); - if ( ! $this->upload->do_upload()) + if ( ! $this->upload->do_upload('userfile')) { $error = array('error' => $this->upload->display_errors()); @@ -352,4 +352,4 @@ Class Reference image_height Image height image_type Image type (usually the file name extension without the period) image_size_str A string containing the width and height (useful to put into an image tag) - ================ ====================================================================================================
\ No newline at end of file + ================ ==================================================================================================== diff --git a/user_guide_src/source/libraries/form_validation.rst b/user_guide_src/source/libraries/form_validation.rst index da43a4bec..c288cc8c0 100644 --- a/user_guide_src/source/libraries/form_validation.rst +++ b/user_guide_src/source/libraries/form_validation.rst @@ -327,15 +327,15 @@ can also prep your data in various ways. For example, you can set up rules like this:: $this->form_validation->set_rules('username', 'Username', 'trim|required|min_length[5]|max_length[12]'); - $this->form_validation->set_rules('password', 'Password', 'trim|required|md5'); + $this->form_validation->set_rules('password', 'Password', 'trim|required|min_length[8]'); $this->form_validation->set_rules('passconf', 'Password Confirmation', 'trim|required|matches[password]'); $this->form_validation->set_rules('email', 'Email', 'trim|required|valid_email'); In the above example, we are "trimming" the fields, checking for length -where necessary and converting the password to MD5. +where necessary and making sure that both password fields match. **Any native PHP function that accepts one parameter can be used as a -rule, like htmlspecialchars, trim, md5, etc.** +rule, like ``htmlspecialchars()``, ``trim()``, etc.** .. note:: You will generally want to use the prepping functions **after** the validation rules so if there is an error, the @@ -547,7 +547,10 @@ All of the native error messages are located in the following language file: **system/language/english/form_validation_lang.php** To set your own global custom message for a rule, you can either -edit that file, or use the following method:: +extend/override the language file by creating your own in +**application/language/english/form_validation_lang.php** (read more +about this in the :doc:`Language Class <language>` documentation), +or use the following method:: $this->form_validation->set_message('rule', 'Error Message'); @@ -689,8 +692,12 @@ In this case, you can specify the array to be validated:: $this->form_validation->set_data($data); -Creating validation rules, running the validation, and retrieving error messages works the -same whether you are validating ``$_POST`` data or an array. +Creating validation rules, running the validation, and retrieving error +messages works the same whether you are validating ``$_POST`` data or +another array of your choice. + +.. important:: You have to call the ``set_data()`` method *before* defining + any validation rules. .. important:: If you want to validate more than one array during a single execution, then you should call the ``reset_validation()`` method @@ -946,6 +953,7 @@ Rule Parameter Description ========================= ========== ============================================================================================= ======================= **required** No Returns FALSE if the form element is empty. **matches** Yes Returns FALSE if the form element does not match the one in the parameter. matches[form_item] +**regex_match** Yes Returns FALSE if the form element does not match the regular expression. regex_match[/regex/] **differs** Yes Returns FALSE if the form element does not differ from the one in the parameter. differs[form_item] **is_unique** Yes Returns FALSE if the form element is not unique to the table and field name in the is_unique[table.field] parameter. Note: This rule requires :doc:`Query Builder <../database/query_builder>` to be @@ -1132,4 +1140,4 @@ the following functions: - :php:func:`set_radio()` Note that these are procedural functions, so they **do not** require you -to prepend them with ``$this->form_validation``.
\ No newline at end of file +to prepend them with ``$this->form_validation``. diff --git a/user_guide_src/source/libraries/image_lib.rst b/user_guide_src/source/libraries/image_lib.rst index ae2c8478e..40a280b5c 100644 --- a/user_guide_src/source/libraries/image_lib.rst +++ b/user_guide_src/source/libraries/image_lib.rst @@ -89,7 +89,7 @@ If they fail you can retrieve the error message using this function:: echo $this->image_lib->display_errors(); -A good practice is use the processing function conditionally, showing an +A good practice is to use the processing function conditionally, showing an error upon failure, like this:: if ( ! $this->image_lib->resize()) @@ -187,7 +187,7 @@ Two Types of Watermarking There are two types of watermarking that you can use: -- **Text**: The watermark message will be generating using text, either +- **Text**: The watermark message will be generated using text, either with a True Type font that you specify, or using the native text output that the GD library supports. If you use the True Type version your GD installation must be compiled with True Type support (most @@ -231,7 +231,7 @@ bottom/center of the image, 20 pixels from the bottom of the image. Watermarking Preferences ======================== -This table shown the preferences that are available for both types of +This table shows the preferences that are available for both types of watermarking (text or overlay) ======================= =================== ======================= ========================================================================== @@ -264,7 +264,7 @@ Preference Default Value Options Description Text Preferences ---------------- -This table shown the preferences that are available for the text type of +This table shows the preferences that are available for the text type of watermarking. ======================= =================== =================== ========================================================================== @@ -282,14 +282,14 @@ Preference Default Value Options Description **wm_shadow_color** None None The color of the drop shadow, specified in hex. If you leave this blank a drop shadow will not be used. Both the full 6-length (ie, 993300) and the short three character abbreviated version (ie, fff) are supported. -**wm_shadow_distance** 3 None The distance (in pixels) from the font that the drop shadow should +**wm_shadow_distance** 2 None The distance (in pixels) from the font that the drop shadow should appear. ======================= =================== =================== ========================================================================== Overlay Preferences ------------------- -This table shown the preferences that are available for the overlay type +This table shows the preferences that are available for the overlay type of watermarking. ======================= =================== =================== ========================================================================== 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/javascript.rst b/user_guide_src/source/libraries/javascript.rst index 7f83b2f70..e91b9ad78 100644 --- a/user_guide_src/source/libraries/javascript.rst +++ b/user_guide_src/source/libraries/javascript.rst @@ -135,7 +135,7 @@ In the above example: keydown, keyup, load, mousedown, mouseup, mouseover, mouseup, resize, scroll, or unload. - "element_path" is any valid `jQuery selector - <http://docs.jquery.com/Selectors>`_. Due to jQuery's unique + <http://api.jquery.com/category/selectors/>`_. Due to jQuery's unique selector syntax, this is usually an element id, or CSS selector. For example "#notice_area" would effect ``<div id="notice_area">``, and "#content a.notice" would effect all anchors with a class of "notice" @@ -147,7 +147,7 @@ Effects ======= The query library supports a powerful -`Effects <http://docs.jquery.com/Effects>`_ repertoire. Before an effect +`Effects <http://api.jquery.com/category/effects/>`_ repertoire. Before an effect can be used, it must be loaded:: $this->jquery->effect([optional path] plugin name); // for example $this->jquery->effect('bounce'); @@ -201,7 +201,7 @@ animate() other additional information. For a full summary, see -`http://docs.jquery.com/Effects/animate <http://docs.jquery.com/Effects/animate>`_ +`http://api.jquery.com/animate/ <http://api.jquery.com/animate/>`_ Here is an example of an animate() called on a div with an id of "note", and triggered by a click using the jQuery library's click() event. @@ -288,7 +288,7 @@ corner() -------- Used to add distinct corners to page elements. For full details see -`http://www.malsup.com/jquery/corner/ <http://www.malsup.com/jquery/corner/>`_ +`http://malsup.com/jquery/corner/ <http://malsup.com/jquery/corner/>`_ :: diff --git a/user_guide_src/source/libraries/language.rst b/user_guide_src/source/libraries/language.rst index ee1cefcd0..de17c8288 100644 --- a/user_guide_src/source/libraries/language.rst +++ b/user_guide_src/source/libraries/language.rst @@ -19,7 +19,7 @@ your **application/language/** directory, with separate sub-directories for each The CodeIgniter framework comes with a set of language files for the "english" idiom. Additional approved translations for different idioms may be found in the -`CodeIgniter 3 Translations repositories <https://github.com/codeigniter3-translations>`_. +`CodeIgniter 3 Translations repositories <https://github.com/bcit-ci/codeigniter3-translations>`_. Each repository deals with a single idiom. When CodeIgniter loads language files, it will load the one in **system/language/** diff --git a/user_guide_src/source/libraries/loader.rst b/user_guide_src/source/libraries/loader.rst index efa9d519b..228d5e478 100644 --- a/user_guide_src/source/libraries/loader.rst +++ b/user_guide_src/source/libraries/loader.rst @@ -238,7 +238,7 @@ Class Reference The second **optional** parameter can take an associative array or an object as input, which it runs through the PHP - `extract() <http://www.php.net/extract>`_ function to convert to variables + `extract() <http://php.net/extract>`_ function to convert to variables that can be used in your view files. Again, read the :doc:`Views <../general/views>` page to learn how this might be useful. @@ -259,7 +259,7 @@ Class Reference :rtype: CI_Loader This method takes an associative array as input and generates - variables using the PHP `extract() <http://www.php.net/extract>`_ + variables using the PHP `extract() <http://php.net/extract>`_ function. This method produces the same result as using the second parameter of the ``$this->load->view()`` method above. The reason you might want to use this method independently is if you would like to diff --git a/user_guide_src/source/libraries/migration.rst b/user_guide_src/source/libraries/migration.rst index 25be0c93c..97c72303c 100644 --- a/user_guide_src/source/libraries/migration.rst +++ b/user_guide_src/source/libraries/migration.rst @@ -88,7 +88,7 @@ as *20121031100537_add_blog.php*. } } -Then in **application/config/migration.php** set ``$config['migration_version'] = 1;``. +Then in **application/config/migration.php** set ``$config['migration_version'] = 20121031100537;``. ************* Usage Example @@ -164,7 +164,7 @@ Class Reference .. php:method:: latest() - :returns: TRUE if no migrations are found, current version string on success, FALSE on failure + :returns: Current version string on success, FALSE on failure :rtype: mixed This works much the same way as ``current()`` but instead of looking for @@ -181,4 +181,4 @@ Class Reference specific versions. It works just like ``current()`` but ignores ``$config['migration_version']``. :: - $this->migration->version(5);
\ No newline at end of file + $this->migration->version(5); diff --git a/user_guide_src/source/libraries/output.rst b/user_guide_src/source/libraries/output.rst index 4b36d2a03..84529f766 100644 --- a/user_guide_src/source/libraries/output.rst +++ b/user_guide_src/source/libraries/output.rst @@ -160,7 +160,7 @@ Class Reference Permits you to manually set a server status header. Example:: - $this->output->set_status_header('401'); + $this->output->set_status_header(401); // Sets the header as: Unauthorized `See here <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html>`_ for a full list of headers. @@ -230,4 +230,4 @@ Class Reference ->_display(); exit; - .. note:: Calling this method manually without aborting script execution will result in duplicated output.
\ No newline at end of file + .. note:: Calling this method manually without aborting script execution will result in duplicated output. diff --git a/user_guide_src/source/libraries/security.rst b/user_guide_src/source/libraries/security.rst index 27e6e561b..f7604ef00 100644 --- a/user_guide_src/source/libraries/security.rst +++ b/user_guide_src/source/libraries/security.rst @@ -16,34 +16,16 @@ application, processing input data for security. XSS Filtering ************* -CodeIgniter comes with a Cross Site Scripting Hack prevention filter -which can either run automatically to filter all POST and COOKIE data -that is encountered, or you can run it on a per item basis. By default -it does **not** run globally since it requires a bit of processing -overhead, and since you may not need it in all cases. - -The XSS filter looks for commonly used techniques to trigger Javascript -or other types of code that attempt to hijack cookies or do other -malicious things. If anything disallowed is encountered it is rendered -safe by converting the data to character entities. - -Note: This function should only be used to deal with data upon -submission. It's not something that should be used for general runtime -processing since it requires a fair amount of processing overhead. +CodeIgniter comes with a Cross Site Scripting prevention filter, which +looks for commonly used techniques to trigger JavaScript or other types +of code that attempt to hijack cookies or do other malicious things. +If anything disallowed is encountered it is rendered safe by converting +the data to character entities. To filter data through the XSS filter use the ``xss_clean()`` method:: $data = $this->security->xss_clean($data); -If you want the filter to run automatically every time it encounters -POST or COOKIE data you can enable it by opening your -application/config/config.php file and setting this:: - - $config['global_xss_filtering'] = TRUE; - -.. note:: If you use the form validation class, it gives you the option of - XSS filtering as well. - An optional second parameter, *is_image*, allows this function to be used to test images for potential XSS attacks, useful for file upload security. When this second parameter is set to TRUE, instead of @@ -91,7 +73,7 @@ may alter this behavior by editing the following config parameter :: - $config['csrf_regeneration'] = TRUE; + $config['csrf_regenerate'] = TRUE; Select URIs can be whitelisted from csrf protection (for example API endpoints expecting externally POSTed content). You can add these URIs diff --git a/user_guide_src/source/libraries/sessions.rst b/user_guide_src/source/libraries/sessions.rst index e2780683f..9c9761bbf 100644 --- a/user_guide_src/source/libraries/sessions.rst +++ b/user_guide_src/source/libraries/sessions.rst @@ -363,7 +363,7 @@ To read a tempdata variable, again you can just access it through the .. important:: The ``userdata()`` method will NOT return tempdata items. -Or if you want to be sure that you're reading "flashdata" (and not any +Or if you want to be sure that you're reading "tempdata" (and not any other kind), you can also use the ``tempdata()`` method:: $this->session->tempdata('item'); @@ -433,20 +433,22 @@ all of the options and their effects. You'll find the following Session related preferences in your **application/config/config.php** file: -======================== =============== ======================================== ============================================================================================ -Preference Default Options Description -======================== =============== ======================================== ============================================================================================ -**sess_driver** files files/database/redis/memcached/*custom* The session storage driver to use. -**sess_cookie_name** ci_session [A-Za-z\_-] characters only The name used for the session cookie. -**sess_expiration** 7200 (2 hours) Time in seconds (integer) The number of seconds you would like the session to last. - If you would like a non-expiring session (until browser is closed) set the value to zero: 0 -**sess_save_path** NULL None Specifies the storage location, depends on the driver being used. -**sess_time_to_update** 300 Time in seconds (integer) This option controls how often the session class will regenerate itself and create a new - session ID. Setting it to 0 will disable session ID regeneration. -**sess_match_ip** FALSE TRUE/FALSE (boolean) Whether to validate the user's IP address when reading the session cookie. - Note that some ISPs dynamically changes the IP, so if you want a non-expiring session you - will likely set this to FALSE. -======================== =============== ======================================== ============================================================================================ +============================ =============== ======================================== ============================================================================================ +Preference Default Options Description +============================ =============== ======================================== ============================================================================================ +**sess_driver** files files/database/redis/memcached/*custom* The session storage driver to use. +**sess_cookie_name** ci_session [A-Za-z\_-] characters only The name used for the session cookie. +**sess_expiration** 7200 (2 hours) Time in seconds (integer) The number of seconds you would like the session to last. + If you would like a non-expiring session (until browser is closed) set the value to zero: 0 +**sess_save_path** NULL None Specifies the storage location, depends on the driver being used. +**sess_match_ip** FALSE TRUE/FALSE (boolean) Whether to validate the user's IP address when reading the session cookie. + Note that some ISPs dynamically changes the IP, so if you want a non-expiring session you + will likely set this to FALSE. +**sess_time_to_update** 300 Time in seconds (integer) This option controls how often the session class will regenerate itself and create a new + session ID. Setting it to 0 will disable session ID regeneration. +**sess_regenerate_destroy** FALSE TRUE/FALSE (boolean) Whether to destroy session data associated with the old session ID when auto-regenerating + the session ID. When set to FALSE, the data will be later deleted by the garbage collector. +============================ =============== ======================================== ============================================================================================ .. note:: As a last resort, the Session library will try to fetch PHP's session related INI settings, as well as legacy CI settings such as @@ -567,9 +569,10 @@ However, there are some conditions that must be met: - Only your **default** database connection (or the one that you access as ``$this->db`` from your controllers) can be used. - - You can NOT use a persistent connection. - You must have the :doc:`Query Builder </database/query_builder>` enabled. + - You can NOT use a persistent connection. + - You can NOT use a connection with the *cache_on* setting enabled. In order to use the 'database' session driver, you must also create this table that we already mentioned and then set it as your @@ -594,8 +597,7 @@ For MySQL:: `id` varchar(40) NOT NULL, `ip_address` varchar(45) NOT NULL, `timestamp` int(10) unsigned DEFAULT 0 NOT NULL, - `data` blob DEFAULT '' NOT NULL, - PRIMARY KEY (id), + `data` blob NOT NULL, KEY `ci_sessions_timestamp` (`timestamp`) ); @@ -605,17 +607,23 @@ For PostgreSQL:: "id" varchar(40) NOT NULL, "ip_address" varchar(45) NOT NULL, "timestamp" bigint DEFAULT 0 NOT NULL, - "data" text DEFAULT '' NOT NULL, - PRIMARY KEY ("id") + "data" text DEFAULT '' NOT NULL ); CREATE INDEX "ci_sessions_timestamp" ON "ci_sessions" ("timestamp"); -However, if you want to turn on the *sess_match_ip* setting, you should -also do the following, after creating the table:: +You will also need to add a PRIMARY KEY **depending on your 'sess_match_ip' +setting**. The examples below work both on MySQL and PostgreSQL:: + + // When sess_match_ip = TRUE + ALTER TABLE ci_sessions ADD PRIMARY KEY (id, ip_address); + + // When sess_match_ip = FALSE + ALTER TABLE ci_sessions ADD PRIMARY KEY (id); + + // To drop a previously created primary key (use when changing the setting) + ALTER TABLE ci_sessions DROP PRIMARY KEY; - // Works both on MySQL and PostgreSQL - ALTER TABLE ci_sessions ADD CONSTRAINT ci_sessions_id_ip UNIQUE (id, ip_address); .. important:: Only MySQL and PostgreSQL databases are officially supported, due to lack of advisory locking mechanisms on other @@ -628,12 +636,16 @@ also do the following, after creating the table:: 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 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 'redis' session driver. The downside is that it is not as ubiquitous as relational databases and -requires the `phpredis <https://github.com/nicolasff/phpredis>`_ PHP +requires the `phpredis <https://github.com/phpredis/phpredis>`_ PHP extension to be installed on your system, and that one doesn't come bundled with PHP. Chances are, you're only be using the 'redis' driver only if you're already @@ -661,6 +673,10 @@ sufficient:: 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 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 <http://php.net/memcached>`_ extension is distributed via PECL and some @@ -826,7 +842,7 @@ Class Reference .. note:: This method is DEPRECATED. Use ``userdata()`` with no parameters instead. - .. php:method:: &get_usedata() + .. php:method:: &get_userdata() :returns: A reference to ``$_SESSION`` :rtype: array @@ -1043,4 +1059,4 @@ Class Reference $this->session->foo = 'bar'; // Results in: - // $_SESSION['foo'] = 'bar';
\ No newline at end of file + // $_SESSION['foo'] = 'bar'; diff --git a/user_guide_src/source/libraries/unit_testing.rst b/user_guide_src/source/libraries/unit_testing.rst index 026781cb7..57934cba3 100644 --- a/user_guide_src/source/libraries/unit_testing.rst +++ b/user_guide_src/source/libraries/unit_testing.rst @@ -76,6 +76,7 @@ result. Here is a list of allowed comparison types: - is_double - is_array - is_null +- is_resource Generating Reports ================== diff --git a/user_guide_src/source/libraries/uri.rst b/user_guide_src/source/libraries/uri.rst index ae56184cc..4d38c1d22 100644 --- a/user_guide_src/source/libraries/uri.rst +++ b/user_guide_src/source/libraries/uri.rst @@ -110,7 +110,7 @@ Class Reference :returns: Associative URI segments array :rtype: array - This method lets you turn URI segments into and associative array of + This method lets you turn URI segments into an associative array of key/value pairs. Consider this URI:: index.php/user/search/name/joe/location/UK/gender/male @@ -230,4 +230,4 @@ Class Reference This method is identical to ``segment_array()``, except that it returns the array of segments in your re-routed URI in the event you are using - CodeIgniter's :doc:`URI Routing <../general/routing>` feature.
\ No newline at end of file + CodeIgniter's :doc:`URI Routing <../general/routing>` feature. diff --git a/user_guide_src/source/overview/at_a_glance.rst b/user_guide_src/source/overview/at_a_glance.rst index facbedaee..ce195c211 100644 --- a/user_guide_src/source/overview/at_a_glance.rst +++ b/user_guide_src/source/overview/at_a_glance.rst @@ -16,8 +16,8 @@ for a given task. CodeIgniter is Free =================== -CodeIgniter is licensed under an Apache/BSD-style open source license so -you can use it however you please. For more information please read the +CodeIgniter is licensed under the MIT license so you can use it however +you please. For more information please read the :doc:`license agreement <../license>`. CodeIgniter is Light Weight diff --git a/user_guide_src/source/overview/features.rst b/user_guide_src/source/overview/features.rst index 8c27b1436..f62546b61 100644 --- a/user_guide_src/source/overview/features.rst +++ b/user_guide_src/source/overview/features.rst @@ -8,8 +8,8 @@ how intuitively or intelligently it is designed. Features don't reveal anything about the quality of the code, or the performance, or the attention to detail, or security practices. The only way to really judge an app is to try it and get to know the code. -`Installing <../installation/>`_ CodeIgniter is child's play so we -encourage you to do just that. In the mean time here's a list of +:doc:`Installing <../installation/index>` CodeIgniter is child's play so +we encourage you to do just that. In the mean time here's a list of CodeIgniter's main features. - Model-View-Controller Based System diff --git a/user_guide_src/source/tutorial/create_news_items.rst b/user_guide_src/source/tutorial/create_news_items.rst index 1f4a96dd3..bc0ce7612 100644 --- a/user_guide_src/source/tutorial/create_news_items.rst +++ b/user_guide_src/source/tutorial/create_news_items.rst @@ -18,11 +18,11 @@ application/views/news/create.php. :: - <h2><?php echo $title ?></h2> + <h2><?php echo $title; ?></h2> <?php echo validation_errors(); ?> - <?php echo form_open('news/create') ?> + <?php echo form_open('news/create'); ?> <label for="title">Title</label> <input type="input" name="title" /><br /> @@ -37,16 +37,16 @@ application/views/news/create.php. There are only two things here that probably look unfamiliar to you: the form_open() function and the validation_errors() function. -The first function is provided by the `form -helper <../helpers/form_helper.html>`_ and renders the form element and -adds extra functionality, like adding a hidden `CSRF prevention -field <../libraries/security.html>`_. The latter is used to report +The first function is provided by the :doc:`form +helper <../helpers/form_helper>` and renders the form element and +adds extra functionality, like adding a hidden :doc:`CSRF prevention +field <../libraries/security>`. The latter is used to report errors related to form validation. Go back to your news controller. You're going to do two things here, check whether the form was submitted and whether the submitted data -passed the validation rules. You'll use the `form -validation <../libraries/form_validation.html>`_ library to do this. +passed the validation rules. You'll use the :doc:`form +validation <../libraries/form_validation>` library to do this. :: @@ -58,7 +58,7 @@ validation <../libraries/form_validation.html>`_ library to do this. $data['title'] = 'Create a news item'; $this->form_validation->set_rules('title', 'Title', 'required'); - $this->form_validation->set_rules('text', 'text', 'required'); + $this->form_validation->set_rules('text', 'Text', 'required'); if ($this->form_validation->run() === FALSE) { @@ -81,14 +81,14 @@ the name of the input field, the name to be used in error messages, and the rule. In this case the title and text fields are required. CodeIgniter has a powerful form validation library as demonstrated -above. You can read `more about this library -here <../libraries/form_validation.html>`_. +above. You can read :doc:`more about this library +here <../libraries/form_validation>`. Continuing down, you can see a condition that checks whether the form validation ran successfully. If it did not, the form is displayed, if it was submitted **and** passed all the rules, the model is called. After this, a view is loaded to display a success message. Create a view at -application/view/news/success.php and write a success message. +application/views/news/success.php and write a success message. Model ----- @@ -117,7 +117,7 @@ the model created earlier and add the following: This new method takes care of inserting the news item into the database. The third line contains a new function, url\_title(). This function - -provided by the `URL helper <../helpers/url_helper.html>`_ - strips down +provided by the :doc:`URL helper <../helpers/url_helper>` - strips down the string you pass it, replacing all spaces by dashes (-) and makes sure everything is in lowercase characters. This leaves you with a nice slug, perfect for creating URIs. @@ -125,8 +125,8 @@ slug, perfect for creating URIs. Let's continue with preparing the record that is going to be inserted later, inside the $data array. Each element corresponds with a column in the database table created earlier. You might notice a new method here, -namely the post() method from the `input -library <../libraries/input.html>`_. This method makes sure the data is +namely the post() method from the :doc:`input +library <../libraries/input>`. This method makes sure the data is sanitized, protecting you from nasty attacks from others. The input library is loaded by default. At last, you insert our $data array into our database. diff --git a/user_guide_src/source/tutorial/index.rst b/user_guide_src/source/tutorial/index.rst index b1ab331d1..91f99c7cd 100644 --- a/user_guide_src/source/tutorial/index.rst +++ b/user_guide_src/source/tutorial/index.rst @@ -24,13 +24,13 @@ through the following pages: - Introduction, this page, which gives you an overview of what to expect. -- `Static pages <static_pages.html>`_, which will teach you the basics +- :doc:`Static pages <static_pages>`, which will teach you the basics of controllers, views and routing. -- `News section <news_section.html>`_, where you'll start using models +- :doc:`News section <news_section>`, where you'll start using models and will be doing some basic database operations. -- `Create news items <create_news_items.html>`_, which will introduce +- :doc:`Create news items <create_news_items>`, which will introduce more advanced database operations and form validation. -- `Conclusion <conclusion.html>`_, which will give you some pointers on +- :doc:`Conclusion <conclusion>`, which will give you some pointers on further reading and other resources. Enjoy your exploration of the CodeIgniter framework. diff --git a/user_guide_src/source/tutorial/news_section.rst b/user_guide_src/source/tutorial/news_section.rst index 80938de32..286d620dc 100644 --- a/user_guide_src/source/tutorial/news_section.rst +++ b/user_guide_src/source/tutorial/news_section.rst @@ -15,10 +15,9 @@ should be placed in a model, so they can easily be reused later. Models are the place where you retrieve, insert, and update information in your database or other data stores. They represent your data. -Open up the application/models directory and create a new file called -News_model.php and add the following code. Make sure you've configured -your database properly as described -`here <../database/configuration.html>`_. +Open up the *application/models/* directory and create a new file called +*News_model.php* and add the following code. Make sure you've configured +your database properly as described :doc:`here <../database/configuration>`. :: @@ -37,8 +36,8 @@ library. This will make the database class available through the ``$this->db`` object. Before querying the database, a database schema has to be created. -Connect to your database and run the SQL command below. Also add some -seed records. +Connect to your database and run the SQL command below (MySQL). +Also add some seed records. :: @@ -53,10 +52,10 @@ seed records. Now that the database and a model have been set up, you'll need a method to get all of our posts from our database. To do this, the database -abstraction layer that is included with CodeIgniter — `Active -Record <../database/query_builder.html>`_ — is used. This makes it -possible to write your 'queries' once and make them work on `all -supported database systems <../general/requirements.html>`_. Add the +abstraction layer that is included with CodeIgniter — +:doc:`Query Builder <../database/query_builder>` — is used. This makes it +possible to write your 'queries' once and make them work on :doc:`all +supported database systems <../general/requirements>`. Add the following code to your model. :: @@ -75,7 +74,7 @@ following code to your model. With this code you can perform two different queries. You can get all news records, or get a news item by its `slug <#>`_. You might have -noticed that the $slug variable wasn't sanitized before running the +noticed that the ``$slug`` variable wasn't sanitized before running the query; :doc:`Query Builder <../database/query_builder>` does this for you. Display the news @@ -83,9 +82,9 @@ Display the news Now that the queries are written, the model should be tied to the views that are going to display the news items to the user. This could be done -in our pages controller created earlier, but for the sake of clarity, a -new "news" controller is defined. Create the new controller at -application/controllers/News.php. +in our ``Pages`` controller created earlier, but for the sake of clarity, +a new ``News`` controller is defined. Create the new controller at +*application/controllers/News.php*. :: @@ -96,6 +95,7 @@ application/controllers/News.php. { parent::__construct(); $this->load->model('news_model'); + $this->load->helper('url_helper'); } public function index() @@ -113,11 +113,13 @@ Looking at the code, you may see some similarity with the files we created earlier. First, the ``__construct()`` method: it calls the constructor of its parent class (``CI_Controller``) and loads the model, so it can be used in all other methods in this controller. +It also loads a collection of :doc:`URL Helper <../helpers/url_helper>` +functions, because we'll use one of them in a view later. -Next, there are two methods to view all news items and one for a -specific news item. You can see that the $slug variable is passed to the -model's method in the second method. The model is using this slug to -identify the news item to be returned. +Next, there are two methods to view all news items and one for a specific +news item. You can see that the ``$slug`` variable is passed to the model's +method in the second method. The model is using this slug to identify the +news item to be returned. Now the data is retrieved by the controller through our model, but nothing is displayed yet. The next thing to do is passing this data to @@ -136,35 +138,35 @@ the views. } The code above gets all news records from the model and assigns it to a -variable. The value for the title is also assigned to the $data['title'] +variable. The value for the title is also assigned to the ``$data['title']`` element and all data is passed to the views. You now need to create a -view to render the news items. Create application/views/news/index.php +view to render the news items. Create *application/views/news/index.php* and add the next piece of code. :: - <h2><?php echo $title ?></h2> + <h2><?php echo $title; ?></h2> <?php foreach ($news as $news_item): ?> - <h3><?php echo $news_item['title'] ?></h3> + <h3><?php echo $news_item['title']; ?></h3> <div class="main"> - <?php echo $news_item['text'] ?> + <?php echo $news_item['text']; ?> </div> - <p><a href="news/<?php echo $news_item['slug'] ?>">View article</a></p> + <p><a href="<?php echo site_url('news/'.$news_item['slug']); ?>">View article</a></p> - <?php endforeach ?> + <?php endforeach; ?> Here, each news item is looped and displayed to the user. You can see we -wrote our template in PHP mixed with HTML. If you prefer to use a -template language, you can use CodeIgniter's `Template -Parser <../libraries/parser>`_ class or a third party parser. +wrote our template in PHP mixed with HTML. If you prefer to use a template +language, you can use CodeIgniter's :doc:`Template +Parser <../libraries/parser>` class or a third party parser. The news overview page is now done, but a page to display individual news items is still absent. The model created earlier is made in such way that it can easily be used for this functionality. You only need to add some code to the controller and create a new view. Go back to the -news controller and update ``view()`` with the following: +``News`` controller and update ``view()`` with the following: :: @@ -198,12 +200,12 @@ The only things left to do is create the corresponding view at Routing ------- -Because of the wildcard routing rule created earlier, you need an -extra route to view the controller that you just made. Modify your -routing file (application/config/routes.php) so it looks as follows. -This makes sure the requests reaches the news controller instead of -going directly to the pages controller. The first line routes URI's with -a slug to the view method in the news controller. +Because of the wildcard routing rule created earlier, you need an extra +route to view the controller that you just made. Modify your routing file +(*application/config/routes.php*) so it looks as follows. +This makes sure the requests reaches the ``News`` controller instead of +going directly to the ``Pages`` controller. The first line routes URI's +with a slug to the ``view()`` method in the ``News`` controller. :: diff --git a/user_guide_src/source/tutorial/static_pages.rst b/user_guide_src/source/tutorial/static_pages.rst index 36bcd2df9..66621471e 100644 --- a/user_guide_src/source/tutorial/static_pages.rst +++ b/user_guide_src/source/tutorial/static_pages.rst @@ -3,7 +3,7 @@ Static pages ############ **Note:** This tutorial assumes you've downloaded CodeIgniter and -`installed the framework <../installation/index.html>`_ in your +:doc:`installed the framework <../installation/index>` in your development environment. The first thing you're going to do is set up a **controller** to handle @@ -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,118 +29,114 @@ 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 -CI_Controller class. This means that the new pages class can access the -methods and variables defined in the CI_Controller class -(system/core/Controller.php). +You have created a class named ``Pages``, with a view method that accepts +one argument named ``$page``. The ``Pages`` class is extending the +``CI_Controller`` class. This means that the new pages class can access the +methods and variables defined in the ``CI_Controller`` class +(*system/core/Controller.php*). The **controller is what will become the center of every request** to your web application. In very technical CodeIgniter discussions, it may be referred to as the *super object*. Like any php class, you refer to -it within your controllers as $this. Referring to $this is how you will -load libraries, views, and generally command the framework. +it within your controllers as ``$this``. Referring to ``$this`` is how +you will load libraries, views, and generally command the framework. Now you've created your first method, it's time to make some basic page templates. We will be creating two "views" (page templates) that act as our page footer and header. -Create the header at application/views/templates/header.php and add the -following code. +Create the header at *application/views/templates/header.php* and add +the following code: :: - <html> - <head> - <title>CodeIgniter Tutorial</title> - </head> - <body> + <html> + <head> + <title>CodeIgniter Tutorial</title> + </head> + <body> - <h1>CodeIgniter Tutorial</h1> + <h1><?php echo $title; ?></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 -output the $title variable, which we'll define later in the controller. -Now create a footer at application/views/templates/footer.php that +output the ``$title`` variable, which we'll define later in the controller. +Now, create a footer at *application/views/templates/footer.php* that includes the following code: :: - <em>© 2014</em> - </body> - <html> + <em>© 2015</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 -those files, type some text − anything you'd like − and save them. If -you like to be particularly un-original, try "Hello World!". +In that directory, create two files named *home.php* and *about.php*. +Within those files, type some text − anything you'd like − and save them. +If you like to be particularly un-original, try "Hello World!". In order to load those pages, you'll have to check whether the requested 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. +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 ------- The controller is now functioning! Point your browser to -[your-site-url]index.php/pages/view to see your page. When you visit -index.php/pages/view/about you'll see the about page, again including +``[your-site-url]index.php/pages/view`` to see your page. When you visit +``index.php/pages/view/about`` you'll see the about page, again including the header and footer. Using custom routing rules, you have the power to map any URI to any @@ -144,13 +144,13 @@ controller and method, and break free from the normal convention: ``http://example.com/[controller-class]/[controller-method]/[arguments]`` Let's do that. Open the routing file located at -application/config/routes.php and add the following two lines. Remove -all other code that sets any element in the $route array. +*application/config/routes.php* and add the following two lines. +Remove 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 @@ -160,11 +160,11 @@ match, and calls the appropriate controller and method, possibly with arguments. More information about routing can be found in the URI Routing -`documentation <../general/routing.html>`_. +:doc:`documentation <../general/routing>`. -Here, the second rule in the $routes array matches **any** request using -the wildcard string (:any). and passes the parameter to the view() -method of the pages class. +Here, the second rule in the ``$routes`` array matches **any** request +using 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! |