diff options
Diffstat (limited to 'system/core')
-rw-r--r-- | system/core/CodeIgniter.php | 41 | ||||
-rw-r--r-- | system/core/Common.php | 174 | ||||
-rw-r--r-- | system/core/Config.php | 38 | ||||
-rw-r--r-- | system/core/Exceptions.php | 53 | ||||
-rw-r--r-- | system/core/Input.php | 58 | ||||
-rw-r--r-- | system/core/Loader.php | 130 | ||||
-rw-r--r-- | system/core/Log.php | 3 | ||||
-rw-r--r-- | system/core/Output.php | 181 | ||||
-rw-r--r-- | system/core/Router.php | 41 | ||||
-rw-r--r-- | system/core/Security.php | 53 | ||||
-rw-r--r-- | system/core/URI.php | 19 |
11 files changed, 455 insertions, 336 deletions
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php index c68266408..45c3485bf 100644 --- a/system/core/CodeIgniter.php +++ b/system/core/CodeIgniter.php @@ -48,13 +48,6 @@ defined('BASEPATH') OR exit('No direct script access allowed'); /* * ------------------------------------------------------ - * Load the global functions - * ------------------------------------------------------ - */ - require_once(BASEPATH.'core/Common.php'); - -/* - * ------------------------------------------------------ * Load the framework constants * ------------------------------------------------------ */ @@ -62,10 +55,15 @@ defined('BASEPATH') OR exit('No direct script access allowed'); { require(APPPATH.'config/'.ENVIRONMENT.'/constants.php'); } - else - { - require(APPPATH.'config/constants.php'); - } + + require(APPPATH.'config/constants.php'); + +/* + * ------------------------------------------------------ + * Load the global functions + * ------------------------------------------------------ + */ + require_once(BASEPATH.'core/Common.php'); /* * ------------------------------------------------------ @@ -73,11 +71,10 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * ------------------------------------------------------ */ set_error_handler('_exception_handler'); + register_shutdown_function('_shutdown_handler'); - if ( ! is_php('5.4')) - { - @ini_set('magic_quotes_runtime', 0); // Kill magic quotes - } + // Kill magic quotes + is_php('5.4') OR @ini_set('magic_quotes_runtime', 0); /* * ------------------------------------------------------ @@ -88,7 +85,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * The subclass prefix allows CI to know if a core class is * being extended via a library in the local application * "libraries" folder. Since CI allows config items to be - * overriden via data set in the main index. php file, + * overriden via data set in the main index.php file, * before proceeding we need to know if a subclass_prefix * override exists. If so, we will set this value now, * before any classes are loaded @@ -240,12 +237,13 @@ defined('BASEPATH') OR exit('No direct script access allowed'); // Load the local application controller // Note: The Router class automatically validates the controller path using the router->_validate_request(). // If this include fails it means that the default controller in the Routes.php file is not resolving to something valid. - if ( ! file_exists(APPPATH.'controllers/'.$RTR->directory.$RTR->class.'.php')) + $class = ucfirst($RTR->class); + if ( ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php')) { show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.'); } - include(APPPATH.'controllers/'.$RTR->directory.$RTR->class.'.php'); + include(APPPATH.'controllers/'.$RTR->directory.$class.'.php'); // Set a mark point for benchmarking $BM->mark('loading_time:_base_classes_end'); @@ -257,9 +255,8 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * * 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 methods that begin with an underscore. */ - $class = $RTR->class; $method = $RTR->method; if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method)) @@ -271,6 +268,8 @@ defined('BASEPATH') OR exit('No direct script access allowed'); $method = 'index'; } + $class = ucfirst($class); + if ( ! class_exists($class, FALSE)) { if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) @@ -309,6 +308,8 @@ defined('BASEPATH') OR exit('No direct script access allowed'); $method = 'index'; } + $class = ucfirst($class); + if ( ! class_exists($class, FALSE)) { if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) diff --git a/system/core/Common.php b/system/core/Common.php index b95a05db9..00e303098 100644 --- a/system/core/Common.php +++ b/system/core/Common.php @@ -82,7 +82,7 @@ if ( ! function_exists('is_really_writable')) function is_really_writable($file) { // If we're on a Unix server with safe_mode off we call is_writable - if (DIRECTORY_SEPARATOR === '/' && (bool) @ini_get('safe_mode') === FALSE) + if (DIRECTORY_SEPARATOR === '/' && (is_php('5.4') OR (bool) @ini_get('safe_mode') === FALSE)) { return is_writable($file); } @@ -224,56 +224,51 @@ if ( ! function_exists('get_config')) * @param array * @return array */ - function &get_config($replace = array()) + function &get_config(Array $replace = array()) { static $_config; - if (isset($_config)) + if (empty($_config)) { - return $_config[0]; - } + $file_path = APPPATH.'config/config.php'; + $found = FALSE; + if (file_exists($file_path)) + { + $found = TRUE; + require($file_path); + } - $file_path = APPPATH.'config/config.php'; - $found = FALSE; - if (file_exists($file_path)) - { - $found = TRUE; - require($file_path); - } + // Is the config file in the environment folder? + if (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php')) + { + require($file_path); + } + elseif ( ! $found) + { + set_status_header(503); + echo 'The configuration file does not exist.'; + exit(EXIT_CONFIG); + } - // Is the config file in the environment folder? - if (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php')) - { - require($file_path); - } - elseif ( ! $found) - { - set_status_header(503); - echo 'The configuration file does not exist.'; - exit(EXIT_CONFIG); - } + // Does the $config array exist in the file? + if ( ! isset($config) OR ! is_array($config)) + { + set_status_header(503); + echo 'Your config file does not appear to be formatted correctly.'; + exit(EXIT_CONFIG); + } - // Does the $config array exist in the file? - if ( ! isset($config) OR ! is_array($config)) - { - set_status_header(503); - echo 'Your config file does not appear to be formatted correctly.'; - exit(EXIT_CONFIG); + // references cannot be directly assigned to static variables, so we use an array + $_config[0] =& $config; } - // Are any values being dynamically replaced? - if (count($replace) > 0) + // Are any values being dynamically added or replaced? + foreach ($replace as $key => $val) { - foreach ($replace as $key => $val) - { - if (isset($config[$key])) - { - $config[$key] = $val; - } - } + $_config[0][$key] = $val; } - return $_config[0] =& $config; + return $_config[0]; } } @@ -360,6 +355,24 @@ if ( ! function_exists('is_https')) // ------------------------------------------------------------------------ +if ( ! function_exists('is_cli')) +{ + + /** + * Is CLI? + * + * Test to see if a request was made from the command line. + * + * @return bool + */ + function is_cli() + { + return (PHP_SAPI === 'cli' OR defined('STDIN')); + } +} + +// ------------------------------------------------------------------------ + if ( ! function_exists('show_error')) { /** @@ -434,29 +447,19 @@ if ( ! function_exists('log_message')) * * @param string the error level: 'error', 'debug' or 'info' * @param string the error message - * @param bool whether the error is a native PHP error * @return void */ - function log_message($level, $message, $php_error = FALSE) + function log_message($level, $message) { - static $_log, $_log_threshold; - - if ($_log_threshold === NULL) - { - $_log_threshold = config_item('log_threshold'); - } - - if ($_log_threshold === 0) - { - return; - } + static $_log; if ($_log === NULL) { - $_log =& load_class('Log', 'core'); + // references cannot be directly assigned to static variables, so we use an array + $_log[0] =& load_class('Log', 'core'); } - $_log->write_log($level, $message, $php_error); + $_log[0]->write_log($level, $message); } } @@ -538,7 +541,7 @@ if ( ! function_exists('set_status_header')) $server_protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : FALSE; - if (strpos(php_sapi_name(), 'cgi') === 0) + if (strpos(PHP_SAPI, 'cgi') === 0) { header('Status: '.$code.' '.$text, TRUE); } @@ -556,22 +559,35 @@ if ( ! function_exists('_exception_handler')) /** * Exception Handler * - * This is the custom exception handler that is declaired at the top - * of Codeigniter.php. The main reason we use this is to permit + * This is the custom exception handler that is declared at the top + * of CodeIgniter.php. The main reason we use this is to permit * PHP errors to be logged in our own log files since the user may * not have access to server logs. Since this function * effectively intercepts PHP errors, however, we also need * to display errors based on the current error_reporting level. * We do that with the use of a PHP error template. * - * @param int - * @param string - * @param string - * @param int + * @param int $severity + * @param string $message + * @param string $filepath + * @param int $line * @return void */ function _exception_handler($severity, $message, $filepath, $line) { + $is_error = (((E_ERROR | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $severity) === $severity); + + // When an error occurred, set the status header to '500 Internal Server Error' + // to indicate to the client something went wrong. + // This can't be done within the $_error->show_php_error method because + // it is only called when the display_errors flag is set (which isn't usually + // the case in a production environment) or when errors are ignored because + // they are above the error_reporting threshold. + if ($is_error) + { + set_status_header(500); + } + $_error =& load_class('Exceptions', 'core'); // Should we ignore the error? We'll get the current error_reporting @@ -588,6 +604,42 @@ if ( ! function_exists('_exception_handler')) } $_error->log_exception($severity, $message, $filepath, $line); + + // If the error is fatal, the execution of the script should be stopped because + // errors can't be recovered from. Halting the script conforms with PHP's + // default error handling. See http://www.php.net/manual/en/errorfunc.constants.php + if ($is_error) + { + exit(EXIT_ERROR); + } + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('_shutdown_handler')) +{ + /** + * Shutdown Handler + * + * This is the shutdown handler that is declared at the top + * of CodeIgniter.php. The main reason we use this is to simulate + * a complete custom exception handler. + * + * E_STRICT is purposivly neglected because such events may have + * been caught. Duplication or none? None is preferred for now. + * + * @link http://insomanic.me.uk/post/229851073/php-trick-catching-fatal-errors-e-error-with-a + * @return void + */ + function _shutdown_handler() + { + $last_error = error_get_last(); + if (isset($last_error) && + ($last_error['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING))) + { + _exception_handler($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); + } } } diff --git a/system/core/Config.php b/system/core/Config.php index 7e64444bc..a0e830abe 100644 --- a/system/core/Config.php +++ b/system/core/Config.php @@ -184,16 +184,16 @@ class CI_Config { * * @param string $item Config item name * @param string $index Index name - * @return string|bool The configuration item or FALSE on failure + * @return string|null The configuration item or NULL if the item doesn't exist */ public function item($item, $index = '') { if ($index == '') { - return isset($this->config[$item]) ? $this->config[$item] : FALSE; + return isset($this->config[$item]) ? $this->config[$item] : NULL; } - return isset($this->config[$index], $this->config[$index][$item]) ? $this->config[$index][$item] : FALSE; + return isset($this->config[$index], $this->config[$index][$item]) ? $this->config[$index][$item] : NULL; } // -------------------------------------------------------------------- @@ -202,13 +202,13 @@ class CI_Config { * Fetch a config file item with slash appended (if not empty) * * @param string $item Config item name - * @return string|bool The configuration item or FALSE on failure + * @return string|null The configuration item or NULL if the item doesn't exist */ public function slash_item($item) { if ( ! isset($this->config[$item])) { - return FALSE; + return NULL; } elseif (trim($this->config[$item]) === '') { @@ -228,13 +228,21 @@ class CI_Config { * @uses CI_Config::_uri_string() * * @param string|string[] $uri URI string or an array of segments + * @param string $protocol * @return string */ - public function site_url($uri = '') + public function site_url($uri = '', $protocol = NULL) { + $base_url = $this->slash_item('base_url'); + + if (isset($protocol)) + { + $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + } + if (empty($uri)) { - return $this->slash_item('base_url').$this->item('index_page'); + return $base_url.$this->item('index_page'); } $uri = $this->_uri_string($uri); @@ -255,14 +263,14 @@ class CI_Config { } } - return $this->slash_item('base_url').$this->slash_item('index_page').$uri; + return $base_url.$this->slash_item('index_page').$uri; } elseif (strpos($uri, '?') === FALSE) { $uri = '?'.$uri; } - return $this->slash_item('base_url').$this->item('index_page').$uri; + return $base_url.$this->item('index_page').$uri; } // ------------------------------------------------------------- @@ -275,11 +283,19 @@ class CI_Config { * @uses CI_Config::_uri_string() * * @param string|string[] $uri URI string or an array of segments + * @param string $protocol * @return string */ - public function base_url($uri = '') + public function base_url($uri = '', $protocol = NULL) { - return $this->slash_item('base_url').ltrim($this->_uri_string($uri), '/'); + $base_url = $this->slash_item('base_url'); + + if (isset($protocol)) + { + $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + } + + return $base_url.ltrim($this->_uri_string($uri), '/'); } // ------------------------------------------------------------- diff --git a/system/core/Exceptions.php b/system/core/Exceptions.php index 9c68d06a5..809dc027a 100644 --- a/system/core/Exceptions.php +++ b/system/core/Exceptions.php @@ -91,7 +91,7 @@ class CI_Exceptions { public function log_exception($severity, $message, $filepath, $line) { $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity; - log_message('error', 'Severity: '.$severity.' --> '.$message. ' '.$filepath.' '.$line, TRUE); + log_message('error', 'Severity: '.$severity.' --> '.$message.' '.$filepath.' '.$line); } // -------------------------------------------------------------------- @@ -107,13 +107,21 @@ class CI_Exceptions { */ public function show_404($page = '', $log_error = TRUE) { - $heading = '404 Page Not Found'; - $message = 'The page you requested was not found.'; + if (is_cli()) + { + $heading = 'Not Found'; + $message = 'The controller/method pair you requested was not found.'; + } + else + { + $heading = '404 Page Not Found'; + $message = 'The page you requested was not found.'; + } // By default we log this, but allow a dev to skip it if ($log_error) { - log_message('error', '404 Page Not Found --> '.$page); + log_message('error', $heading.': '.$page); } echo $this->show_error($heading, $message, 'error_404', 404); @@ -137,16 +145,24 @@ class CI_Exceptions { */ public function show_error($heading, $message, $template = 'error_general', $status_code = 500) { - set_status_header($status_code); - - $message = '<p>'.implode('</p><p>', is_array($message) ? $message : array($message)).'</p>'; + if (is_cli()) + { + $message = "\t".(is_array($message) ? implode("\n\t", $message) : $message); + $template = 'cli'.DIRECTORY_SEPARATOR.$template; + } + else + { + set_status_header($status_code); + $message = '<p>'.(is_array($message) ? implode('</p><p>', $message) : $message).'</p>'; + $template = 'html'.DIRECTORY_SEPARATOR.$template; + } if (ob_get_level() > $this->ob_level + 1) { ob_end_flush(); } ob_start(); - include(VIEWPATH.'errors/'.$template.'.php'); + include(VIEWPATH.'errors'.DIRECTORY_SEPARATOR.$template.'.php'); $buffer = ob_get_contents(); ob_end_clean(); return $buffer; @@ -166,13 +182,22 @@ class CI_Exceptions { public function show_php_error($severity, $message, $filepath, $line) { $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity; - $filepath = str_replace('\\', '/', $filepath); - // For safety reasons we do not show the full file path - if (FALSE !== strpos($filepath, '/')) + // For safety reasons we don't show the full file path in non-CLI requests + if ( ! is_cli()) + { + $filepath = str_replace('\\', '/', $filepath); + if (FALSE !== strpos($filepath, '/')) + { + $x = explode('/', $filepath); + $filepath = $x[count($x)-2].'/'.end($x); + } + + $template = 'html'.DIRECTORY_SEPARATOR.'error_php'; + } + else { - $x = explode('/', $filepath); - $filepath = $x[count($x)-2].'/'.end($x); + $template = 'cli'.DIRECTORY_SEPARATOR.'error_php'; } if (ob_get_level() > $this->ob_level + 1) @@ -180,7 +205,7 @@ class CI_Exceptions { ob_end_flush(); } ob_start(); - include(VIEWPATH.'errors/error_php.php'); + include(VIEWPATH.'errors'.DIRECTORY_SEPARATOR.$template.'.php'); $buffer = ob_get_contents(); ob_end_clean(); echo $buffer; diff --git a/system/core/Input.php b/system/core/Input.php index 0ef81128e..384e30d43 100644 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -47,7 +47,7 @@ class CI_Input { public $ip_address = FALSE; /** - * User agent strin + * User agent string * * @var string */ @@ -261,7 +261,7 @@ class CI_Input { * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ - public function get_post($index = '', $xss_clean = FALSE) + public function post_get($index = '', $xss_clean = FALSE) { return isset($_POST[$index]) ? $this->post($index, $xss_clean) @@ -271,6 +271,22 @@ class CI_Input { // -------------------------------------------------------------------- /** + * Fetch an item from GET data with fallback to POST + * + * @param string $index Index for item to be fetched from $_GET or $_POST + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + public function get_post($index = '', $xss_clean = FALSE) + { + return isset($_GET[$index]) + ? $this->get($index, $xss_clean) + : $this->post($index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** * Fetch an item from the COOKIE array * * @param string $index Index for item to be fetched from $_COOKIE @@ -677,7 +693,14 @@ class CI_Input { foreach ($_COOKIE as $key => $val) { - $_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); + if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE) + { + $_COOKIE[$cookie_key] = $this->_clean_input_data($val); + } + else + { + unset($_COOKIE[$key]); + } } } @@ -685,12 +708,12 @@ class CI_Input { $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']); // CSRF Protection check - if ($this->_enable_csrf === TRUE && ! $this->is_cli_request()) + if ($this->_enable_csrf === TRUE && ! is_cli()) { $this->security->csrf_verify(); } - log_message('debug', 'Global POST and COOKIE data sanitized'); + log_message('debug', 'Global POST, GET and COOKIE data sanitized'); } // -------------------------------------------------------------------- @@ -760,15 +783,25 @@ class CI_Input { * only named with alpha-numeric text and a few other items. * * @param string $str Input string - * @return string + * @param string $fatal Whether to terminate script exection + * or to return FALSE if an invalid + * key is encountered + * @return string|bool */ - protected function _clean_input_keys($str) + protected function _clean_input_keys($str, $fatal = TRUE) { if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str)) { - set_status_header(503); - echo 'Disallowed Key Characters.'; - exit(EXIT_USER_INPUT); + if ($fatal === TRUE) + { + return FALSE; + } + else + { + set_status_header(503); + echo 'Disallowed Key Characters.'; + exit(EXIT_USER_INPUT); + } } // Clean UTF-8 if supported @@ -868,11 +901,12 @@ class CI_Input { * * Test to see if a request was made from the command line. * - * @return bool + * @deprecated 3.0.0 Use is_cli() instead + * @return bool */ public function is_cli_request() { - return (php_sapi_name() === 'cli' OR defined('STDIN')); + return is_cli(); } // -------------------------------------------------------------------- diff --git a/system/core/Loader.php b/system/core/Loader.php index 70a6b6fa6..70c1e4154 100644 --- a/system/core/Loader.php +++ b/system/core/Loader.php @@ -76,13 +76,6 @@ class CI_Loader { protected $_ci_helper_paths = array(APPPATH, BASEPATH); /** - * List of loaded base classes - * - * @var array - */ - protected $_base_classes = array(); // Set by the controller class - - /** * List of cached variables * * @var array @@ -120,6 +113,8 @@ class CI_Loader { 'user_agent' => 'agent' ); + // -------------------------------------------------------------------- + /** * Class constructor * @@ -129,7 +124,8 @@ class CI_Loader { */ public function __construct() { - $this->_ci_ob_level = ob_get_level(); + $this->_ci_ob_level = ob_get_level(); + $this->_ci_classes =& is_loaded(); log_message('debug', 'Loader Class Initialized'); } @@ -147,7 +143,6 @@ class CI_Loader { */ public function initialize() { - $this->_base_classes =& is_loaded(); $this->_ci_autoloader(); } @@ -165,7 +160,7 @@ class CI_Loader { */ public function is_loaded($class) { - return isset($this->_ci_classes[$class]) ? $this->_ci_classes[$class] : FALSE; + return array_search(ucfirst($class), $this->_ci_classes, TRUE); } // -------------------------------------------------------------------- @@ -183,7 +178,11 @@ class CI_Loader { */ public function library($library = '', $params = NULL, $object_name = NULL) { - if (is_array($library)) + if (empty($library)) + { + return; + } + elseif (is_array($library)) { foreach ($library as $class) { @@ -193,11 +192,6 @@ class CI_Loader { return; } - if ($library === '' OR isset($this->_base_classes[$library])) - { - return; - } - if ($params !== NULL && ! is_array($params)) { $params = NULL; @@ -228,7 +222,7 @@ class CI_Loader { { foreach ($model as $key => $value) { - $this->model(is_int($key) ? $value : $key, $value); + is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn); } return; } @@ -261,33 +255,32 @@ class CI_Loader { show_error('The model name you are loading is the name of a resource that is already being used: '.$name); } - $model = strtolower($model); - - foreach ($this->_ci_model_paths as $mod_path) + if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE)) { - if ( ! file_exists($mod_path.'models/'.$path.$model.'.php')) + if ($db_conn === TRUE) { - continue; + $db_conn = ''; } - if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE)) - { - if ($db_conn === TRUE) - { - $db_conn = ''; - } + $CI->load->database($db_conn, FALSE, TRUE); + } - $CI->load->database($db_conn, FALSE, TRUE); - } + if ( ! class_exists('CI_Model', FALSE)) + { + load_class('Model', 'core'); + } - if ( ! class_exists('CI_Model', FALSE)) + $model = ucfirst(strtolower($model)); + + foreach ($this->_ci_model_paths as $mod_path) + { + if ( ! file_exists($mod_path.'models/'.$path.$model.'.php')) { - load_class('Model', 'core'); + continue; } require_once($mod_path.'models/'.$path.$model.'.php'); - $model = ucfirst($model); $CI->$name = new $model(); $this->_ci_models[] = $name; return; @@ -422,7 +415,7 @@ class CI_Loader { * 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 + * @return void|string */ public function view($view, $vars = array(), $return = FALSE) { @@ -478,6 +471,20 @@ class CI_Loader { // -------------------------------------------------------------------- /** + * Clear Cached Variables + * + * Clears the cached variables. + * + * @return void + */ + public function clear_vars() + { + $this->_ci_cached_vars = array(); + } + + // -------------------------------------------------------------------- + + /** * Get Variable * * Check if a variable is set and retrieve it. @@ -1118,30 +1125,35 @@ class CI_Loader { // Set the variable name we will assign the class to // Was a custom class name supplied? If so we'll use it - $class = strtolower($class); - - if ($object_name === NULL) + if (empty($object_name)) { - $classvar = isset($this->_ci_varmap[$class]) ? $this->_ci_varmap[$class] : $class; + $object_name = strtolower($class); + if (isset($this->_ci_varmap[$object_name])) + { + $object_name = $this->_ci_varmap[$object_name]; + } } - else + + // Don't overwrite existing properties + $CI =& get_instance(); + if (isset($CI->$object_name)) { - $classvar = $object_name; + if ($CI->$object_name instanceof $name) + { + log_message('debug', $class." has already been instantiated as '".$object_name."'. Second attempt aborted."); + return; + } + + show_error("Resource '".$object_name."' already exists and is not a ".$class." instance."); } // Save the class name and object name - $this->_ci_classes[$class] = $classvar; + $this->_ci_classes[$object_name] = $class; // Instantiate the class - $CI =& get_instance(); - if ($config !== NULL) - { - $CI->$classvar = new $name($config); - } - else - { - $CI->$classvar = new $name(); - } + $CI->$object_name = isset($config) + ? new $name($config) + : new $name(); } // -------------------------------------------------------------------- @@ -1198,6 +1210,15 @@ class CI_Loader { } } + // Autoload drivers + if (isset($autoload['drivers'])) + { + foreach ($autoload['drivers'] as $item) + { + $this->driver($item); + } + } + // Load libraries if (isset($autoload['libraries']) && count($autoload['libraries']) > 0) { @@ -1215,15 +1236,6 @@ class CI_Loader { } } - // Autoload drivers - if (isset($autoload['drivers'])) - { - foreach ($autoload['drivers'] as $item) - { - $this->driver($item); - } - } - // Autoload models if (isset($autoload['model'])) { diff --git a/system/core/Log.php b/system/core/Log.php index e4d72b544..b2327b8f0 100644 --- a/system/core/Log.php +++ b/system/core/Log.php @@ -140,10 +140,9 @@ class CI_Log { * * @param string the error level: 'error', 'debug' or 'info' * @param string the error message - * @param bool whether the error is a native PHP error * @return bool */ - public function write_log($level, $msg, $php_error = FALSE) + public function write_log($level, $msg) { if ($this->_enabled === FALSE) { diff --git a/system/core/Output.php b/system/core/Output.php index 06d7a866b..10332f0d8 100644 --- a/system/core/Output.php +++ b/system/core/Output.php @@ -701,7 +701,7 @@ class CI_Output { else { header('Pragma: public'); - header('Cache-Control: max-age=' . $max_age . ', public'); + header('Cache-Control: max-age='.$max_age.', public'); header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT'); header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT'); } @@ -740,13 +740,13 @@ class CI_Output { preg_match_all('{<style.+</style>}msU', $output, $style_clean); foreach ($style_clean[0] as $s) { - $output = str_replace($s, $this->_minify_script_style($s, TRUE), $output); + $output = str_replace($s, $this->_minify_js_css($s, 'css', TRUE), $output); } // Minify the javascript in <script> tags. foreach ($javascript_clean[0] as $s) { - $javascript_mini[] = $this->_minify_script_style($s, TRUE); + $javascript_mini[] = $this->_minify_js_css($s, 'js', TRUE); } // Replace multiple spaces with a single space. @@ -792,13 +792,14 @@ class CI_Output { break; case 'text/css': + + return $this->_minify_js_css($output, 'css'); + case 'text/javascript': case 'application/javascript': case 'application/x-javascript': - $output = $this->_minify_script_style($output); - - break; + return $this->_minify_js_css($output, 'js'); default: break; } @@ -809,134 +810,100 @@ class CI_Output { // -------------------------------------------------------------------- /** - * Minify Style and Script - * - * Reduce excessive size of CSS/JavaScript content. To remove spaces this - * script walks the string as an array and determines if the pointer is inside - * a string created by single quotes or double quotes. spaces inside those - * strings are not stripped. Opening and closing tags are severed from - * the string initially and saved without stripping whitespace to preserve - * the tags and any associated properties if tags are present + * Minify JavaScript and CSS code * - * Minification logic/workflow is similar to methods used by Douglas Crockford - * in JSMIN. http://www.crockford.com/javascript/jsmin.html + * Strips comments and excessive whitespace characters * - * KNOWN ISSUE: ending a line with a closing parenthesis ')' and no semicolon - * where there should be one will break the Javascript. New lines after a - * closing parenthesis are not recognized by the script. For best results - * be sure to terminate lines with a semicolon when appropriate. - * - * @param string $output Output to minify - * @param bool $has_tags Specify if the output has style or script tags - * @return string Minified output + * @param string $output + * @param string $type 'js' or 'css' + * @param bool $tags Whether $output contains the 'script' or 'style' tag + * @return string */ - protected function _minify_script_style($output, $has_tags = FALSE) + protected function _minify_js_css($output, $type, $tags = FALSE) { - // We only need this if there are tags in the file - if ($has_tags === TRUE) + if ($tags === TRUE) { - // Remove opening tag and save for later - $pos = strpos($output, '>') + 1; - $open_tag = substr($output, 0, $pos); - $output = substr_replace($output, '', 0, $pos); + $tags = array('close' => strrchr($output, '<')); - // Remove closing tag and save it for later - $pos = strpos($output, '</'); - $closing_tag = substr($output, $pos, strlen($output)); - $output = substr_replace($output, '', $pos); - } + $open_length = strpos($output, '>') + 1; + $tags['open'] = substr($output, 0, $open_length); - // Remove CSS comments - $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!i', '', $output); + $output = substr($output, $open_length, -strlen($tags['close'])); - // Remove spaces around curly brackets, colons, - // semi-colons, parenthesis, commas - $chunks = preg_split('/([\'|"]).+(?![^\\\]\\1)\\1/iU', $output, -1, PREG_SPLIT_OFFSET_CAPTURE); - for ($i = count($chunks) - 1; $i >= 0; $i--) - { - $output = substr_replace( - $output, - preg_replace('/\s*(:|;|,|}|{|\(|\))\s*/i', '$1', $chunks[$i][0]), - $chunks[$i][1], - strlen($chunks[$i][0]) - ); + // Strip spaces from the tags + $tags = preg_replace('#\s{2,}#', ' ', $tags); } - // Replace tabs with spaces - // Replace carriage returns & multiple new lines with single new line - // and trim any leading or trailing whitespace - $output = trim(preg_replace(array('/\t+/', '/\r/', '/\n+/'), array(' ', "\n", "\n"), $output)); + $output = trim($output); - // Remove spaces when safe to do so. - $in_string = $in_dstring = $prev = FALSE; - $array_output = str_split($output); - foreach ($array_output as $key => $value) + if ($type === 'js') { - if ($in_string === FALSE && $in_dstring === FALSE) + // Catch all string literals and comment blocks + if (preg_match_all('#((?:((?<!\\\)\'|")|(/\*)|(//)).*(?(2)(?<!\\\)\2|(?(3)\*/|\n)))#msuUS', $output, $match, PREG_OFFSET_CAPTURE)) { - if ($value === ' ') + $js_literals = $js_code = array(); + for ($match = $match[0], $c = count($match), $i = $pos = $offset = 0; $i < $c; $i++) { - // Get the next element in the array for comparisons - $next = $array_output[$key + 1]; - - // Strip spaces preceded/followed by a non-ASCII character - // or not preceded/followed by an alphanumeric - // or not preceded/followed \ $ and _ - if ((preg_match('/^[\x20-\x7f]*$/D', $next) OR preg_match('/^[\x20-\x7f]*$/D', $prev)) - && ( ! ctype_alnum($next) OR ! ctype_alnum($prev)) - && ! in_array($next, array('\\', '_', '$'), TRUE) - && ! in_array($prev, array('\\', '_', '$'), TRUE) - ) + $js_code[$pos++] = trim(substr($output, $offset, $match[$i][1] - $offset)); + $offset = $match[$i][1] + strlen($match[$i][0]); + + // Save only if we haven't matched a comment block + if ($match[$i][0][0] !== '/') { - unset($array_output[$key]); + $js_literals[$pos++] = array_shift($match[$i]); } } - else - { - // Save this value as previous for the next iteration - // if it is not a blank space - $prev = $value; - } - } + $js_code[$pos] = substr($output, $offset); - if ($value === "'") - { - $in_string = ! $in_string; + // $match might be quite large, so free it up together with other vars that we no longer need + unset($match, $offset, $pos); } - elseif ($value === '"') + else { - $in_dstring = ! $in_dstring; + $js_code = array($output); + $js_literals = array(); } + + $varname = 'js_code'; + } + else + { + $varname = 'output'; } - // Put the string back together after spaces have been stripped - $output = implode($array_output); + // Standartize new lines + $$varname = str_replace(array("\r\n", "\r"), "\n", $$varname); - // Remove new line characters unless previous or next character is - // printable or Non-ASCII - preg_match_all('/[\n]/', $output, $lf, PREG_OFFSET_CAPTURE); - $removed_lf = 0; - foreach ($lf as $feed_position) + if ($type === 'js') { - foreach ($feed_position as $position) - { - $position = $position[1] - $removed_lf; - $next = $output[$position + 1]; - $prev = $output[$position - 1]; - if ( ! ctype_print($next) && ! ctype_print($prev) - && ! preg_match('/^[\x20-\x7f]*$/D', $next) - && ! preg_match('/^[\x20-\x7f]*$/D', $prev) - ) - { - $output = substr_replace($output, '', $position, 1); - $removed_lf++; - } - } + $patterns = array( + '#\s*([!\#%&()*+,\-./:;<=>?@\[\]^`{|}~])\s*#' => '$1', // Remove spaces following and preceeding JS-wise non-special & non-word characters + '#\s{2,}#' => ' ' // Reduce the remaining multiple whitespace characters to a single space + ); + } + else + { + $patterns = array( + '#/\*.*(?=\*/)\*/#s' => '', // Remove /* block comments */ + '#\n?//[^\n]*#' => '', // Remove // line comments + '#\s*([^\w.\#%])\s*#U' => '$1', // Remove spaces following and preceeding non-word characters, excluding dots, hashes and the percent sign + '#\s{2,}#' => ' ' // Reduce the remaining multiple space characters to a single space + ); + } + + $$varname = preg_replace(array_keys($patterns), array_values($patterns), $$varname); + + // Glue back JS quoted strings + if ($type === 'js') + { + $js_code += $js_literals; + ksort($js_code); + $output = implode($js_code); + unset($js_code, $js_literals, $varname, $patterns); } - // Put the opening and closing tags back if applicable - return isset($open_tag) - ? $open_tag.$output.$closing_tag + return is_array($tags) + ? $tags['open'].$output.$tags['close'] : $output; } diff --git a/system/core/Router.php b/system/core/Router.php index cc3916f86..d467d60fd 100644 --- a/system/core/Router.php +++ b/system/core/Router.php @@ -270,8 +270,7 @@ class CI_Router { return $segments; } - $test = ($this->translate_uri_dashes === TRUE) - ? str_replace('-', '_', $segments[0]) : $segments[0]; + $test = ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]); // Does the requested controller exist in the root folder? if (file_exists(APPPATH.'controllers/'.$test.'.php')) @@ -286,8 +285,7 @@ class CI_Router { $this->set_directory(array_shift($segments)); if (count($segments) > 0) { - $test = ($this->translate_uri_dashes === TRUE) - ? str_replace('-', '_', $segments[0]) : $segments[0]; + $test = ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]); // Does the requested controller exist in the sub-directory? if ( ! file_exists(APPPATH.'controllers/'.$this->directory.$test.'.php')) @@ -307,7 +305,7 @@ class CI_Router { { // Is the method being specified in the route? $segments = explode('/', $this->default_controller); - if ( ! file_exists(APPPATH.'controllers/'.$this->directory.$segments[0].'.php')) + if ( ! file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($segments[0]).'.php')) { $this->directory = ''; } @@ -347,16 +345,41 @@ class CI_Router { // Turn the segment array into a URI string $uri = implode('/', $this->uri->segments); + // Get HTTP verb + $http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli'; + // Is there a literal match? If so we're done - if (isset($this->routes[$uri]) && is_string($this->routes[$uri])) + if (isset($this->routes[$uri])) { - return $this->_set_request(explode('/', $this->routes[$uri])); + // Check default routes format + if (is_string($this->routes[$uri])) + { + return $this->_set_request(explode('/', $this->routes[$uri])); + } + // Is there a matching http verb? + elseif (is_array($this->routes[$uri]) && isset($this->routes[$uri][$http_verb])) + { + return $this->_set_request(explode('/', $this->routes[$uri][$http_verb])); + } } - // Loop through the route array looking for wild-cards + // Loop through the route array looking for wildcards foreach ($this->routes as $key => $val) { - // Convert wild-cards to RegEx + // Check if route format is using http verb + if (is_array($val)) + { + if (isset($val[$http_verb])) + { + $val = $val[$http_verb]; + } + else + { + continue; + } + } + + // Convert wildcards to RegEx $key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key); // Does the RegEx match? diff --git a/system/core/Security.php b/system/core/Security.php index 196d61144..9423f825c 100644 --- a/system/core/Security.php +++ b/system/core/Security.php @@ -38,6 +38,30 @@ defined('BASEPATH') OR exit('No direct script access allowed'); class CI_Security { /** + * List of sanitize filename strings + * + * @var array + */ + public $filename_bad_chars = array( + '../', '<!--', '-->', '<', '>', + "'", '"', '&', '$', '#', + '{', '}', '[', ']', '=', + ';', '?', '%20', '%22', + '%3c', // < + '%253c', // < + '%3e', // > + '%0e', // > + '%28', // ( + '%29', // ) + '%2528', // ( + '%26', // & + '%24', // $ + '%3f', // ? + '%3b', // ; + '%3d' // = + ); + + /** * XSS Hash * * Random Hash for protecting URLs. @@ -529,9 +553,9 @@ class CI_Security { { $matches = $matches1 = 0; + $str = preg_replace('~(�*[0-9a-f]{2,5});?~iS', '$1;', $str, -1, $matches); + $str = preg_replace('~(&#\d{2,4});?~S', '$1;', $str, -1, $matches1); $str = html_entity_decode($str, ENT_COMPAT, $charset); - $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str, -1, $matches); - $str = preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str, -1, $matches1); } while ($matches OR $matches1); @@ -549,24 +573,7 @@ class CI_Security { */ public function sanitize_filename($str, $relative_path = FALSE) { - $bad = array( - '../', '<!--', '-->', '<', '>', - "'", '"', '&', '$', '#', - '{', '}', '[', ']', '=', - ';', '?', '%20', '%22', - '%3c', // < - '%253c', // < - '%3e', // > - '%0e', // > - '%28', // ( - '%29', // ) - '%2528', // ( - '%26', // & - '%24', // $ - '%3f', // ? - '%3b', // ; - '%3d' // = - ); + $bad = $this->filename_bad_chars; if ( ! $relative_path) { @@ -596,7 +603,7 @@ class CI_Security { */ public function strip_image_tags($str) { - return preg_replace(array('#<img\s+.*?src\s*=\s*["\'](.+?)["\'].*?\>#', '#<img\s+.*?src\s*=\s*(.+?).*?\>#'), '\\1', $str); + return preg_replace(array('#<img[\s/]+.*?src\s*=\s*["\'](.+?)["\'].*?\>#', '#<img[\s/]+.*?src\s*=\s*(.+?).*?\>#'), '\\1', $str); } // ---------------------------------------------------------------- @@ -877,7 +884,7 @@ class CI_Security { { if ($this->_csrf_hash === '') { - // If the cookie exists we will use it's value. + // If the cookie exists we will use its value. // We don't necessarily want to regenerate it with // each page load since a page could contain embedded // sub-pages causing this feature to fail @@ -887,7 +894,7 @@ class CI_Security { return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name]; } - $this->_csrf_hash = md5(uniqid(rand(), TRUE)); + $this->_csrf_hash = md5(uniqid(mt_rand(), TRUE)); $this->csrf_set_cookie(); } diff --git a/system/core/URI.php b/system/core/URI.php index bc086d223..bad9985d7 100644 --- a/system/core/URI.php +++ b/system/core/URI.php @@ -99,7 +99,7 @@ class CI_URI { if ($protocol === 'AUTO') { // Is the request coming from the command line? - if ($this->_is_cli_request()) + if (is_cli()) { $this->_set_uri_string($this->_parse_argv()); return; @@ -280,23 +280,6 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Is CLI Request? - * - * 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() - { - return (PHP_SAPI === 'cli') OR defined('STDIN'); - } - - // -------------------------------------------------------------------- - - /** * Parse CLI arguments * * Take each command line argument and assume it is a URI segment. |