diff options
Diffstat (limited to 'system/core')
-rw-r--r-- | system/core/Benchmark.php | 35 | ||||
-rw-r--r-- | system/core/CodeIgniter.php | 106 | ||||
-rw-r--r-- | system/core/Common.php | 138 | ||||
-rw-r--r-- | system/core/Config.php | 104 | ||||
-rw-r--r-- | system/core/Controller.php | 14 | ||||
-rw-r--r-- | system/core/Exceptions.php | 51 | ||||
-rw-r--r-- | system/core/Hooks.php | 71 | ||||
-rw-r--r-- | system/core/Input.php | 429 | ||||
-rw-r--r-- | system/core/Lang.php | 73 | ||||
-rw-r--r-- | system/core/Loader.php | 439 | ||||
-rw-r--r-- | system/core/Log.php | 180 | ||||
-rw-r--r-- | system/core/Model.php | 13 | ||||
-rw-r--r-- | system/core/Output.php | 241 | ||||
-rw-r--r-- | system/core/Router.php | 227 | ||||
-rw-r--r-- | system/core/Security.php | 187 | ||||
-rw-r--r-- | system/core/URI.php | 362 | ||||
-rw-r--r-- | system/core/Utf8.php | 67 |
17 files changed, 1716 insertions, 1021 deletions
diff --git a/system/core/Benchmark.php b/system/core/Benchmark.php index 2fabdf46e..e80ee54dd 100644 --- a/system/core/Benchmark.php +++ b/system/core/Benchmark.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,9 +24,10 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** - * CodeIgniter Benchmark Class + * Benchmark Class * * This class enables you to mark points and calculate the time difference * between them. Memory consumption can also be displayed. @@ -40,21 +41,19 @@ class CI_Benchmark { /** - * List of all benchmark markers and when they were added + * List of all benchmark markers * - * @var array + * @var array */ - public $marker = array(); - - // -------------------------------------------------------------------- + public $marker = array(); /** * Set a benchmark marker * * Multiple calls to this function can be made so that several - * execution points can be timed + * execution points can be timed. * - * @param string $name name of the marker + * @param string $name Marker name * @return void */ public function mark($name) @@ -65,6 +64,8 @@ class CI_Benchmark { // -------------------------------------------------------------------- /** + * Elapsed time + * * Calculates the time difference between two marked points. * * If the first parameter is empty this function instead returns the @@ -72,10 +73,13 @@ class CI_Benchmark { * execution time to be shown in a template. The output class will * swap the real value for this variable. * - * @param string a particular marked point - * @param string a particular marked point - * @param integer the number of decimal places - * @return mixed + * @param string $point1 A particular marked point + * @param string $point2 A particular marked point + * @param int $decimals Number of decimal places + * + * @return string Calculated elapsed time on success, + * an '{elapsed_string}' if $point1 is empty + * or an empty string if $point1 is not found. */ public function elapsed_time($point1 = '', $point2 = '', $decimals = 4) { @@ -102,12 +106,13 @@ class CI_Benchmark { /** * Memory Usage * - * This function returns the {memory_usage} pseudo-variable. + * Simply returns the {memory_usage} marker. + * * This permits it to be put it anywhere in a template * without the memory being calculated until the end. * The output class will swap the real value for this variable. * - * @return string + * @return string '{memory_usage}' */ public function memory_usage() { diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php index 8159b19f5..89081b572 100644 --- a/system/core/CodeIgniter.php +++ b/system/core/CodeIgniter.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,6 +24,7 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * System Initialization File @@ -50,7 +51,7 @@ * Load the global functions * ------------------------------------------------------ */ - require(BASEPATH.'core/Common.php'); + require_once(BASEPATH.'core/Common.php'); /* * ------------------------------------------------------ @@ -130,9 +131,12 @@ $CFG =& load_class('Config', 'core'); // Do we have any manually set config items in the index.php file? - if (isset($assign_to_config)) + if (isset($assign_to_config) && is_array($assign_to_config)) { - $CFG->_assign_to_config($assign_to_config); + foreach ($assign_to_config as $key => $value) + { + $CFG->set_item($key, $value); + } } /* @@ -229,7 +233,6 @@ return CI_Controller::get_instance(); } - if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php')) { require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'; @@ -253,23 +256,22 @@ * Security check * ------------------------------------------------------ * - * None of the functions in the app controller or the + * None of the methods in the app controller or the * loader class can be called via the URI, nor can - * controller functions that begin with an underscore + * controller functions that begin with an underscore. */ $class = $RTR->fetch_class(); $method = $RTR->fetch_method(); - if ( ! class_exists($class) - OR strpos($method, '_') === 0 - OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller'))) - ) + if ( ! class_exists($class) OR $method[0] === '_' OR method_exists('CI_Controller', $method)) { if ( ! empty($RTR->routes['404_override'])) { - $x = explode('/', $RTR->routes['404_override'], 2); - $class = $x[0]; - $method = isset($x[1]) ? $x[1] : 'index'; + if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $class, $method) !== 2) + { + $method = 'index'; + } + if ( ! class_exists($class)) { if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) @@ -286,6 +288,42 @@ } } + if (method_exists($class, '_remap')) + { + $params = array($method, array_slice($URI->rsegments, 2)); + $method = '_remap'; + } + else + { + // WARNING: It appears that there are issues with is_callable() even in PHP 5.2! + // Furthermore, there are bug reports and feature/change requests related to it + // that make it unreliable to use in this context. Please, DO NOT change this + // work-around until a better alternative is available. + if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($class)), TRUE)) + { + if (empty($RTR->routes['404_override'])) + { + show_404($class.'/'.$method); + } + elseif (sscanf($RTR->routes['404_override'], '%[^/]/%s', $class, $method) !== 2) + { + $method = 'index'; + } + + if ( ! class_exists($class)) + { + if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) + { + show_404($class.'/'.$method); + } + + include_once(APPPATH.'controllers/'.$class.'.php'); + } + } + + $params = array_slice($URI->rsegments, 2); + } + /* * ------------------------------------------------------ * Is there a "pre_controller" hook? @@ -315,45 +353,7 @@ * Call the requested method * ------------------------------------------------------ */ - // Is there a "remap" function? If so, we call it instead - if (method_exists($CI, '_remap')) - { - $CI->_remap($method, array_slice($URI->rsegments, 2)); - } - else - { - // is_callable() returns TRUE on some versions of PHP 5 for private and protected - // methods, so we'll use this workaround for consistent behavior - if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI)))) - { - // Check and see if we are using a 404 override and use it. - if ( ! empty($RTR->routes['404_override'])) - { - $x = explode('/', $RTR->routes['404_override'], 2); - $class = $x[0]; - $method = isset($x[1]) ? $x[1] : 'index'; - if ( ! class_exists($class)) - { - if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) - { - show_404($class.'/'.$method); - } - - include_once(APPPATH.'controllers/'.$class.'.php'); - unset($CI); - $CI = new $class(); - } - } - else - { - show_404($class.'/'.$method); - } - } - - // Call the requested method. - // Any URI segments present (besides the class/function) will be passed to the method for convenience - call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2)); - } + call_user_func_array(array(&$CI, $method), $params); // Mark a benchmark end point $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end'); diff --git a/system/core/Common.php b/system/core/Common.php index c309d4192..c7ab387a3 100644 --- a/system/core/Common.php +++ b/system/core/Common.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,6 +24,7 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * Common Functions @@ -150,7 +151,7 @@ if ( ! function_exists('load_class')) if (class_exists($name) === FALSE) { - require($path.$directory.'/'.$class.'.php'); + require_once($path.$directory.'/'.$class.'.php'); } break; @@ -164,7 +165,7 @@ if ( ! function_exists('load_class')) if (class_exists($name) === FALSE) { - require(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'); + require_once(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'); } } @@ -172,7 +173,7 @@ if ( ! function_exists('load_class')) if ($name === FALSE) { // Note: We use exit() rather then show_error() in order to avoid a - // self-referencing loop with the Excptions class + // self-referencing loop with the Exceptions class set_status_header(503); exit('Unable to locate the specified class: '.$class.'.php'); } @@ -330,6 +331,24 @@ if ( ! function_exists('get_mimes')) // ------------------------------------------------------------------------ +if ( ! function_exists('is_https')) +{ + /** + * Is HTTPS? + * + * Determines if the application is accessed via an encrypted + * (HTTPS) connection. + * + * @return bool + */ + function is_https() + { + return ( ! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off'); + } +} + +// ------------------------------------------------------------------------ + if ( ! function_exists('show_error')) { /** @@ -401,7 +420,7 @@ if ( ! function_exists('log_message')) return; } - $_log =& load_class('Log'); + $_log =& load_class('Log', 'core'); $_log->write_log($level, $message, $php_error); } } @@ -488,13 +507,9 @@ if ( ! function_exists('set_status_header')) { header('Status: '.$code.' '.$text, TRUE); } - elseif ($server_protocol === 'HTTP/1.0') - { - header('HTTP/1.0 '.$code.' '.$text, TRUE, $code); - } else { - header('HTTP/1.1 '.$code.' '.$text, TRUE, $code); + header(($server_protocol ? $server_protocol : 'HTTP/1.1').' '.$code.' '.$text, TRUE, $code); } } } @@ -524,17 +539,17 @@ if ( ! function_exists('_exception_handler')) { $_error =& load_class('Exceptions', 'core'); - // Should we display the error? We'll get the current error_reporting + // Should we ignore the error? We'll get the current error_reporting // level and add its bits with the severity bits to find out. - if (($severity & error_reporting()) === $severity) + if (($severity & error_reporting()) !== $severity) { - $_error->show_php_error($severity, $message, $filepath, $line); + return; } - // Should we log the error? No? We're done... - if (config_item('log_threshold') === 0) + // Should we display the error? + if ((bool) ini_get('display_errors') === TRUE) { - return; + $_error->show_php_error($severity, $message, $filepath, $line); } $_error->log_exception($severity, $message, $filepath, $line); @@ -597,5 +612,96 @@ if ( ! function_exists('html_escape')) } } +// ------------------------------------------------------------------------ + +if ( ! function_exists('_stringify_attributes')) +{ + /** + * Stringify attributes for use in HTML tags. + * + * Helper function used to convert a string, array, or object + * of attributes to a string. + * + * @param mixed string, array, object + * @param bool + * @return string + */ + function _stringify_attributes($attributes, $js = FALSE) + { + $atts = NULL; + + if (empty($attributes)) + { + return $atts; + } + + if (is_string($attributes)) + { + return ' '.$attributes; + } + + $attributes = (array) $attributes; + + foreach ($attributes as $key => $val) + { + $atts .= ($js) ? $key.'='.$val.',' : ' '.$key.'="'.$val.'"'; + } + + return rtrim($atts, ','); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('function_usable')) +{ + /** + * Function usable + * + * Executes a function_exists() check, and if the Suhosin PHP + * extension is loaded - checks whether the function that is + * checked might be disabled in there as well. + * + * This is useful as function_exists() will return FALSE for + * functions disabled via the *disable_functions* php.ini + * setting, but not for *suhosin.executor.func.blacklist* and + * *suhosin.executor.disable_eval*. These settings will just + * terminate script execution if a disabled function is executed. + * + * @link http://www.hardened-php.net/suhosin/ + * @param string $function_name Function to check for + * @return bool TRUE if the function exists and is safe to call, + * FALSE otherwise. + */ + function function_usable($function_name) + { + static $_suhosin_func_blacklist; + + if (function_exists($function_name)) + { + 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(); + } + } + + return ! in_array($function_name, $_suhosin_func_blacklist, TRUE); + } + + return FALSE; + } +} + /* End of file Common.php */ /* Location: ./system/core/Common.php */
\ No newline at end of file diff --git a/system/core/Config.php b/system/core/Config.php index 2f6a9e085..38bcd5c8f 100644 --- a/system/core/Config.php +++ b/system/core/Config.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,9 +24,10 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** - * CodeIgniter Config Class + * Config Class * * This class contains functions that enable config files to be managed * @@ -41,29 +42,31 @@ class CI_Config { /** * List of all loaded config values * - * @var array + * @var array */ public $config = array(); /** * List of all loaded config files * - * @var array + * @var array */ public $is_loaded = array(); /** * List of paths to search when trying to load a config file. - * This must be public as it's used by the Loader class. * - * @var array + * @used-by CI_Loader + * @var array */ public $_config_paths = array(APPPATH); /** - * Constructor + * Class constructor * - * Sets the $config data from the primary config.php file as a class variable + * Sets the $config data from the primary config.php file as a class variable. + * + * @return void */ public function __construct() { @@ -75,7 +78,7 @@ class CI_Config { { if (isset($_SERVER['HTTP_HOST'])) { - $base_url = ( ! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') ? 'https' : 'http'; + $base_url = is_https() ? 'https' : 'http'; $base_url .= '://'.$_SERVER['HTTP_HOST'] .str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']); } @@ -93,16 +96,16 @@ class CI_Config { /** * Load Config File * - * @param string the config file name - * @param bool if configuration values should be loaded into their own section - * @param bool true if errors should just return false, false if an error message should be displayed - * @return bool if the file was loaded correctly + * @param string $file Configuration file name + * @param bool $use_sections Whether configuration values should be loaded into their own section + * @param bool $fail_gracefully Whether to just return FALSE or display an error message + * @return bool TRUE if the file was loaded correctly or FALSE on failure */ public function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) { $file = ($file === '') ? 'config' : str_replace('.php', '', $file); $found = $loaded = FALSE; - + $check_locations = defined('ENVIRONMENT') ? array(ENVIRONMENT.'/'.$file, $file) : array($file); @@ -183,9 +186,9 @@ class CI_Config { /** * Fetch a config file item * - * @param string the config item name - * @param string the index name - * @return string + * @param string $item Config item name + * @param string $index Index name + * @return string|bool The configuration item or FALSE on failure */ public function item($item, $index = '') { @@ -200,10 +203,10 @@ class CI_Config { // -------------------------------------------------------------------- /** - * Fetch a config file item - adds slash after item (if item is not empty) + * Fetch a config file item with slash appended (if not empty) * - * @param string the config item name - * @return string + * @param string $item Config item name + * @return string|bool The configuration item or FALSE on failure */ public function slash_item($item) { @@ -223,9 +226,12 @@ class CI_Config { /** * Site URL + * * Returns base_url . index_page [. uri_string] * - * @param mixed the URI string or an array of segments + * @uses CI_Config::_uri_string() + * + * @param string|string[] $uri URI string or an array of segments * @return string */ public function site_url($uri = '') @@ -239,15 +245,18 @@ class CI_Config { if ($this->item('enable_query_strings') === FALSE) { - $suffix = ($this->item('url_suffix') === FALSE) ? '' : $this->item('url_suffix'); + $suffix = isset($this->config['url_suffix']) ? $this->config['url_suffix'] : ''; - if ($suffix !== '' && ($offset = strpos($uri, '?')) !== FALSE) - { - $uri = substr($uri, 0, $offset).$suffix.substr($uri, $offset); - } - else + if ($suffix !== '') { - $uri .= $suffix; + if (($offset = strpos($uri, '?')) !== FALSE) + { + $uri = substr($uri, 0, $offset).$suffix.substr($uri, $offset); + } + else + { + $uri .= $suffix; + } } return $this->slash_item('base_url').$this->slash_item('index_page').$uri; @@ -264,9 +273,12 @@ class CI_Config { /** * Base URL + * * Returns base_url [. uri_string] * - * @param string $uri + * @uses CI_Config::_uri_string() + * + * @param string|string[] $uri URI string or an array of segments * @return string */ public function base_url($uri = '') @@ -277,9 +289,12 @@ class CI_Config { // ------------------------------------------------------------- /** - * Build URI string for use in Config::site_url() and Config::base_url() + * Build URI string + * + * @used-by CI_Config::site_url() + * @used-by CI_Config::base_url() * - * @param mixed $uri + * @param string|string[] $uri URI string or an array of segments * @return string */ protected function _uri_string($uri) @@ -318,8 +333,8 @@ class CI_Config { /** * Set a config file item * - * @param string the config item key - * @param string the config item value + * @param string $item Config item key + * @param string $value Config item value * @return void */ public function set_item($item, $value) @@ -327,29 +342,6 @@ class CI_Config { $this->config[$item] = $value; } - // -------------------------------------------------------------------- - - /** - * Assign to Config - * - * This function is called by the front controller (CodeIgniter.php) - * after the Config class is instantiated. It permits config items - * to be assigned or overriden by variables contained in the index.php file - * - * @param array - * @return void - */ - public function _assign_to_config($items = array()) - { - if (is_array($items)) - { - foreach ($items as $key => $val) - { - $this->set_item($key, $val); - } - } - } - } /* End of file Config.php */ diff --git a/system/core/Controller.php b/system/core/Controller.php index 491414807..ee6fec8d5 100644 --- a/system/core/Controller.php +++ b/system/core/Controller.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,9 +24,10 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** - * CodeIgniter Application Controller Class + * Application Controller Class * * This class object is the super class that every library in * CodeIgniter will be assigned to. @@ -40,14 +41,14 @@ class CI_Controller { /** - * Reference to the global CI instance + * Reference to the CI singleton * * @var object */ private static $instance; /** - * Set up controller properties and methods + * Class constructor * * @return void */ @@ -68,9 +69,12 @@ class CI_Controller { log_message('debug', 'Controller Class Initialized'); } + // -------------------------------------------------------------------- + /** - * Return the CI object + * Get the CI singleton * + * @static * @return object */ public static function &get_instance() diff --git a/system/core/Exceptions.php b/system/core/Exceptions.php index bd9178dbd..ced65ece4 100644 --- a/system/core/Exceptions.php +++ b/system/core/Exceptions.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,6 +24,7 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * Exceptions Class @@ -64,7 +65,7 @@ class CI_Exceptions { ); /** - * Initialize execption class + * Class constructor * * @return void */ @@ -79,12 +80,12 @@ class CI_Exceptions { /** * Exception Logger * - * This function logs PHP generated error messages + * Logs PHP generated error messages * - * @param string the error severity - * @param string the error string - * @param string the error filepath - * @param string the error line number + * @param int $severity Log level + * @param string $message Error message + * @param string $filepath File path + * @param int $line Line number * @return void */ public function log_exception($severity, $message, $filepath, $line) @@ -96,11 +97,13 @@ class CI_Exceptions { // -------------------------------------------------------------------- /** - * 404 Page Not Found Handler + * 404 Error Handler * - * @param string the page - * @param bool log error yes/no - * @return string + * @uses CI_Exceptions::show_error() + * + * @param string $page Page URI + * @param bool $log_error Whether to log the error + * @return void */ public function show_404($page = '', $log_error = TRUE) { @@ -122,15 +125,15 @@ class CI_Exceptions { /** * General Error Page * - * This function takes an error message as input - * (either as a string or an array) and displays - * it using the specified template. + * Takes an error message as input (either as a string or an array) + * and displays it using the specified template. + * + * @param string $heading Page heading + * @param string|string[] $message Error message + * @param string $template Template name + * @param int $status_code (default: 500) * - * @param string the heading - * @param string the message - * @param string the template name - * @param int the status code - * @return string + * @return string Error page output */ public function show_error($heading, $message, $template = 'error_general', $status_code = 500) { @@ -154,11 +157,11 @@ class CI_Exceptions { /** * Native PHP error handler * - * @param string the error severity - * @param string the error string - * @param string the error filepath - * @param string the error line number - * @return string + * @param int $severity Error level + * @param string $message Error message + * @param string $filepath File path + * @param int $line Line number + * @return string Error page output */ public function show_php_error($severity, $message, $filepath, $line) { diff --git a/system/core/Hooks.php b/system/core/Hooks.php index afbf4b453..3c28ec9ba 100644 --- a/system/core/Hooks.php +++ b/system/core/Hooks.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,9 +24,10 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** - * CodeIgniter Hooks Class + * Hooks Class * * Provides a mechanism to extend the base system without hacking. * @@ -41,26 +42,28 @@ class CI_Hooks { /** * Determines whether hooks are enabled * - * @var bool + * @var bool */ - public $enabled = FALSE; + public $enabled = FALSE; /** * List of all hooks set in config/hooks.php * - * @var array + * @var array */ public $hooks = array(); /** + * In progress flag + * * Determines whether hook is in progress, used to prevent infinte loops * - * @var bool + * @var bool */ - public $in_progress = FALSE; + protected $_in_progress = FALSE; /** - * Initialize the Hooks Preferences + * Class constructor * * @return void */ @@ -104,8 +107,10 @@ class CI_Hooks { * * Calls a particular hook. Called by CodeIgniter.php. * - * @param string the hook name - * @return mixed + * @uses CI_Hooks::_run_hook() + * + * @param string $which Hook name + * @return bool TRUE on success or FALSE on failure */ public function call_hook($which = '') { @@ -136,8 +141,8 @@ class CI_Hooks { * * Runs a particular hook * - * @param array the hook details - * @return bool + * @param array $data Hook details + * @return bool TRUE on success or FALSE on failure */ protected function _run_hook($data) { @@ -152,7 +157,7 @@ class CI_Hooks { // If the script being called happens to have the same // hook call within it a loop can happen - if ($this->in_progress === TRUE) + if ($this->_in_progress === TRUE) { return; } @@ -173,44 +178,20 @@ class CI_Hooks { return FALSE; } - // ----------------------------------- - // Set class/function name - // ----------------------------------- - - $class = FALSE; - $function = FALSE; - $params = ''; - - if ( ! empty($data['class'])) - { - $class = $data['class']; - } - - if ( ! empty($data['function'])) - { - $function = $data['function']; - } - - if (isset($data['params'])) - { - $params = $data['params']; - } + // Determine and class and/or function names + $class = empty($data['class']) ? FALSE : $data['class']; + $function = empty($data['function']) ? FALSE : $data['function']; + $params = isset($data['params']) ? $data['params'] : ''; if ($class === FALSE && $function === FALSE) { return FALSE; } - // ----------------------------------- - // Set the in_progress flag - // ----------------------------------- - - $this->in_progress = TRUE; + // Set the _in_progress flag + $this->_in_progress = TRUE; - // ----------------------------------- // Call the requested class and/or function - // ----------------------------------- - if ($class !== FALSE) { if ( ! class_exists($class)) @@ -218,7 +199,7 @@ class CI_Hooks { require($filepath); } - $HOOK = new $class; + $HOOK = new $class(); $HOOK->$function($params); } else @@ -231,7 +212,7 @@ class CI_Hooks { $function($params); } - $this->in_progress = FALSE; + $this->_in_progress = FALSE; return TRUE; } diff --git a/system/core/Input.php b/system/core/Input.php index 162e40c85..a3ad14e24 100644 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,6 +24,7 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * Input Class @@ -41,59 +42,78 @@ class CI_Input { /** * IP address of the current user * - * @var string + * @var string */ - public $ip_address = FALSE; + public $ip_address = FALSE; /** - * user agent (web browser) being used by the current user + * User agent strin * - * @var string + * @var string */ - public $user_agent = FALSE; + public $user_agent = FALSE; /** - * If FALSE, then $_GET will be set to an empty array + * Allow GET array flag * - * @var bool + * If set to FALSE, then $_GET will be set to an empty array. + * + * @var bool */ - protected $_allow_get_array = TRUE; + protected $_allow_get_array = TRUE; /** - * If TRUE, then newlines are standardized + * Standartize new lines flag + * + * If set to TRUE, then newlines are standardized. * - * @var bool + * @var bool */ - protected $_standardize_newlines = TRUE; + protected $_standardize_newlines = TRUE; /** - * Determines whether the XSS filter is always active when GET, POST or COOKIE data is encountered - * Set automatically based on config setting + * Enable XSS flag * - * @var bool + * Determines whether the XSS filter is always active when + * GET, POST or COOKIE data is encountered. + * Set automatically based on config setting. + * + * @var bool */ - protected $_enable_xss = FALSE; + protected $_enable_xss = FALSE; /** + * Enable CSRF flag + * * Enables a CSRF cookie token to be set. - * Set automatically based on config setting + * Set automatically based on config setting. * - * @var bool + * @var bool */ - protected $_enable_csrf = FALSE; + protected $_enable_csrf = FALSE; /** * List of all HTTP request headers * * @var array */ - protected $headers = array(); + protected $headers = array(); /** - * Constructor + * Input stream data * - * Sets whether to globally enable the XSS processing - * and whether to allow the $_GET array + * Parsed from php://input at runtime + * + * @see CI_Input::input_stream() + * @var array + */ + protected $_input_stream = NULL; + + /** + * Class constructor + * + * Determines whether to globally enable the XSS processing + * and whether to allow the $_GET array. * * @return void */ @@ -124,12 +144,12 @@ class CI_Input { /** * Fetch from array * - * This is a helper function to retrieve values from global arrays + * Internal method used to retrieve values from global arrays. * - * @param array - * @param string - * @param bool - * @return string + * @param array &$array $_GET, $_POST, $_COOKIE, $_SERVER, etc. + * @param string $index Index for item to be fetched from $array + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed */ protected function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE) { @@ -151,15 +171,20 @@ class CI_Input { /** * Fetch an item from the GET array * - * @param string - * @param bool - * @return string + * @param string $index Index for item to be fetched from $_GET + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed */ public function get($index = NULL, $xss_clean = FALSE) { // Check if a field has been provided - if ($index === NULL && ! empty($_GET)) + if ($index === NULL) { + if (empty($_GET)) + { + return array(); + } + $get = array(); // loop through the full _GET array @@ -178,15 +203,20 @@ class CI_Input { /** * Fetch an item from the POST array * - * @param string - * @param bool - * @return string + * @param string $index Index for item to be fetched from $_POST + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed */ public function post($index = NULL, $xss_clean = FALSE) { // Check if a field has been provided - if ($index === NULL && ! empty($_POST)) + if ($index === NULL) { + if (empty($_POST)) + { + return array(); + } + $post = array(); // Loop through the full _POST array and return it @@ -200,15 +230,14 @@ class CI_Input { return $this->_fetch_from_array($_POST, $index, $xss_clean); } - // -------------------------------------------------------------------- /** - * Fetch an item from either the GET array or the POST + * Fetch an item from POST data with fallback to GET * - * @param string The index key - * @param bool XSS cleaning - * @return string + * @param string $index Index for item to be fetched from $_POST or $_GET + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed */ public function get_post($index = '', $xss_clean = FALSE) { @@ -222,31 +251,76 @@ class CI_Input { /** * Fetch an item from the COOKIE array * - * @param string - * @param bool - * @return string + * @param string $index Index for item to be fetched from $_COOKIE + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed */ public function cookie($index = '', $xss_clean = FALSE) { return $this->_fetch_from_array($_COOKIE, $index, $xss_clean); } + // -------------------------------------------------------------------- + + /** + * Fetch an item from the SERVER array + * + * @param string $index Index for item to be fetched from $_SERVER + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + public function server($index = '', $xss_clean = FALSE) + { + return $this->_fetch_from_array($_SERVER, $index, $xss_clean); + } + + // ------------------------------------------------------------------------ + + /** + * Fetch an item from the php://input stream + * + * Useful when you need to access PUT, DELETE or PATCH request data. + * + * @param string $index Index for item to be fetched + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + public function input_stream($index = '', $xss_clean = FALSE) + { + // The input stream can only be read once, so we'll need to check + // if we have already done that first. + if (is_array($this->_input_stream)) + { + return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean); + } + + // Parse the input stream in our cache var + parse_str(file_get_contents('php://input'), $this->_input_stream); + if ( ! is_array($this->_input_stream)) + { + $this->_input_stream = array(); + return NULL; + } + + return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean); + } + // ------------------------------------------------------------------------ /** * Set cookie * - * Accepts seven parameters, or you can submit an associative + * Accepts an arbitrary number of parameters (up to 7) or an associative * array in the first parameter containing all the values. * - * @param mixed - * @param string the value of the cookie - * @param string the number of seconds until expiration - * @param string the cookie domain. Usually: .yourdomain.com - * @param string the cookie path - * @param string the cookie prefix - * @param bool true makes the cookie secure - * @param bool true makes the cookie accessible via http(s) only (no javascript) + * @param string|mixed[] $name Cookie name or an array containing parameters + * @param string $value Cookie value + * @param int $expire Cookie expiration time in seconds + * @param string $domain Cookie domain (e.g.: '.yourdomain.com') + * @param string $path Cookie path (default: '/') + * @param string $prefix Cookie name prefix + * @param bool $secure Whether to only transfer cookies via SSL + * @param bool $httponly Whether to only makes the cookie accessible via HTTP (no javascript) * @return void */ public function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE, $httponly = FALSE) @@ -303,23 +377,11 @@ class CI_Input { // -------------------------------------------------------------------- /** - * Fetch an item from the SERVER array - * - * @param string - * @param bool - * @return string - */ - public function server($index = '', $xss_clean = FALSE) - { - return $this->_fetch_from_array($_SERVER, $index, $xss_clean); - } - - // -------------------------------------------------------------------- - - /** * Fetch the IP Address * - * @return string + * Determines and validates the visitor's IP address. + * + * @return string IP address */ public function ip_address() { @@ -328,39 +390,117 @@ class CI_Input { return $this->ip_address; } - if (config_item('proxy_ips') != '' && $this->server('HTTP_X_FORWARDED_FOR') && $this->server('REMOTE_ADDR')) - { - $proxies = preg_split('/[\s,]/', config_item('proxy_ips'), -1, PREG_SPLIT_NO_EMPTY); - $proxies = is_array($proxies) ? $proxies : array($proxies); - - $this->ip_address = in_array($_SERVER['REMOTE_ADDR'], $proxies) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']; - } - elseif ( ! $this->server('HTTP_CLIENT_IP') && $this->server('REMOTE_ADDR')) - { - $this->ip_address = $_SERVER['REMOTE_ADDR']; - } - elseif ($this->server('REMOTE_ADDR') && $this->server('HTTP_CLIENT_IP')) - { - $this->ip_address = $_SERVER['HTTP_CLIENT_IP']; - } - elseif ($this->server('HTTP_CLIENT_IP')) - { - $this->ip_address = $_SERVER['HTTP_CLIENT_IP']; - } - elseif ($this->server('HTTP_X_FORWARDED_FOR')) + $proxy_ips = config_item('proxy_ips'); + if ( ! empty($proxy_ips) && ! is_array($proxy_ips)) { - $this->ip_address = $_SERVER['HTTP_X_FORWARDED_FOR']; + $proxy_ips = explode(',', str_replace(' ', '', $proxy_ips)); } - if ($this->ip_address === FALSE) - { - return $this->ip_address = '0.0.0.0'; - } + $this->ip_address = $this->server('REMOTE_ADDR'); - if (strpos($this->ip_address, ',') !== FALSE) + if ($proxy_ips) { - $x = explode(',', $this->ip_address); - $this->ip_address = trim(end($x)); + foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header) + { + if (($spoof = $this->server($header)) !== NULL) + { + // Some proxies typically list the whole chain of IP + // addresses through which the client has reached us. + // e.g. client_ip, proxy_ip1, proxy_ip2, etc. + sscanf($spoof, '%[^,]', $spoof); + + if ( ! $this->valid_ip($spoof)) + { + $spoof = NULL; + } + else + { + break; + } + } + } + + if ($spoof) + { + for ($i = 0, $c = count($proxy_ips); $i < $c; $i++) + { + // Check if we have an IP address or a subnet + if (strpos($proxy_ips[$i], '/') === FALSE) + { + // An IP address (and not a subnet) is specified. + // We can compare right away. + if ($proxy_ips[$i] === $this->ip_address) + { + $this->ip_address = $spoof; + break; + } + + continue; + } + + // We have a subnet ... now the heavy lifting begins + isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.'; + + // If the proxy entry doesn't match the IP protocol - skip it + if (strpos($proxy_ips[$i], $separator) === FALSE) + { + continue; + } + + // Convert the REMOTE_ADDR IP address to binary, if needed + if ( ! isset($ip, $sprintf)) + { + if ($separator === ':') + { + // Make sure we're have the "full" IPv6 format + $ip = explode(':', + str_replace('::', + str_repeat(':', 9 - substr_count($this->ip_address, ':')), + $this->ip_address + ) + ); + + for ($i = 0; $i < 8; $i++) + { + $ip[$i] = intval($ip[$i], 16); + } + + $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b'; + } + else + { + $ip = explode('.', $this->ip_address); + $sprintf = '%08b%08b%08b%08b'; + } + + $ip = vsprintf($sprintf, $ip); + } + + // Split the netmask length off the network address + sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen); + + // Again, an IPv6 address is most likely in a compressed form + if ($separator === ':') + { + $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr)); + for ($i = 0; $i < 8; $i++) + { + $netaddr[$i] = intval($netaddr[$i], 16); + } + } + else + { + $netaddr = explode('.', $netaddr); + } + + // Convert to binary and finally compare + if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0) + { + $this->ip_address = $spoof; + break; + } + } + } } if ( ! $this->valid_ip($this->ip_address)) @@ -376,8 +516,8 @@ class CI_Input { /** * Validate IP Address * - * @param string - * @param string 'ipv4' or 'ipv6' + * @param string $ip IP address + * @param string $which IP protocol: 'ipv4' or 'ipv6' * @return bool */ public function valid_ip($ip, $which = '') @@ -401,9 +541,9 @@ class CI_Input { // -------------------------------------------------------------------- /** - * User Agent + * Fetch User Agent string * - * @return string + * @return string|null User Agent string or NULL if it doesn't exist */ public function user_agent() { @@ -412,7 +552,7 @@ class CI_Input { return $this->user_agent; } - return $this->user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : FALSE; + return $this->user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : NULL; } // -------------------------------------------------------------------- @@ -420,11 +560,12 @@ class CI_Input { /** * Sanitize Globals * - * This function does the following: + * Internal method serving for the following purposes: * - * - Unsets $_GET data (if query strings are not enabled) - * - Unsets all globals if register_globals is enabled - * - Standardizes newline characters to \n + * - Unsets $_GET data (if query strings are not enabled) + * - Unsets all globals if register_globals is enabled + * - Cleans POST, COOKIE and SERVER data + * - Standardizes newline characters to PHP_EOL * * @return void */ @@ -452,25 +593,29 @@ class CI_Input { 'IN' ); - // Unset globals for securiy. + // Unset globals for security. // This is effectively the same as register_globals = off - foreach (array($_GET, $_POST, $_COOKIE) as $global) + // PHP 5.4 no longer has the register_globals functionality. + if ( ! is_php('5.4')) { - if (is_array($global)) + foreach (array($_GET, $_POST, $_COOKIE) as $global) { - foreach ($global as $key => $val) + if (is_array($global)) { - if ( ! in_array($key, $protected)) + foreach ($global as $key => $val) { - global $$key; - $$key = NULL; + if ( ! in_array($key, $protected)) + { + global $$key; + $$key = NULL; + } } } - } - elseif ( ! in_array($global, $protected)) - { - global $$global; - $$global = NULL; + elseif ( ! in_array($global, $protected)) + { + global $$global; + $$global = NULL; + } } } @@ -518,7 +663,7 @@ class CI_Input { $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']); // CSRF Protection check - if ($this->_enable_csrf === TRUE) + if ($this->_enable_csrf === TRUE && ! $this->is_cli_request()) { $this->security->csrf_verify(); } @@ -531,10 +676,10 @@ class CI_Input { /** * Clean Input Data * - * This is a helper function. It escapes data and - * standardizes newline characters to \n + * Internal method that aids in escaping data and + * standardizing newline characters to PHP_EOL. * - * @param string + * @param string|string[] $str Input string(s) * @return string */ protected function _clean_input_data($str) @@ -542,9 +687,9 @@ class CI_Input { if (is_array($str)) { $new_array = array(); - foreach ($str as $key => $val) + foreach (array_keys($str) as $key) { - $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); + $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($str[$key]); } return $new_array; } @@ -588,16 +733,16 @@ class CI_Input { /** * Clean Keys * - * This is a helper function. To prevent malicious users + * Internal method that helps to prevent malicious users * from trying to exploit keys we make sure that keys are * only named with alpha-numeric text and a few other items. * - * @param string + * @param string $str Input string * @return string */ protected function _clean_input_keys($str) { - if ( ! preg_match('/^[a-z0-9:_\/-]+$/i', $str)) + if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str)) { set_status_header(503); exit('Disallowed Key Characters.'); @@ -617,15 +762,12 @@ class CI_Input { /** * Request Headers * - * In Apache, you can simply call apache_request_headers(), however for - * people running other webservers the function is undefined. - * - * @param bool XSS cleaning + * @param bool $xss_clean Whether to apply XSS filtering * @return array */ public function request_headers($xss_clean = FALSE) { - // Look at Apache go! + // In Apache, you can simply call apache_request_headers() if (function_exists('apache_request_headers')) { $headers = apache_request_headers(); @@ -636,9 +778,9 @@ class CI_Input { foreach ($_SERVER as $key => $val) { - if (strpos($key, 'HTTP_') === 0) + if (sscanf($key, 'HTTP_%s', $header) === 1) { - $headers[substr($key, 5)] = $this->_fetch_from_array($_SERVER, $key, $xss_clean); + $headers[$header] = $this->_fetch_from_array($_SERVER, $key, $xss_clean); } } } @@ -662,9 +804,9 @@ class CI_Input { * * Returns the value of a single member of the headers class member * - * @param string array key for $this->headers - * @param bool XSS Clean or not - * @return mixed FALSE on failure, string on success + * @param string $index Header name + * @param bool $xss_clean Whether to apply XSS filtering + * @return string|bool The requested header on success or FALSE on failure */ public function get_request_header($index, $xss_clean = FALSE) { @@ -686,9 +828,9 @@ class CI_Input { // -------------------------------------------------------------------- /** - * Is ajax Request? + * Is AJAX request? * - * Test to see if a request contains the HTTP_X_REQUESTED_WITH header + * Test to see if a request contains the HTTP_X_REQUESTED_WITH header. * * @return bool */ @@ -700,9 +842,9 @@ class CI_Input { // -------------------------------------------------------------------- /** - * Is cli Request? + * Is CLI request? * - * Test to see if a request was made from the command line + * Test to see if a request was made from the command line. * * @return bool */ @@ -716,10 +858,11 @@ class CI_Input { /** * Get Request Method * - * Return the Request Method + * Return the request method * - * @param bool uppercase or lowercase - * @return bool + * @param bool $upper Whether to return in upper or lower case + * (default: FALSE) + * @return string */ public function method($upper = FALSE) { diff --git a/system/core/Lang.php b/system/core/Lang.php index 3001f1b13..9e6f43716 100644 --- a/system/core/Lang.php +++ b/system/core/Lang.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,6 +24,7 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * Language Class @@ -39,19 +40,19 @@ class CI_Lang { /** * List of translations * - * @var array + * @var array */ public $language = array(); /** * List of loaded language files * - * @var array + * @var array */ public $is_loaded = array(); /** - * Initialize language class + * Class constructor * * @return void */ @@ -65,12 +66,13 @@ class CI_Lang { /** * Load a language file * - * @param mixed the name of the language file to be loaded - * @param string the language (english, etc.) - * @param bool return loaded array of translations - * @param bool add suffix to $langfile - * @param string alternative path to look for language file - * @return mixed + * @param mixed $langfile Language file name + * @param string $idiom Language name (english, etc.) + * @param bool $return Whether to return the loaded array of translations + * @param bool $add_suffix Whether to add suffix to $langfile + * @param string $alt_path Alternative path to look for the language file + * + * @return void|string[] Array containing translations, if $return is set to TRUE */ public function load($langfile, $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '') { @@ -83,10 +85,10 @@ class CI_Lang { $langfile .= '.php'; - if ($idiom === '') + if (empty($idiom) OR ! ctype_alpha($idiom)) { $config =& get_config(); - $idiom = ( ! empty($config['language'])) ? $config['language'] : 'english'; + $idiom = empty($config['language']) ? 'english' : $config['language']; } if ($return === FALSE && isset($this->is_loaded[$langfile]) && $this->is_loaded[$langfile] === $idiom) @@ -94,31 +96,41 @@ class CI_Lang { return; } - // Determine where the language file is and load it - if ($alt_path !== '' && file_exists($alt_path.'language/'.$idiom.'/'.$langfile)) + // Load the base file, so any others found can override it + $basepath = BASEPATH.'language/'.$idiom.'/'.$langfile; + if (($found = file_exists($basepath)) === TRUE) { - include($alt_path.'language/'.$idiom.'/'.$langfile); + include($basepath); + } + + // Do we have an alternative path to look in? + if ($alt_path !== '') + { + $alt_path .= 'language/'.$idiom.'/'.$langfile; + if (file_exists($alt_path)) + { + include($alt_path); + $found = TRUE; + } } else { - $found = FALSE; - foreach (get_instance()->load->get_package_paths(TRUE) as $package_path) { - if (file_exists($package_path.'language/'.$idiom.'/'.$langfile)) + $package_path .= 'language/'.$idiom.'/'.$langfile; + if ($basepath !== $package_path && file_exists($package_path)) { - include($package_path.'language/'.$idiom.'/'.$langfile); + include($package_path); $found = TRUE; break; } } - - if ($found !== TRUE) - { - show_error('Unable to load the requested language file: language/'.$idiom.'/'.$langfile); - } } + if ($found !== TRUE) + { + show_error('Unable to load the requested language file: language/'.$idiom.'/'.$langfile); + } if ( ! isset($lang) OR ! is_array($lang)) { @@ -146,17 +158,20 @@ class CI_Lang { // -------------------------------------------------------------------- /** - * Fetch a single line of text from the language array + * Language line + * + * Fetches a single line of text from the language array * - * @param string $line the language line - * @return string + * @param string $line Language line key + * @param bool $log_errors Whether to log an error message if the line is not found + * @return string Translation */ - public function line($line = '') + public function line($line = '', $log_errors = TRUE) { $value = ($line === '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line]; // Because killer robots like unicorns! - if ($value === FALSE) + if ($value === FALSE && $log_errors === TRUE) { log_message('error', 'Could not find the language line "'.$line.'"'); } diff --git a/system/core/Loader.php b/system/core/Loader.php index d51ee0b34..651507470 100644 --- a/system/core/Loader.php +++ b/system/core/Loader.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,11 +24,12 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * Loader Class * - * Loads views and files + * Loads framework components. * * @package CodeIgniter * @subpackage Libraries @@ -42,84 +43,84 @@ class CI_Loader { /** * Nesting level of the output buffering mechanism * - * @var int + * @var int */ protected $_ci_ob_level; /** * List of paths to load views from * - * @var array + * @var array */ protected $_ci_view_paths = array(); /** * List of paths to load libraries from * - * @var array + * @var array */ protected $_ci_library_paths = array(); /** * List of paths to load models from * - * @var array + * @var array */ protected $_ci_model_paths = array(); /** * List of paths to load helpers from * - * @var array + * @var array */ protected $_ci_helper_paths = array(); /** * List of loaded base classes * - * @var array + * @var array */ protected $_base_classes = array(); // Set by the controller class /** * List of cached variables * - * @var array + * @var array */ protected $_ci_cached_vars = array(); /** * List of loaded classes * - * @var array + * @var array */ protected $_ci_classes = array(); /** * List of loaded files * - * @var array + * @var array */ protected $_ci_loaded_files = array(); /** * List of loaded models * - * @var array + * @var array */ protected $_ci_models = array(); /** * List of loaded helpers * - * @var array + * @var array */ protected $_ci_helpers = array(); /** * List of class name mappings * - * @var array + * @var array */ protected $_ci_varmap = array( 'unit_test' => 'unit', @@ -127,9 +128,9 @@ class CI_Loader { ); /** - * Constructor + * Class constructor * - * Sets the path to the view files and gets the initial output buffering level + * Sets component load paths, gets the initial output buffering level. * * @return void */ @@ -147,21 +148,18 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Initialize the Loader + * Initializer * - * This method is called once in CI_Controller. - * - * @return object + * @todo Figure out a way to move this to the constructor + * without breaking *package_path*() methods. + * @uses CI_Loader::_ci_autoloader() + * @used-by CI_Controller::__construct() + * @return void */ public function initialize() { - $this->_ci_classes = array(); - $this->_ci_loaded_files = array(); - $this->_ci_models = array(); $this->_base_classes =& is_loaded(); - $this->_ci_autoloader(); - return $this; } // -------------------------------------------------------------------- @@ -169,14 +167,12 @@ class CI_Loader { /** * Is Loaded * - * A utility function to test if a class is in the self::$_ci_classes array. - * This function returns the object name if the class tested for is loaded, - * and returns FALSE if it isn't. + * A utility method to test if a class is in the self::$_ci_classes array. * - * It is mainly used in the form_helper -> _get_validation_object() + * @used-by Mainly used by Form Helper function _get_validation_object(). * - * @param string class being checked for - * @return mixed class object name on the CI SuperObject or FALSE + * @param string $class Class name to check for + * @return string|bool Class object name if loaded or FALSE */ public function is_loaded($class) { @@ -186,14 +182,14 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Class Loader + * Library Loader * - * This function lets users load and instantiate classes. - * It is designed to be called from a user's app controllers. + * Loads and instantiates libraries. + * Designed to be called from application controllers. * - * @param string the name of the class - * @param mixed the optional parameters - * @param string an optional object name + * @param string $library Library name + * @param array $params Optional parameters to pass to the library class constructor + * @param string $object_name An optional object name to assign to * @return void */ public function library($library = '', $params = NULL, $object_name = NULL) @@ -210,7 +206,7 @@ class CI_Loader { if ($library === '' OR isset($this->_base_classes[$library])) { - return FALSE; + return; } if ( ! is_null($params) && ! is_array($params)) @@ -226,26 +222,25 @@ class CI_Loader { /** * Model Loader * - * This function lets users load and instantiate models. + * Loads and instantiates libraries. * - * @param string the name of the class - * @param string name for the model - * @param bool database connection + * @param string $model Model name + * @param string $name An optional object name to assign to + * @param bool $db_conn An optional database connection configuration to initialize * @return void */ public function model($model, $name = '', $db_conn = FALSE) { - if (is_array($model)) + if (empty($model)) { - foreach ($model as $babe) - { - $this->model($babe); - } return; } - - if ($model === '') + elseif (is_array($model)) { + foreach ($model as $class) + { + $this->model($class); + } return; } @@ -318,10 +313,13 @@ class CI_Loader { /** * Database Loader * - * @param string the DB credentials - * @param bool whether to return the DB object - * @param bool whether to enable query builder (this allows us to override the config setting) - * @return object + * @param mixed $params Database configuration options + * @param bool $return Whether to return the database object + * @param bool $query_builder Whether to enable Query Builder + * (overrides the configuration setting) + * + * @return void|object|bool Database object if $return is set to TRUE, + * FALSE on failure, void in any other case */ public function database($params = '', $return = FALSE, $query_builder = NULL) { @@ -329,7 +327,7 @@ class CI_Loader { $CI =& get_instance(); // Do we even need to load the database class? - if (class_exists('CI_DB') && $return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db)) + if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id)) { return FALSE; } @@ -352,28 +350,32 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Load the Utilities Class + * Load the Database Utilities Class * - * @return string + * @param object $db Database object + * @param bool $return Whether to return the DB Forge class object or not + * @return void|object */ - public function dbutil() + public function dbutil($db = NULL, $return = FALSE) { - if ( ! class_exists('CI_DB')) - { - $this->database(); - } - $CI =& get_instance(); - // for backwards compatibility, load dbforge so we can extend dbutils off it - // this use is deprecated and strongly discouraged - $CI->load->dbforge(); + if ( ! is_object($db) OR ! ($db instanceof CI_DB)) + { + class_exists('CI_DB', FALSE) OR $this->database(); + $db =& $CI->db; + } require_once(BASEPATH.'database/DB_utility.php'); - require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_utility.php'); - $class = 'CI_DB_'.$CI->db->dbdriver.'_utility'; + require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php'); + $class = 'CI_DB_'.$db->dbdriver.'_utility'; + + if ($return === TRUE) + { + return new $class($db); + } - $CI->dbutil = new $class(); + $CI->dbutil = new $class($db); } // -------------------------------------------------------------------- @@ -381,40 +383,56 @@ class CI_Loader { /** * Load the Database Forge Class * - * @return string + * @param object $db Database object + * @param bool $return Whether to return the DB Forge class object or not + * @return void|object */ - public function dbforge() + public function dbforge($db = NULL, $return = FALSE) { - if ( ! class_exists('CI_DB')) + $CI =& get_instance(); + if ( ! is_object($db) OR ! ($db instanceof CI_DB)) { - $this->database(); + class_exists('CI_DB', FALSE) OR $this->database(); + $db =& $CI->db; } - $CI =& get_instance(); - require_once(BASEPATH.'database/DB_forge.php'); - require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge.php'); - $class = 'CI_DB_'.$CI->db->dbdriver.'_forge'; + require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php'); + + if ( ! empty($db->subdriver)) + { + $driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php'; + if (file_exists($driver_path)) + { + require_once($driver_path); + $class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge'; + } + } + else + { + $class = 'CI_DB_'.$db->dbdriver.'_forge'; + } + + if ($return === TRUE) + { + return new $class($db); + } - $CI->dbforge = new $class(); + $CI->dbforge = new $class($db); } // -------------------------------------------------------------------- /** - * Load View + * View Loader * - * This function is used to load a "view" file. It has three parameters: + * Loads "view" files. * - * 1. The name of the "view" file to be included. - * 2. An associative array of data to be extracted for use in the view. - * 3. TRUE/FALSE - whether to return the data or load it. In - * some cases it's advantageous to be able to return data so that - * a developer can process it in some way. - * - * @param string - * @param array - * @param bool + * @param string $view View name + * @param array $vars An associative array of data + * to be extracted for use in the view + * @param bool $return Whether to return the view output + * or leave it to the Output class * @return void */ public function view($view, $vars = array(), $return = FALSE) @@ -425,13 +443,11 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Load File - * - * This is a generic file loader + * Generic File Loader * - * @param string - * @param bool - * @return string + * @param string $path File path + * @param bool $return Whether to return the file output + * @return void|string */ public function file($path, $return = FALSE) { @@ -446,8 +462,10 @@ class CI_Loader { * Once variables are set they become available within * the controller class and its "view" files. * - * @param array - * @param string + * @param array|object|string $vars + * An associative array or object containing values + * to be set, or a value's name if string + * @param string $val Value to set, only used if $vars is a string * @return void */ public function vars($vars = array(), $val = '') @@ -475,8 +493,8 @@ class CI_Loader { * * Check if a variable is set and retrieve it. * - * @param array - * @return void + * @param string $key Variable name + * @return mixed The variable or NULL if not found */ public function get_var($key) { @@ -488,7 +506,7 @@ class CI_Loader { /** * Get Variables * - * Retrieve all loaded variables + * Retrieves all loaded variables. * * @return array */ @@ -500,11 +518,9 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Load Helper + * Helper Loader * - * This function loads the specified helper file. - * - * @param mixed + * @param string|string[] $helpers Helper name(s) * @return void */ public function helper($helpers = array()) @@ -516,27 +532,34 @@ class CI_Loader { continue; } - $ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.'.php'; - // Is this a helper extension request? - if (file_exists($ext_helper)) + $ext_helper = config_item('subclass_prefix').$helper; + $ext_loaded = FALSE; + foreach ($this->_ci_helper_paths as $path) { - $base_helper = BASEPATH.'helpers/'.$helper.'.php'; + if (file_exists($path.'helpers/'.$ext_helper.'.php')) + { + include_once($path.'helpers/'.$ext_helper.'.php'); + $ext_loaded = TRUE; + } + } + // If we have loaded extensions - check if the base one is here + if ($ext_loaded === TRUE) + { + $base_helper = BASEPATH.'helpers/'.$helper.'.php'; if ( ! file_exists($base_helper)) { show_error('Unable to load the requested file: helpers/'.$helper.'.php'); } - include_once($ext_helper); include_once($base_helper); - $this->_ci_helpers[$helper] = TRUE; log_message('debug', 'Helper loaded: '.$helper); continue; } - // Try to load the helper + // No extensions found ... try loading regular helpers and/or overrides foreach ($this->_ci_helper_paths as $path) { if (file_exists($path.'helpers/'.$helper.'.php')) @@ -562,10 +585,11 @@ class CI_Loader { /** * Load Helpers * - * This is simply an alias to the above function in case the - * user has written the plural form of this function. + * An alias for the helper() method in case the developer has + * written the plural form of it. * - * @param array + * @uses CI_Loader::helper() + * @param string|string[] $helpers Helper name(s) * @return void */ public function helpers($helpers = array()) @@ -576,22 +600,21 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Loads a language file + * Language Loader * - * @param array - * @param string + * Loads language files. + * + * @param string|string[] $files List of language file names to load + * @param string Language name * @return void */ - public function language($file = array(), $lang = '') + public function language($files = array(), $lang = '') { $CI =& get_instance(); - if ( ! is_array($file)) - { - $file = array($file); - } + is_array($files) OR $files = array($files); - foreach ($file as $langfile) + foreach ($files as $langfile) { $CI->lang->load($langfile, $lang); } @@ -600,30 +623,35 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Loads a config file + * Config Loader * - * @param string - * @param bool - * @param bool - * @return void + * Loads a config file (an alias for CI_Config::load()). + * + * @uses CI_Config::load() + * @param string $file Configuration file name + * @param bool $use_sections Whether configuration values should be loaded into their own section + * @param bool $fail_gracefully Whether to just return FALSE or display an error message + * @return bool TRUE if the file was loaded correctly or FALSE on failure */ public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) { $CI =& get_instance(); - $CI->config->load($file, $use_sections, $fail_gracefully); + return $CI->config->load($file, $use_sections, $fail_gracefully); } // -------------------------------------------------------------------- /** - * Driver + * Driver Loader * - * Loads a driver library + * Loads a driver library. * - * @param mixed the name of the class or array of classes - * @param mixed the optional parameters - * @param string an optional object name - * @return void + * @param string|string[] $library Driver name(s) + * @param array $params Optional parameters to pass to the driver + * @param string $object_name An optional object name to assign to + * + * @return void|object|bool Object or FALSE on failure if $library is a string + * and $object_name is set. void otherwise. */ public function driver($library = '', $params = NULL, $object_name = NULL) { @@ -633,18 +661,18 @@ class CI_Loader { { $this->driver($driver); } - return FALSE; + return; } - if ( ! class_exists('CI_Driver_Library')) + if ($library === '') { - // we aren't instantiating an object here, that'll be done by the Library itself - require BASEPATH.'libraries/Driver.php'; + return FALSE; } - if ($library === '') + if ( ! class_exists('CI_Driver_Library')) { - return FALSE; + // We aren't instantiating an object here, just making the base class available + require BASEPATH.'libraries/Driver.php'; } // We can save the loader some time since Drivers will *always* be in a subfolder, @@ -662,10 +690,16 @@ class CI_Loader { /** * Add Package Path * - * Prepends a parent path to the library, model, helper, and config path arrays + * Prepends a parent path to the library, model, helper and config + * path arrays. * - * @param string - * @param bool + * @see CI_Loader::$_ci_library_paths + * @see CI_Loader::$_ci_model_paths + * @see CI_Loader::$_ci_helper_paths + * @see CI_Config::$_config_paths + * + * @param string $path Path to add + * @param bool $view_cascade (default: TRUE) * @return void */ public function add_package_path($path, $view_cascade = TRUE) @@ -688,14 +722,14 @@ class CI_Loader { /** * Get Package Paths * - * Return a list of all package paths, by default it will ignore BASEPATH. + * Return a list of all package paths. * - * @param string - * @return void + * @param bool $include_base Whether to include BASEPATH (default: TRUE) + * @return array */ public function get_package_paths($include_base = FALSE) { - return $include_base === TRUE ? $this->_ci_library_paths : $this->_ci_model_paths; + return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths; } // -------------------------------------------------------------------- @@ -703,14 +737,14 @@ class CI_Loader { /** * Remove Package Path * - * Remove a path from the library, model, and helper path arrays if it exists - * If no path is provided, the most recently added path is removed. + * Remove a path from the library, model, helper and/or config + * path arrays if it exists. If no path is provided, the most recently + * added path will be removed removed. * - * @param string - * @param bool + * @param string $path Path to remove * @return void */ - public function remove_package_path($path = '', $remove_config_path = TRUE) + public function remove_package_path($path = '') { $config =& $this->_ci_get_component('config'); @@ -755,13 +789,16 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Loader + * Internal CI Data Loader + * + * Used to load views and files. * - * This function is used to load views and files. * Variables are prefixed with _ci_ to avoid symbol collision with - * variables made available to view files + * variables made available to view files. * - * @param array + * @used-by CI_Loader::view() + * @used-by CI_Loader::file() + * @param array $_ci_data Data to load * @return void */ protected function _ci_load($_ci_data) @@ -785,11 +822,11 @@ class CI_Loader { $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION); $_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view; - foreach ($this->_ci_view_paths as $view_file => $cascade) + foreach ($this->_ci_view_paths as $_ci_view_file => $cascade) { - if (file_exists($view_file.$_ci_file)) + if (file_exists($_ci_view_file.$_ci_file)) { - $_ci_path = $view_file.$_ci_file; + $_ci_path = $_ci_view_file.$_ci_file; $file_exists = TRUE; break; } @@ -837,17 +874,19 @@ class CI_Loader { * We buffer the output for two reasons: * 1. Speed. You get a significant speed boost. * 2. So that the final rendered template can be post-processed by - * the output class. Why do we need post processing? For one thing, - * in order to show the elapsed page load time. Unless we can - * intercept the content right before it's sent to the browser and - * then stop the timer it won't be accurate. + * the output class. Why do we need post processing? For one thing, + * in order to show the elapsed page load time. Unless we can + * intercept the content right before it's sent to the browser and + * then stop the timer it won't be accurate. */ ob_start(); // If the PHP installation does not support short tags we'll // do a little string replacement, changing the short tags // to standard PHP echo statements. - if ( ! is_php('5.4') && (bool) @ini_get('short_open_tag') === FALSE && config_item('rewrite_short_tags') === TRUE) + if ( ! is_php('5.4') && (bool) @ini_get('short_open_tag') === FALSE + && config_item('rewrite_short_tags') === TRUE && function_usable('eval') + ) { echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace('<?=', '<?php echo ', file_get_contents($_ci_path)))); } @@ -889,13 +928,14 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Load class + * Internal CI Class Loader * - * This function loads the requested class. + * @used-by CI_Loader::library() + * @uses CI_Loader::_ci_init_class() * - * @param string the item that is being loaded - * @param mixed any additional parameters - * @param string an optional object name + * @param string $class Class name to load + * @param mixed $params Optional parameters to pass to the class constructor + * @param string $object_name Optional object name to assign to * @return void */ protected function _ci_load_class($class, $params = NULL, $object_name = NULL) @@ -996,14 +1036,19 @@ class CI_Loader { $this->_ci_loaded_files[] = $filepath; return $this->_ci_init_class($class, '', $params, $object_name); } - } // END FOREACH // One last attempt. Maybe the library is in a subdirectory, but it wasn't specified? if ($subdir === '') { $path = strtolower($class).'/'.$class; - return $this->_ci_load_class($path, $params); + return $this->_ci_load_class($path, $params, $object_name); + } + elseif (ucfirst($subdir) != $subdir) + { + // Lowercase subdir failed - retry capitalized + $path = ucfirst($subdir).$class; + return $this->_ci_load_class($path, $params, $object_name); } // If we got this far we were unable to find the requested class. @@ -1018,12 +1063,17 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Instantiates a class + * Internal CI Class Instantiator + * + * @used-by CI_Loader::_ci_load_class() * - * @param string - * @param string - * @param bool - * @param string an optional object name + * @param string $class Class name + * @param string $prefix Class name prefix + * @param array|null|bool $config Optional configuration to pass to the class constructor: + * FALSE to skip; + * NULL to search in config paths; + * array containing configuration data + * @param string $object_name Optional object name to assign to * @return void */ protected function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL) @@ -1091,7 +1141,7 @@ class CI_Loader { if ( ! class_exists($name)) { log_message('error', 'Non-existent class: '.$name); - show_error('Non-existent class: '.$class); + show_error('Non-existent class: '.$name); } // Set the variable name we will assign the class to @@ -1125,11 +1175,11 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Autoloader + * CI Autoloader * - * The config/autoload.php file contains an array that permits sub-systems, - * libraries, and helpers to be loaded automatically. + * Loads component listed in the config/autoload.php file. * + * @used-by CI_Loader::initialize() * @return void */ protected function _ci_autoloader() @@ -1193,6 +1243,15 @@ class CI_Loader { } } + // Autoload drivers + if (isset($autoload['drivers'])) + { + foreach ($autoload['drivers'] as $item) + { + $this->driver($item); + } + } + // Autoload models if (isset($autoload['model'])) { @@ -1203,11 +1262,12 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Object to Array + * CI Object to Array translator * - * Takes an object as input and converts the class variables to array key/vals + * Takes an object as input and converts the class variables to + * an associative array with key/value pairs. * - * @param object + * @param object $object Object data to translate * @return array */ protected function _ci_object_to_array($object) @@ -1218,9 +1278,11 @@ class CI_Loader { // -------------------------------------------------------------------- /** - * Get a reference to a specific library or model + * CI Component getter + * + * Get a reference to a specific library or model. * - * @param string + * @param string $component Component name * @return bool */ protected function &_ci_get_component($component) @@ -1234,10 +1296,11 @@ class CI_Loader { /** * Prep filename * - * This function preps the name of various items to make loading them more reliable. + * This function prepares filenames of various items to + * make their loading more reliable. * - * @param mixed - * @param string + * @param string|string[] $filename Filename(s) + * @param string $extension Filename extension * @return array */ protected function _ci_prep_filename($filename, $extension) diff --git a/system/core/Log.php b/system/core/Log.php new file mode 100644 index 000000000..718f50587 --- /dev/null +++ b/system/core/Log.php @@ -0,0 +1,180 @@ +<?php +/** + * CodeIgniter + * + * An open source application development framework for PHP 5.2.4 or newer + * + * NOTICE OF LICENSE + * + * Licensed under the Open Software License version 3.0 + * + * This source file is subject to the Open Software License (OSL 3.0) that is + * bundled with this package in the files license.txt / license.rst. It is + * also available through the world wide web at this URL: + * http://opensource.org/licenses/OSL-3.0 + * If you did not receive a copy of the license and are unable to obtain it + * through the world wide web, please send an email to + * licensing@ellislab.com so we can send you a copy immediately. + * + * @package CodeIgniter + * @author EllisLab Dev Team + * @copyright Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/) + * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * @link http://codeigniter.com + * @since Version 1.0 + * @filesource + */ +defined('BASEPATH') OR exit('No direct script access allowed'); + +/** + * Logging Class + * + * @package CodeIgniter + * @subpackage Libraries + * @category Logging + * @author EllisLab Dev Team + * @link http://codeigniter.com/user_guide/general/errors.html + */ +class CI_Log { + + /** + * Path to save log files + * + * @var string + */ + protected $_log_path; + + /** + * Level of logging + * + * @var int + */ + protected $_threshold = 1; + + /** + * Highest level of logging + * + * @var int + */ + protected $_threshold_max = 0; + + /** + * Array of threshold levels to log + * + * @var array + */ + protected $_threshold_array = array(); + + /** + * Format of timestamp for log files + * + * @var string + */ + protected $_date_fmt = 'Y-m-d H:i:s'; + + /** + * Whether or not the logger can write to the log files + * + * @var bool + */ + protected $_enabled = TRUE; + + /** + * Predefined logging levels + * + * @var array + */ + protected $_levels = array('ERROR' => 1, 'DEBUG' => 2, 'INFO' => 3, 'ALL' => 4); + + /** + * Initialize Logging class + * + * @return void + */ + public function __construct() + { + $config =& get_config(); + + $this->_log_path = ($config['log_path'] !== '') ? $config['log_path'] : APPPATH.'logs/'; + + if ( ! is_dir($this->_log_path) OR ! is_really_writable($this->_log_path)) + { + $this->_enabled = FALSE; + } + + if (is_numeric($config['log_threshold'])) + { + $this->_threshold = (int) $config['log_threshold']; + } + elseif (is_array($config['log_threshold'])) + { + $this->_threshold = $this->_threshold_max; + $this->_threshold_array = array_flip($config['log_threshold']); + } + + if ($config['log_date_format'] !== '') + { + $this->_date_fmt = $config['log_date_format']; + } + } + + // -------------------------------------------------------------------- + + /** + * Write Log File + * + * Generally this function will be called using the global log_message() function + * + * @param string the error level + * @param string the error message + * @param bool whether the error is a native PHP error + * @return bool + */ + public function write_log($level = 'error', $msg, $php_error = FALSE) + { + if ($this->_enabled === FALSE) + { + return FALSE; + } + + $level = strtoupper($level); + + if (( ! isset($this->_levels[$level]) OR ($this->_levels[$level] > $this->_threshold)) + && ! isset($this->_threshold_array[$this->_levels[$level]])) + { + return FALSE; + } + + $filepath = $this->_log_path.'log-'.date('Y-m-d').'.php'; + $message = ''; + + if ( ! file_exists($filepath)) + { + $newfile = TRUE; + $message .= '<'."?php defined('BASEPATH') OR exit('No direct script access allowed'); ?".">\n\n"; + } + + if ( ! $fp = @fopen($filepath, FOPEN_WRITE_CREATE)) + { + return FALSE; + } + + $message .= $level.' '.($level === 'INFO' ? ' -' : '-').' '.date($this->_date_fmt).' --> '.$msg."\n"; + + flock($fp, LOCK_EX); + fwrite($fp, $message); + flock($fp, LOCK_UN); + fclose($fp); + + if (isset($newfile) && $newfile === TRUE) + { + @chmod($filepath, FILE_WRITE_MODE); + } + + return TRUE; + } + +} + +/* End of file Log.php */ +/* Location: ./system/libraries/Log.php */
\ No newline at end of file diff --git a/system/core/Model.php b/system/core/Model.php index 9bc9f879f..28fdfbb69 100644 --- a/system/core/Model.php +++ b/system/core/Model.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,9 +24,10 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** - * CodeIgniter Model Class + * Model Class * * @package CodeIgniter * @subpackage Libraries @@ -37,7 +38,7 @@ class CI_Model { /** - * Initialize CI_Model Class + * Class constructor * * @return void */ @@ -46,13 +47,15 @@ class CI_Model { log_message('debug', 'Model Class Initialized'); } + // -------------------------------------------------------------------- + /** - * __get + * __get magic * * Allows models to access CI's loaded classes using the same * syntax as controllers. * - * @param string + * @param string $key */ public function __get($key) { diff --git a/system/core/Output.php b/system/core/Output.php index 11f484058..2e239c787 100644 --- a/system/core/Output.php +++ b/system/core/Output.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,11 +24,12 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * Output Class * - * Responsible for sending final output to browser + * Responsible for sending final output to the browser. * * @package CodeIgniter * @subpackage Libraries @@ -39,70 +40,74 @@ class CI_Output { /** - * Current output string + * Final output string * - * @var string + * @var string */ public $final_output; /** * Cache expiration time * - * @var int + * @var int */ - public $cache_expiration = 0; + public $cache_expiration = 0; /** * List of server headers * - * @var array + * @var array */ public $headers = array(); /** * List of mime types * - * @var array + * @var array */ public $mimes = array(); /** * Mime-type for the current page * - * @var string + * @var string */ - protected $mime_type = 'text/html'; + protected $mime_type = 'text/html'; /** - * Determines whether profiler is enabled + * Enable Profiler flag * - * @var book + * @var bool */ - public $enable_profiler = FALSE; + public $enable_profiler = FALSE; /** - * Determines if output compression is enabled + * zLib output compression flag * - * @var bool + * @var bool */ protected $_zlib_oc = FALSE; /** * List of profiler sections * - * @var array + * @var array */ protected $_profiler_sections = array(); /** - * Whether or not to parse variables like {elapsed_time} and {memory_usage} + * Parse markers flag * - * @var bool + * Whether or not to parse variables like {elapsed_time} and {memory_usage}. + * + * @var bool */ public $parse_exec_vars = TRUE; /** - * Set up Output class + * Class constructor + * + * Determines whether zLib output compression will be used. * * @return void */ @@ -121,7 +126,7 @@ class CI_Output { /** * Get Output * - * Returns the current output string + * Returns the current output string. * * @return string */ @@ -135,10 +140,10 @@ class CI_Output { /** * Set Output * - * Sets the output string + * Sets the output string. * - * @param string - * @return void + * @param string $output Output data + * @return object $this */ public function set_output($output) { @@ -151,14 +156,14 @@ class CI_Output { /** * Append Output * - * Appends data onto the output string + * Appends data onto the output string. * - * @param string - * @return void + * @param string $output Data to append + * @return object $this */ public function append_output($output) { - if ($this->final_output == '') + if (empty($this->final_output)) { $this->final_output = $output; } @@ -175,14 +180,14 @@ class CI_Output { /** * Set Header * - * Lets you set a server header which will be outputted with the final display. + * Lets you set a server header which will be sent with the final output. * - * Note: If a file is cached, headers will not be sent. We need to figure out - * how to permit header data to be saved with the cache data... + * Note: If a file is cached, headers will not be sent. + * @todo We need to figure out how to permit headers to be cached. * - * @param string - * @param bool - * @return void + * @param string $header Header + * @param bool $replace Whether to replace the old header value, if already set + * @return object $this */ public function set_header($header, $replace = TRUE) { @@ -192,7 +197,7 @@ class CI_Output { // We'll just skip content-length in those cases. if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0) { - return; + return $this; } $this->headers[] = array($header, $replace); @@ -202,10 +207,11 @@ class CI_Output { // -------------------------------------------------------------------- /** - * Set Content Type Header + * Set Content-Type Header * - * @param string extension of the file we're outputting - * @return void + * @param string $mime_type Extension of the file we're outputting + * @param string $charset Character set (default: NULL) + * @return object $this */ public function set_content_type($mime_type, $charset = NULL) { @@ -242,7 +248,7 @@ class CI_Output { // -------------------------------------------------------------------- /** - * Get Current Content Type Header + * Get Current Content-Type Header * * @return string 'text/html', if not already set */ @@ -250,9 +256,9 @@ class CI_Output { { for ($i = 0, $c = count($this->headers); $i < $c; $i++) { - if (preg_match('/^Content-Type:\s(.+)$/', $this->headers[$i][0], $matches)) + if (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1) { - return $matches[1]; + return $content_type; } } @@ -262,12 +268,47 @@ class CI_Output { // -------------------------------------------------------------------- /** + * Get Header + * + * @param string $header_name + * @return string + */ + public function get_header($header) + { + // Combine headers already sent with our batched headers + $headers = array_merge( + // We only need [x][0] from our multi-dimensional array + array_map('array_shift', $this->headers), + headers_list() + ); + + if (empty($headers) OR empty($header)) + { + return NULL; + } + + for ($i = 0, $c = count($headers); $i < $c; $i++) + { + if (strncasecmp($header, $headers[$i], $l = strlen($header)) === 0) + { + return trim(substr($headers[$i], $l+1)); + } + } + + return NULL; + } + + // -------------------------------------------------------------------- + + /** * Set HTTP Status Header - * moved to Common procedural functions in 1.7.2 * - * @param int the status code - * @param string - * @return void + * As of version 1.7.2, this is an alias for common function + * set_status_header(). + * + * @param int $code Status code (default: 200) + * @param string $text Optional message + * @return object $this */ public function set_status_header($code = 200, $text = '') { @@ -280,8 +321,8 @@ class CI_Output { /** * Enable/disable Profiler * - * @param bool - * @return void + * @param bool $val TRUE to enable or FALSE to disable + * @return object $this */ public function enable_profiler($val = TRUE) { @@ -294,10 +335,11 @@ class CI_Output { /** * Set Profiler Sections * - * Allows override of default / config settings for Profiler section display + * Allows override of default/config settings for + * Profiler section display. * - * @param array - * @return void + * @param array $sections Profiler sections + * @return object $this */ public function set_profiler_sections($sections) { @@ -320,8 +362,8 @@ class CI_Output { /** * Set Cache * - * @param int - * @return void + * @param int $time Cache expiration time in seconds + * @return object $this */ public function cache($time) { @@ -334,16 +376,16 @@ class CI_Output { /** * Display Output * - * All "view" data is automatically put into this variable by the controller class: - * - * $this->final_output + * Processes sends the sends finalized output data to the browser along + * with any server headers and profile data. It also stops benchmark + * timers so the page rendering speed and memory usage can be shown. * - * This function sends the finalized output data to the browser along - * with any server headers and profile data. It also stops the - * benchmark timer so the page rendering speed and memory usage can be shown. + * Note: All "view" data is automatically put into $this->final_output + * by controller class. * - * @param string - * @return mixed + * @uses CI_Output::$final_output + * @param string $output Output data override + * @return void */ public function _display($output = '') { @@ -374,10 +416,9 @@ class CI_Output { $output = $this->minify($output, $this->mime_type); } - // -------------------------------------------------------------------- - // Do we need to write a cache file? Only if the controller does not have its + // Do we need to write a cache file? Only if the controller does not have its // own _output() method and we are not dealing with a cache file, which we // can determine by the existence of the $CI object above if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output')) @@ -430,7 +471,7 @@ class CI_Output { echo $output; log_message('debug', 'Final output sent to browser'); log_message('debug', 'Total execution time: '.$elapsed); - return TRUE; + return; } // -------------------------------------------------------------------- @@ -472,9 +513,9 @@ class CI_Output { // -------------------------------------------------------------------- /** - * Write a Cache File + * Write Cache * - * @param string + * @param string $output Output data to cache * @return void */ public function _write_cache($output) @@ -525,11 +566,14 @@ class CI_Output { // -------------------------------------------------------------------- /** - * Update/serve a cached file + * Update/serve cached output * - * @param object config class - * @param object uri class - * @return bool + * @uses CI_Config + * @uses CI_URI + * + * @param object &$CFG CI_Config class instance + * @param object &$URI CI_URI class instance + * @return bool TRUE on success or FALSE on failure */ public function _display_cache(&$CFG, &$URI) { @@ -552,13 +596,13 @@ class CI_Output { fclose($fp); // Strip out the embedded timestamp - if ( ! preg_match('/(\d+TS--->)/', $cache, $match)) + if ( ! preg_match('/^(\d+)TS--->/', $cache, $match)) { return FALSE; } $last_modified = filemtime($cache_path); - $expire = trim(str_replace('TS--->', '', $match[1])); + $expire = $match[1]; // Has the file expired? if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path)) @@ -575,7 +619,7 @@ class CI_Output { } // Display the cache - $this->_display(str_replace($match[0], '', $cache)); + $this->_display(substr($cache, strlen($match[0]))); log_message('debug', 'Cache file is current. Sending it to browser.'); return TRUE; } @@ -583,11 +627,52 @@ class CI_Output { // -------------------------------------------------------------------- /** + * Delete cache + * + * @param string $uri URI string + * @return bool + */ + public function delete_cache($uri = '') + { + $CI =& get_instance(); + $cache_path = $CI->config->item('cache_path'); + if ($cache_path === '') + { + $cache_path = APPPATH.'cache/'; + } + + if ( ! is_dir($cache_path)) + { + log_message('error', 'Unable to find cache path: '.$cache_path); + return FALSE; + } + + if (empty($uri)) + { + $uri = $CI->uri->uri_string(); + } + + $cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').$uri); + + if ( ! @unlink($cache_path)) + { + log_message('error', 'Unable to delete cache file for '.$uri); + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set Cache Header + * * Set the HTTP headers to match the server-side file cache settings * in order to reduce bandwidth. * - * @param int timestamp of when the page was last modified - * @param int timestamp of when should the requested page expire from cache + * @param int $last_modified Timestamp of when the page was last modified + * @param int $expiration Timestamp of when should the requested page expire from cache * @return void */ public function set_cache_header($last_modified, $expiration) @@ -611,11 +696,13 @@ class CI_Output { // -------------------------------------------------------------------- /** - * Reduce excessive size of HTML content. + * Minify * - * @param string - * @param string - * @return string + * Reduce excessive size of HTML/CSS/JavaScript content. + * + * @param string $output Output to minify + * @param string $type Output content MIME type + * @return string Minified output */ public function minify($output, $type = 'text/html') { diff --git a/system/core/Router.php b/system/core/Router.php index 5bc053045..76772a0fb 100644 --- a/system/core/Router.php +++ b/system/core/Router.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,6 +24,7 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * Router Class @@ -39,56 +40,49 @@ class CI_Router { /** - * Config class + * CI_Config class object * - * @var object + * @var object */ public $config; /** * List of routes * - * @var array + * @var array */ public $routes = array(); /** - * List of error routes - * - * @var array - */ - public $error_routes = array(); - - /** * Current class name * - * @var string + * @var string */ public $class = ''; /** * Current method name * - * @var string + * @var string */ public $method = 'index'; /** * Sub-directory that contains the requested controller class * - * @var string + * @var string */ public $directory = ''; /** * Default controller (and method if specific) * - * @var string + * @var string */ public $default_controller; /** - * Constructor + * Class constructor * * Runs the route mapping function. * @@ -104,9 +98,9 @@ class CI_Router { // -------------------------------------------------------------------- /** - * Set the route mapping + * Set route mapping * - * This function determines what should be served based on the URI request, + * Determines what should be served based on the URI request, * as well as any "routes" that have been set in the routing config file. * * @return void @@ -117,21 +111,21 @@ class CI_Router { // 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 $segments = array(); - if ($this->config->item('enable_query_strings') === TRUE && isset($_GET[$this->config->item('controller_trigger')])) + if ($this->config->item('enable_query_strings') === TRUE + && ! empty($_GET[$this->config->item('controller_trigger')]) + && is_string($_GET[$this->config->item('controller_trigger')]) + ) { - if (isset($_GET[$this->config->item('directory_trigger')])) + if (isset($_GET[$this->config->item('directory_trigger')]) && is_string($_GET[$this->config->item('directory_trigger')])) { $this->set_directory(trim($this->uri->_filter_uri($_GET[$this->config->item('directory_trigger')]))); $segments[] = $this->fetch_directory(); } - if (isset($_GET[$this->config->item('controller_trigger')])) - { - $this->set_class(trim($this->uri->_filter_uri($_GET[$this->config->item('controller_trigger')]))); - $segments[] = $this->fetch_class(); - } + $this->set_class(trim($this->uri->_filter_uri($_GET[$this->config->item('controller_trigger')]))); + $segments[] = $this->fetch_class(); - if (isset($_GET[$this->config->item('function_trigger')])) + if ( ! empty($_GET[$this->config->item('function_trigger')]) && is_string($_GET[$this->config->item('function_trigger')])) { $this->set_method(trim($this->uri->_filter_uri($_GET[$this->config->item('function_trigger')]))); $segments[] = $this->fetch_method(); @@ -148,12 +142,12 @@ class CI_Router { include(APPPATH.'config/routes.php'); } - $this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route; + $this->routes = (empty($route) OR ! is_array($route)) ? array() : $route; unset($route); // Set the default controller so we can display it in the event // the URI doesn't correlated to a valid controller. - $this->default_controller = empty($this->routes['default_controller']) ? FALSE : strtolower($this->routes['default_controller']); + $this->default_controller = empty($this->routes['default_controller']) ? FALSE : $this->routes['default_controller']; // Were there any query string segments? If so, we'll validate them and bail out since we're done. if (count($segments) > 0) @@ -179,31 +173,27 @@ class CI_Router { // -------------------------------------------------------------------- /** - * Set the default controller + * Set default controller * * @return void */ protected function _set_default_controller() { - if ($this->default_controller === FALSE) + if (empty($this->default_controller)) { show_error('Unable to determine what should be displayed. A default route has not been specified in the routing file.'); } + // Is the method being specified? - if (strpos($this->default_controller, '/') !== FALSE) - { - $x = explode('/', $this->default_controller); - $this->set_class($x[0]); - $this->set_method($x[1]); - $this->_set_request($x); - } - else + if (sscanf($this->default_controller, '%[^/]/%s', $class, $method) !== 2) { - $this->set_class($this->default_controller); - $this->set_method('index'); - $this->_set_request(array($this->default_controller, 'index')); + $method = 'index'; } + $this->set_class($class); + $this->set_method($method); + $this->_set_request(array($class, $method)); + // re-index the routed segments array so it starts with 1 rather than 0 $this->uri->_reindex_segments(); @@ -213,12 +203,12 @@ class CI_Router { // -------------------------------------------------------------------- /** - * Set the Route + * Set request route * - * This function takes an array of URI segments as - * input, and sets the current class/method + * Takes an array of URI segments as input and sets the class/method + * to be called. * - * @param array + * @param array $segments URI segments * @return void */ protected function _set_request($segments = array()) @@ -232,17 +222,8 @@ class CI_Router { $this->set_class($segments[0]); - if (isset($segments[1])) - { - // A standard method request - $this->set_method($segments[1]); - } - else - { - // This lets the "routed" segment array identify that the default - // index method is being used. - $segments[1] = 'index'; - } + isset($segments[1]) OR $segments[1] = 'index'; + $this->set_method($segments[1]); // Update our "routed" segment array to contain the segments. // Note: If there is no custom routing, this array will be @@ -253,11 +234,12 @@ class CI_Router { // -------------------------------------------------------------------- /** - * Validates the supplied segments. - * Attempts to determine the path to the controller. + * Validate request * - * @param array - * @return array + * Attempts validate the URI request and determine the controller path. + * + * @param array $segments URI segments + * @return array URI segments */ protected function _validate_request($segments) { @@ -266,9 +248,13 @@ class CI_Router { return $segments; } + $temp = str_replace('-', '_', $segments[0]); + // Does the requested controller exist in the root folder? - if (file_exists(APPPATH.'controllers/'.$segments[0].'.php')) + if (file_exists(APPPATH.'controllers/'.$temp.'.php')) { + $segments[0] = $temp; + empty($segments[1]) OR $segments[1] = str_replace('-', '_', $segments[1]); return $segments; } @@ -276,22 +262,19 @@ class CI_Router { if (is_dir(APPPATH.'controllers/'.$segments[0])) { // Set the directory and remove it from the segment array - $this->set_directory($segments[0]); - $segments = array_slice($segments, 1); - + $this->set_directory(array_shift($segments)); if (count($segments) > 0) { + $segments[0] = str_replace('-', '_', $segments[0]); + empty($segments[1]) OR $segments[1] = str_replace('-', '_', $segments[1]); + // Does the requested controller exist in the sub-folder? if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].'.php')) { if ( ! empty($this->routes['404_override'])) { - $x = explode('/', $this->routes['404_override']); - $this->set_directory(''); - $this->set_class($x[0]); - $this->set_method(isset($x[1]) ? $x[1] : 'index'); - - return $x; + $this->directory = ''; + return explode('/', $this->routes['404_override'], 2); } else { @@ -302,40 +285,26 @@ class CI_Router { else { // Is the method being specified in the route? - if (strpos($this->default_controller, '/') !== FALSE) - { - $x = explode('/', $this->default_controller); - $this->set_class($x[0]); - $this->set_method($x[1]); - } - else - { - $this->set_class($this->default_controller); - $this->set_method('index'); - } - - // Does the default controller exist in the sub-folder? - if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.'.php')) + $segments = explode('/', $this->default_controller); + if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].'.php')) { $this->directory = ''; - return array(); } - } return $segments; } - // If we've gotten this far it means that the URI does not correlate to a valid // controller class. We will now see if there is an override if ( ! empty($this->routes['404_override'])) { - $x = explode('/', $this->routes['404_override']); - $this->set_class($x[0]); - $this->set_method(isset($x[1]) ? $x[1] : 'index'); + if (sscanf($this->routes['404_override'], '%[^/]/%s', $class, $method) !== 2) + { + $method = 'index'; + } - return $x; + return array($class, $method); } // Nothing else to do at this point but show a 404 @@ -347,9 +316,8 @@ class CI_Router { /** * Parse Routes * - * This function matches any routes that may exist in - * the config/routes.php file against the URI to - * determine if the class/method need to be remapped. + * Matches any routes that may exist in the config/routes.php file + * against the URI to determine if the class/method need to be remapped. * * @return void */ @@ -359,7 +327,7 @@ class CI_Router { $uri = implode('/', $this->uri->segments); // Is there a literal match? If so we're done - if (isset($this->routes[$uri])) + if (isset($this->routes[$uri]) && is_string($this->routes[$uri])) { return $this->_set_request(explode('/', $this->routes[$uri])); } @@ -368,13 +336,51 @@ class CI_Router { foreach ($this->routes as $key => $val) { // Convert wild-cards to RegEx - $key = str_replace(array(':any', ':num'), array('.+', '[0-9]+'), $key); + $key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key); // Does the RegEx match? - if (preg_match('#^'.$key.'$#', $uri)) + if (preg_match('#^'.$key.'$#', $uri, $matches)) { - // Do we have a back-reference? - if (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE) + // Are we using callbacks to process back-references? + if ( ! is_string($val) && is_callable($val)) + { + // Remove the original string from the matches array. + array_shift($matches); + + // Get the match count. + $match_count = count($matches); + + // Determine how many parameters the callback has. + $reflection = new ReflectionFunction($val); + $param_count = $reflection->getNumberOfParameters(); + + // Are there more parameters than matches? + if ($param_count > $match_count) + { + // Any params without matches will be set to an empty string. + $matches = array_merge($matches, array_fill($match_count, $param_count - $match_count, '')); + + $match_count = $param_count; + } + + // Get the parameters so we can use their default values. + $params = $reflection->getParameters(); + + for ($m = 0; $m < $match_count; $m++) + { + // Is the match empty and does a default value exist? + if (empty($matches[$m]) && $params[$m]->isDefaultValueAvailable()) + { + // Substitute the empty match for the default value. + $matches[$m] = $params[$m]->getDefaultValue(); + } + } + + // Execute the callback using the values in matches as its parameters. + $val = call_user_func_array($val, $matches); + } + // Are we using the default routing method for back-references? + elseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE) { $val = preg_replace('#^'.$key.'$#', $val, $uri); } @@ -391,9 +397,9 @@ class CI_Router { // -------------------------------------------------------------------- /** - * Set the class name + * Set class name * - * @param string + * @param string $class Class name * @return void */ public function set_class($class) @@ -416,9 +422,9 @@ class CI_Router { // -------------------------------------------------------------------- /** - * Set the method name + * Set method name * - * @param string + * @param string $method Method name * @return void */ public function set_method($method) @@ -441,9 +447,9 @@ class CI_Router { // -------------------------------------------------------------------- /** - * Set the directory name + * Set directory name * - * @param string + * @param string $dir Directory name * @return void */ public function set_directory($dir) @@ -454,7 +460,10 @@ class CI_Router { // -------------------------------------------------------------------- /** - * Fetch the sub-directory (if any) that contains the requested controller class + * Fetch directory + * + * Feches the sub-directory (if any) that contains the requested + * controller class. * * @return string */ @@ -466,9 +475,9 @@ class CI_Router { // -------------------------------------------------------------------- /** - * Set the controller overrides + * Set controller overrides * - * @param array + * @param array $routing Route overrides * @return void */ public function _set_overrides($routing) @@ -490,7 +499,7 @@ class CI_Router { if (isset($routing['function'])) { - $routing['function'] = ($routing['function'] == '') ? 'index' : $routing['function']; + $routing['function'] = empty($routing['function']) ? 'index' : $routing['function']; $this->set_method($routing['function']); } } diff --git a/system/core/Security.php b/system/core/Security.php index b22d2cf19..c415544b6 100644 --- a/system/core/Security.php +++ b/system/core/Security.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,6 +24,7 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * Security Class @@ -37,45 +38,55 @@ class CI_Security { /** - * Random Hash for protecting URLs + * XSS Hash * - * @var string + * Random Hash for protecting URLs. + * + * @var string */ protected $_xss_hash = ''; /** - * Random Hash for Cross Site Request Forgery Protection Cookie + * CSRF Hash + * + * Random hash for Cross Site Request Forgery protection cookie * - * @var string + * @var string */ protected $_csrf_hash = ''; /** - * Expiration time for Cross Site Request Forgery Protection Cookie - * Defaults to two hours (in seconds) + * CSRF Expire time + * + * Expiration time for Cross Site Request Forgery protection cookie. + * Defaults to two hours (in seconds). * - * @var int + * @var int */ protected $_csrf_expire = 7200; /** - * Token name for Cross Site Request Forgery Protection Cookie + * CSRF Token name * - * @var string + * Token name for Cross Site Request Forgery protection cookie. + * + * @var string */ protected $_csrf_token_name = 'ci_csrf_token'; /** - * Cookie name for Cross Site Request Forgery Protection Cookie + * CSRF Cookie name + * + * Cookie name for Cross Site Request Forgery protection cookie. * - * @var string + * @var string */ protected $_csrf_cookie_name = 'ci_csrf_token'; /** * List of never allowed strings * - * @var array + * @var array */ protected $_never_allowed_str = array( 'document.cookie' => '[removed]', @@ -91,9 +102,9 @@ class CI_Security { ); /** - * List of never allowed regex replacement + * List of never allowed regex replacements * - * @var array + * @var array */ protected $_never_allowed_regex = array( 'javascript\s*:', @@ -104,7 +115,7 @@ class CI_Security { ); /** - * Initialize security class + * Class constructor * * @return void */ @@ -138,7 +149,7 @@ class CI_Security { // -------------------------------------------------------------------- /** - * Verify Cross Site Request Forgery Protection + * CSRF Verify * * @return object */ @@ -161,7 +172,7 @@ class CI_Security { } // Do the tokens exist in both the _POST and _COOKIE arrays? - if ( ! isset($_POST[$this->_csrf_token_name]) OR ! isset($_COOKIE[$this->_csrf_cookie_name]) + if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]) OR $_POST[$this->_csrf_token_name] !== $_COOKIE[$this->_csrf_cookie_name]) // Do the tokens match? { $this->csrf_show_error(); @@ -188,17 +199,17 @@ class CI_Security { // -------------------------------------------------------------------- /** - * Set Cross Site Request Forgery Protection Cookie + * CSRF Set Cookie * - * @return object * @codeCoverageIgnore + * @return object */ public function csrf_set_cookie() { $expire = time() + $this->_csrf_expire; $secure_cookie = (bool) config_item('cookie_secure'); - if ($secure_cookie && (empty($_SERVER['HTTPS']) OR strtolower($_SERVER['HTTPS']) === 'off')) + if ($secure_cookie && ! is_https()) { return FALSE; } @@ -234,9 +245,8 @@ class CI_Security { /** * Get CSRF Hash * - * Getter Method - * - * @return string self::_csrf_hash + * @see CI_Security::$_csrf_hash + * @return string CSRF hash */ public function get_csrf_hash() { @@ -248,9 +258,8 @@ class CI_Security { /** * Get CSRF Token Name * - * Getter Method - * - * @return string self::_csrf_token_name + * @see CI_Security::$_csrf_token_name + * @return string CSRF token name */ public function get_csrf_token_name() { @@ -263,26 +272,26 @@ class CI_Security { * XSS Clean * * Sanitizes data so that Cross Site Scripting Hacks can be - * prevented. This function does a fair amount of work but + * prevented. This method does a fair amount of work but * it is extremely thorough, designed to prevent even the * most obscure XSS attempts. Nothing is ever 100% foolproof, * of course, but I haven't been able to get anything passed * the filter. * - * 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. + * Note: Should only be used to deal with data upon submission. + * It's not something that should be used for general + * runtime processing. * - * This function was based in part on some code and ideas I - * got from Bitflux: http://channel.bitflux.ch/wiki/XSS_Prevention + * @link http://channel.bitflux.ch/wiki/XSS_Prevention + * Based in part on some code and ideas from Bitflux. * - * To help develop this script I used this great list of - * vulnerabilities along with a few other hacks I've - * harvested from examining vulnerabilities in other programs: - * http://ha.ckers.org/xss.html + * @link http://ha.ckers.org/xss.html + * To help develop this script I used this great list of + * vulnerabilities along with a few other hacks I've + * harvested from examining vulnerabilities in other programs. * - * @param mixed string or array - * @param bool + * @param string|string[] $str Input data + * @param bool $is_image Whether the input is an image * @return string */ public function xss_clean($str, $is_image = FALSE) @@ -359,7 +368,7 @@ class CI_Security { } else { - $str = str_replace(array('<?', '?'.'>'), array('<?', '?>'), $str); + $str = str_replace(array('<?', '?'.'>'), array('<?', '?>'), $str); } /* @@ -373,7 +382,6 @@ class CI_Security { 'applet', 'alert', 'document', 'write', 'cookie', 'window' ); - foreach ($words as $word) { $word = implode('\s*', str_split($word)).'\s*'; @@ -469,9 +477,12 @@ class CI_Security { // -------------------------------------------------------------------- /** - * Random Hash for protecting URLs + * XSS Hash * - * @return string + * Generates the XSS hash if needed and returns it. + * + * @see CI_Security::$_xss_hash + * @return string XSS hash */ public function xss_hash() { @@ -489,7 +500,7 @@ class CI_Security { /** * HTML Entities Decode * - * This function is a replacement for html_entity_decode() + * A replacement for html_entity_decode() * * The reason we are not using html_entity_decode() by itself is because * while it is not technically correct to leave out the semicolon @@ -497,8 +508,10 @@ class CI_Security { * correctly. html_entity_decode() does not convert entities without * semicolons, so we are left with our own little solution here. Bummer. * - * @param string - * @param string + * @link http://php.net/html-entity-decode + * + * @param string $str Input + * @param string $charset Character set * @return string */ public function entity_decode($str, $charset = NULL) @@ -521,10 +534,10 @@ class CI_Security { // -------------------------------------------------------------------- /** - * Filename Security + * Sanitize Filename * - * @param string - * @param bool + * @param string $str Input file name + * @param bool $relative_path Whether to preserve paths * @return string */ public function sanitize_filename($str, $relative_path = FALSE) @@ -563,7 +576,7 @@ class CI_Security { /** * Strip Image Tags * - * @param string + * @param string $str * @return string */ public function strip_image_tags($str) @@ -576,10 +589,11 @@ class CI_Security { /** * Compact Exploded Words * - * Callback function for xss_clean() to remove whitespace from - * things like j a v a s c r i p t + * Callback method for xss_clean() to remove whitespace from + * things like 'j a v a s c r i p t'. * - * @param array + * @used-by CI_Security::xss_clean() + * @param array $matches * @return string */ protected function _compact_exploded_words($matches) @@ -593,16 +607,22 @@ 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: + * + * - Everything up until a space. For example, everything between the pipes: + * + * <code> * <a |style=document.write('hello');alert('world');| class=link> - * - Everything inside the quotes - * For example, everything between the pipes: + * </code> + * + * - Everything inside the quotes. For example, everything between the pipes: + * + * <code> * <a |style="document.write('hello'); alert('world');"| class="link"> + * </code> * - * @param string $str The string to check - * @param boolean $is_image TRUE if this is an image - * @return string The string with the evil attributes removed + * @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 */ protected function _remove_evil_attributes($str, $is_image) { @@ -655,9 +675,10 @@ class CI_Security { /** * Sanitize Naughty HTML * - * Callback function for xss_clean() to remove naughty HTML elements + * Callback method for xss_clean() to remove naughty HTML elements. * - * @param array + * @used-by CI_Security::xss_clean() + * @param array $matches * @return string */ protected function _sanitize_naughty_html($matches) @@ -672,12 +693,14 @@ class CI_Security { /** * JS Link Removal * - * Callback function for xss_clean() to sanitize links + * Callback method for xss_clean() to sanitize links. + * * This limits the PCRE backtracks, making it more performance friendly * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in - * PHP 5.2+ on link-heavy strings + * PHP 5.2+ on link-heavy strings. * - * @param array + * @used-by CI_Security::xss_clean() + * @param array $match * @return string */ protected function _js_link_removal($match) @@ -695,12 +718,14 @@ class CI_Security { /** * JS Image Removal * - * Callback function for xss_clean() to sanitize image tags + * Callback method for xss_clean() to sanitize image tags. + * * This limits the PCRE backtracks, making it more performance friendly * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in - * PHP 5.2+ on image tag heavy strings + * PHP 5.2+ on image tag heavy strings. * - * @param array + * @used-by CI_Security::xss_clean() + * @param array $match * @return string */ protected function _js_img_removal($match) @@ -718,9 +743,8 @@ class CI_Security { /** * Attribute Conversion * - * Used as a callback for XSS Clean - * - * @param array + * @used-by CI_Security::xss_clean() + * @param array $match * @return string */ protected function _convert_attribute($match) @@ -733,9 +757,11 @@ class CI_Security { /** * Filter Attributes * - * Filters tag attributes for consistency and safety + * Filters tag attributes for consistency and safety. * - * @param string + * @used-by CI_Security::_js_img_removal() + * @used-by CI_Security::_js_link_removal() + * @param string $str * @return string */ protected function _filter_attributes($str) @@ -757,9 +783,8 @@ class CI_Security { /** * HTML Entity Decode Callback * - * Used as a callback for XSS Clean - * - * @param array + * @used-by CI_Security::xss_clean() + * @param array $match * @return string */ protected function _decode_entity($match) @@ -772,9 +797,8 @@ class CI_Security { /** * Validate URL entities * - * Called by xss_clean() - * - * @param string + * @used-by CI_Security::xss_clean() + * @param string $str * @return string */ protected function _validate_entities($str) @@ -812,8 +836,7 @@ class CI_Security { /** * Do Never Allowed * - * A utility function for xss_clean() - * + * @used-by CI_Security::xss_clean() * @param string * @return string */ @@ -832,7 +855,7 @@ class CI_Security { // -------------------------------------------------------------------- /** - * Set Cross Site Request Forgery Protection Cookie + * Set CSRF Hash and Cookie * * @return string */ diff --git a/system/core/URI.php b/system/core/URI.php index 6a8b1a5ac..900472b61 100644 --- a/system/core/URI.php +++ b/system/core/URI.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,6 +24,7 @@ * @since Version 1.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * URI Class @@ -39,36 +40,37 @@ class CI_URI { /** - * List of cached uri segments + * List of cached URI segments * - * @var array + * @var array */ public $keyval = array(); /** - * Current uri string + * Current URI string * - * @var string + * @var string */ public $uri_string; /** - * List of uri segments + * List of URI segments * - * @var array + * @var array */ public $segments = array(); /** - * Re-indexed list of uri segments - * Starts at 1 instead of 0 + * Re-indexed list of URI segments * - * @var array + * Starts at 1 instead of 0. + * + * @var array */ public $rsegments = array(); /** - * Constructor + * Class constructor * * Simply globalizes the $RTR object. The front * loads the Router class early on so it's not available @@ -85,10 +87,9 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Get the URI String - * - * Called by CI_Router + * Fetch URI String * + * @used-by CI_Router * @return void */ public function _fetch_uri_string() @@ -98,31 +99,28 @@ class CI_URI { // Is the request coming from the command line? if ($this->_is_cli_request()) { - $this->_set_uri_string($this->_parse_cli_args()); + $this->_set_uri_string($this->_parse_argv()); return; } - // Let's try the REQUEST_URI first, this will work in most situations - if ($uri = $this->_detect_uri()) + // Is there a PATH_INFO variable? This should be the easiest solution. + if (isset($_SERVER['PATH_INFO'])) { - $this->_set_uri_string($uri); + $this->_set_uri_string($_SERVER['PATH_INFO']); return; } - // Is there a PATH_INFO variable? - // Note: some servers seem to have trouble with getenv() so we'll test it two ways - $path = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO'); - if (trim($path, '/') !== '' && $path !== '/'.SELF) + // Let's try REQUEST_URI then, this will work in most situations + if (($uri = $this->_parse_request_uri()) !== '') { - $this->_set_uri_string($path); + $this->_set_uri_string($uri); return; } - // No PATH_INFO?... What about QUERY_STRING? - $path = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); - if (trim($path, '/') !== '') + // No REQUEST_URI either?... What about QUERY_STRING? + if (($uri = $this->_parse_query_string()) !== '') { - $this->_set_uri_string($path); + $this->_set_uri_string($uri); return; } @@ -140,106 +138,155 @@ class CI_URI { $uri = strtoupper($this->config->item('uri_protocol')); - if ($uri === 'REQUEST_URI') + if ($uri === 'CLI') { - $this->_set_uri_string($this->_detect_uri()); + $this->_set_uri_string($this->_parse_argv()); return; } - elseif ($uri === 'CLI') + elseif (method_exists($this, ($method = '_parse_'.strtolower($uri)))) { - $this->_set_uri_string($this->_parse_cli_args()); + $this->_set_uri_string($this->$method()); return; } - $path = isset($_SERVER[$uri]) ? $_SERVER[$uri] : @getenv($uri); - $this->_set_uri_string($path); + $uri = isset($_SERVER[$uri]) ? $_SERVER[$uri] : @getenv($uri); + $this->_set_uri_string($uri); } // -------------------------------------------------------------------- /** - * Set the URI String + * Set URI String * - * @param string + * @param string $str * @return void */ protected function _set_uri_string($str) { - // Filter out control characters - $str = remove_invisible_characters($str, FALSE); - - // If the URI contains only a slash we'll kill it - $this->uri_string = ($str === '/') ? '' : $str; + // Filter out control characters and trim slashes + $this->uri_string = trim(remove_invisible_characters($str, FALSE), '/'); } // -------------------------------------------------------------------- /** - * Detects the URI + * Parse REQUEST_URI * - * This function will detect the URI automatically - * and fix the query string if necessary. + * Will parse REQUEST_URI and automatically detect the URI from it, + * while fixing the query string if necessary. * + * @used-by CI_URI::_fetch_uri_string() * @return string */ - protected function _detect_uri() + protected function _parse_request_uri() { if ( ! isset($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])) { return ''; } - if (strpos($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']) === 0) + $uri = parse_url($_SERVER['REQUEST_URI']); + $query = isset($uri['query']) ? $uri['query'] : ''; + $uri = isset($uri['path']) ? rawurldecode($uri['path']) : ''; + + if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) { - $uri = substr($_SERVER['REQUEST_URI'], strlen($_SERVER['SCRIPT_NAME'])); + $uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME'])); } - elseif (strpos($_SERVER['REQUEST_URI'], dirname($_SERVER['SCRIPT_NAME'])) === 0) + elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) { - $uri = substr($_SERVER['REQUEST_URI'], strlen(dirname($_SERVER['SCRIPT_NAME']))); - } - else - { - $uri = $_SERVER['REQUEST_URI']; + $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 // URI is found, and also fixes the QUERY_STRING server var and $_GET array. - if (strpos($uri, '?/') === 0) + if (trim($uri, '/') === '' && strncmp($query, '/', 1) === 0) + { + $query = explode('?', $query, 2); + $uri = rawurldecode($query[0]); + $_SERVER['QUERY_STRING'] = isset($query[1]) ? $query[1] : ''; + } + else { - $uri = substr($uri, 2); + $_SERVER['QUERY_STRING'] = $query; } - $parts = explode('?', $uri, 2); - $uri = $parts[0]; - if (isset($parts[1])) + parse_str($_SERVER['QUERY_STRING'], $_GET); + + if ($uri === '/' OR $uri === '') { - $_SERVER['QUERY_STRING'] = $parts[1]; - parse_str($_SERVER['QUERY_STRING'], $_GET); + return '/'; } - else + + // Do some final cleaning of the URI and return it + return $this->_remove_relative_directory($uri); + } + + // -------------------------------------------------------------------- + + /** + * Remove relative directory (../) and multi slashes (///) + * + * Do some final cleaning of the URI and return it, currently only used in self::_parse_request_uri() + * + * @param string $url + * @return string + */ + protected function _remove_relative_directory($uri) + { + $uris = array(); + $tok = strtok($uri, '/'); + while ($tok !== FALSE) { - $_SERVER['QUERY_STRING'] = ''; - $_GET = array(); + if (( ! empty($tok) OR $tok === '0') && $tok !== '..') + { + $uris[] = $tok; + } + $tok = strtok('/'); } + return implode('/', $uris); + } + + // -------------------------------------------------------------------- - if ($uri === '/' OR empty($uri)) + /** + * Parse QUERY_STRING + * + * Will parse QUERY_STRING and automatically detect the URI from it. + * + * @used-by CI_URI::_fetch_uri_string() + * @return string + */ + protected function _parse_query_string() + { + $uri = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); + + if (trim($uri, '/') === '') { - return '/'; + return ''; + } + elseif (strncmp($uri, '/', 1) === 0) + { + $uri = explode('?', $uri, 2); + $_SERVER['QUERY_STRING'] = isset($uri[1]) ? $uri[1] : ''; + $uri = rawurldecode($uri[0]); } - $uri = parse_url('pseudo://hostname/'.$uri, PHP_URL_PATH); + parse_str($_SERVER['QUERY_STRING'], $_GET); - // Do some final cleaning of the URI and return it - return str_replace(array('//', '../'), '/', trim($uri, '/')); + return $this->_remove_relative_directory($uri); } // -------------------------------------------------------------------- /** - * Is cli Request? + * Is CLI Request? * - * Duplicate of function from the Input class to test to see if a request was made from the command line + * Duplicate of method from the Input class to test to see if + * a request was made from the command line. * + * @see CI_Input::is_cli_request() + * @used-by CI_URI::_fetch_uri_string() * @return bool */ protected function _is_cli_request() @@ -250,26 +297,27 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Parse cli arguments + * Parse CLI arguments * * Take each command line argument and assume it is a URI segment. * * @return string */ - protected function _parse_cli_args() + protected function _parse_argv() { $args = array_slice($_SERVER['argv'], 1); - return $args ? '/'.implode('/', $args) : ''; + return $args ? implode('/', $args) : ''; } // -------------------------------------------------------------------- /** - * Filter segments for malicious characters + * Filter URI * - * Called by CI_Router + * Filters segments for malicious characters. * - * @param string + * @used-by CI_Router + * @param string $str * @return string */ public function _filter_uri($str) @@ -278,7 +326,7 @@ class CI_URI { { // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern - if ( ! preg_match('|^['.str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-')).']+$|i', urldecode($str))) + if ( ! preg_match('|^['.str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-')).']+$|i', $str)) { show_error('The URI you submitted has disallowed characters.', 400); } @@ -294,10 +342,11 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Remove the suffix from the URL if needed + * Remove URL suffix * - * Called by CI_Router + * Removes the suffix from the URL if needed. * + * @used-by CI_Router * @return void */ public function _remove_url_suffix() @@ -313,11 +362,12 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Explode the URI Segments. The individual segments will - * be stored in the $this->segments array. + * Explode URI segments * - * Called by CI_Router + * The individual segments will be stored in the $this->segments array. * + * @see CI_URI::$segments + * @used-by CI_Router * @return void */ public function _explode_segments() @@ -339,13 +389,12 @@ class CI_URI { /** * Re-index Segments * - * This function re-indexes the $this->segment array so that it - * starts at 1 rather than 0. Doing so makes it simpler to - * use functions like $this->uri->segment(n) since there is - * a 1:1 relationship between the segment array and the actual segments. - * - * Called by CI_Router + * Re-indexes the CI_URI::$segment array so that it starts at 1 rather + * than 0. Doing so makes it simpler to use methods like + * CI_URI::segment(n) since there is a 1:1 relationship between the + * segment array and the actual segments. * + * @used-by CI_Router * @return void */ public function _reindex_segments() @@ -359,13 +408,12 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Fetch a URI Segment + * Fetch URI Segment * - * This function returns the URI segment based on the number provided. - * - * @param int - * @param mixed - * @return string + * @see CI_URI::$segments + * @param int $n Index + * @param mixed $no_result What to return if the segment index is not found + * @return mixed */ public function segment($n, $no_result = NULL) { @@ -375,15 +423,17 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Fetch a URI "routed" Segment + * Fetch URI "routed" Segment * - * This function returns the re-routed URI segment (assuming routing rules are used) - * based on the number provided. If there is no routing this function returns the - * same result as $this->segment() + * Returns the re-routed URI segment (assuming routing rules are used) + * based on the index provided. If there is no routing, will return + * the same result as CI_URI::segment(). * - * @param int - * @param mixed - * @return string + * @see CI_URI::$rsegments + * @see CI_URI::segment() + * @param int $n Index + * @param mixed $no_result What to return if the segment index is not found + * @return mixed */ public function rsegment($n, $no_result = NULL) { @@ -393,23 +443,23 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Generate a key value pair from the URI string + * URI to assoc * - * This function generates and associative array of URI data starting - * at the supplied segment. For example, if this is your URI: + * Generates an associative array of URI data starting at the supplied + * segment index. For example, if this is your URI: * * example.com/user/search/name/joe/location/UK/gender/male * - * You can use this function to generate an array with this prototype: + * You can use this method to generate an array with this prototype: * - * array ( - * name => joe - * location => UK - * gender => male - * ) + * array ( + * name => joe + * location => UK + * gender => male + * ) * - * @param int the starting segment number - * @param array an array of default values + * @param int $n Index (default: 3) + * @param array $default Default values * @return array */ public function uri_to_assoc($n = 3, $default = array()) @@ -420,10 +470,14 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Identical to above only it uses the re-routed segment array + * Routed URI to assoc + * + * Identical to CI_URI::uri_to_assoc(), only it uses the re-routed + * segment array. * - * @param int the starting segment number - * @param array an array of default values + * @see CI_URI::uri_to_assoc() + * @param int $n Index (default: 3) + * @param array $default Default values * @return array */ public function ruri_to_assoc($n = 3, $default = array()) @@ -434,11 +488,15 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Generate a key value pair from the URI string or Re-routed URI string + * Internal URI-to-assoc * - * @param int the starting segment number - * @param array an array of default values - * @param string which array we should use + * Generates a key/value pair from the URI string or re-routed URI string. + * + * @used-by CI_URI::uri_to_assoc() + * @used-by CI_URI::ruri_to_assoc() + * @param int $n Index (default: 3) + * @param array $default Default values + * @param string $which Array name ('segment' or 'rsegment') * @return array */ protected function _uri_to_assoc($n = 3, $default = array(), $which = 'segment') @@ -448,9 +506,11 @@ class CI_URI { return $default; } - if (isset($this->keyval[$n])) + in_array($which, array('segment', 'rsegment'), TRUE) OR $which = 'segment'; + + if (isset($this->keyval[$which], $this->keyval[$which][$n])) { - return $this->keyval[$n]; + return $this->keyval[$which][$n]; } if ($which === 'segment') @@ -474,7 +534,7 @@ class CI_URI { $segments = array_slice($this->$segment_array(), ($n - 1)); $i = 0; $lastval = ''; - $retval = array(); + $retval = array(); foreach ($segments as $seg) { if ($i % 2) @@ -502,17 +562,20 @@ class CI_URI { } // Cache the array for reuse - $this->keyval[$n] = $retval; + isset($this->keyval[$which]) OR $this->keyval[$which] = array(); + $this->keyval[$which][$n] = $retval; return $retval; } // -------------------------------------------------------------------- /** - * Generate a URI string from an associative array + * Assoc to URI * - * @param array an associative array of key/values - * @return array + * Generates a URI string from an associative array. + * + * @param array $array Input array of key/value pairs + * @return string URI string */ public function assoc_to_uri($array) { @@ -529,10 +592,12 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Fetch a URI Segment and add a trailing slash + * Slash segment * - * @param int - * @param string + * Fetches an URI segment with a slash. + * + * @param int $n Index + * @param string $where Where to add the slash ('trailing' or 'leading') * @return string */ public function slash_segment($n, $where = 'trailing') @@ -543,10 +608,12 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Fetch a URI Segment and add a trailing slash + * Slash routed segment + * + * Fetches an URI routed segment with a slash. * - * @param int - * @param string + * @param int $n Index + * @param string $where Where to add the slash ('trailing' or 'leading') * @return string */ public function slash_rsegment($n, $where = 'trailing') @@ -557,11 +624,16 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Fetch a URI Segment and add a trailing slash - helper function + * Internal Slash segment + * + * Fetches an URI Segment and adds a slash to it. + * + * @used-by CI_URI::slash_segment() + * @used-by CI_URI::slash_rsegment() * - * @param int - * @param string - * @param string + * @param int $n Index + * @param string $where Where to add the slash ('trailing' or 'leading') + * @param string $which Array name ('segment' or 'rsegment') * @return string */ protected function _slash_segment($n, $where = 'trailing', $which = 'segment') @@ -585,7 +657,7 @@ class CI_URI { /** * Segment Array * - * @return array + * @return array CI_URI::$segments */ public function segment_array() { @@ -597,7 +669,7 @@ class CI_URI { /** * Routed Segment Array * - * @return array + * @return array CI_URI::$rsegments */ public function rsegment_array() { @@ -631,26 +703,32 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Fetch the entire URI string + * Fetch URI string * - * @return string + * @return string CI_URI::$uri_string */ public function uri_string() { return $this->uri_string; } - // -------------------------------------------------------------------- /** - * Fetch the entire Re-routed URI string + * Fetch Re-routed URI string * * @return string */ public function ruri_string() { - return implode('/', $this->rsegment_array()); + global $RTR; + + if (($dir = $RTR->fetch_directory()) === '/') + { + $dir = ''; + } + + return $dir.implode('/', $this->rsegment_array()); } } diff --git a/system/core/Utf8.php b/system/core/Utf8.php index 0a7ec501c..5bc2dd5c9 100644 --- a/system/core/Utf8.php +++ b/system/core/Utf8.php @@ -1,4 +1,4 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * @@ -24,6 +24,7 @@ * @since Version 2.0 * @filesource */ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * Utf8 Class @@ -39,9 +40,9 @@ class CI_Utf8 { /** - * Constructor + * Class constructor * - * Determines if UTF-8 support is to be enabled + * Determines if UTF-8 support is to be enabled. * * @return void */ @@ -49,30 +50,30 @@ class CI_Utf8 { { log_message('debug', 'Utf8 Class Initialized'); - global $CFG; + $charset = strtoupper(config_item('charset')); + + // set internal encoding for multibyte string functions if necessary + // and set a flag so we don't have to repeatedly use extension_loaded() + // or function_exists() + if (extension_loaded('mbstring')) + { + define('MB_ENABLED', TRUE); + mb_internal_encoding($charset); + } + else + { + define('MB_ENABLED', FALSE); + } if ( - @preg_match('/./u', 'é') === 1 // PCRE must support UTF-8 - && function_exists('iconv') // iconv must be installed - && (bool) @ini_get('mbstring.func_overload') !== TRUE // Multibyte string function overloading cannot be enabled - && $CFG->item('charset') === 'UTF-8' // Application charset must be UTF-8 + @preg_match('/./u', 'é') === 1 // PCRE must support UTF-8 + && function_exists('iconv') // iconv must be installed + && MB_ENABLED === TRUE // mbstring must be enabled + && $charset === 'UTF-8' // Application charset must be UTF-8 ) { define('UTF8_ENABLED', TRUE); log_message('debug', 'UTF-8 Support Enabled'); - - // set internal encoding for multibyte string functions if necessary - // and set a flag so we don't have to repeatedly use extension_loaded() - // or function_exists() - if (extension_loaded('mbstring')) - { - define('MB_ENABLED', TRUE); - mb_internal_encoding('UTF-8'); - } - else - { - define('MB_ENABLED', FALSE); - } } else { @@ -86,9 +87,11 @@ class CI_Utf8 { /** * Clean UTF-8 strings * - * Ensures strings are UTF-8 + * Ensures strings contain only valid UTF-8 characters. * - * @param string + * @uses CI_Utf8::_is_ascii() Decide whether a conversion is needed + * + * @param string $str String to clean * @return string */ public function clean_string($str) @@ -108,9 +111,9 @@ class CI_Utf8 { * * Removes all ASCII control characters except horizontal tabs, * line feeds, and carriage returns, as all others can cause - * problems in XML + * problems in XML. * - * @param string + * @param string $str String to clean * @return string */ public function safe_ascii_for_xml($str) @@ -123,11 +126,11 @@ class CI_Utf8 { /** * Convert to UTF-8 * - * Attempts to convert a string to UTF-8 + * Attempts to convert a string to UTF-8. * - * @param string - * @param string input encoding - * @return string + * @param string $str Input string + * @param string $encoding Input encoding + * @return string $str encoded in UTF-8 or FALSE on failure */ public function convert_to_utf8($str, $encoding) { @@ -135,7 +138,7 @@ class CI_Utf8 { { return @iconv($encoding, 'UTF-8', $str); } - elseif (function_exists('mb_convert_encoding')) + elseif (MB_ENABLED === TRUE) { return @mb_convert_encoding($str, 'UTF-8', $encoding); } @@ -148,9 +151,9 @@ class CI_Utf8 { /** * Is ASCII? * - * Tests if a string is standard 7-bit ASCII or not + * Tests if a string is standard 7-bit ASCII or not. * - * @param string + * @param string $str String to check * @return bool */ protected function _is_ascii($str) |