diff options
44 files changed, 832 insertions, 281 deletions
diff --git a/.travis.yml b/.travis.yml index a1635eaa8..070b23c8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ php: env: - DB=mysql + - DB=mysqli - DB=pgsql - DB=sqlite - DB=pdo/mysql @@ -17,7 +18,7 @@ before_script: - php composer.phar install - sh -c "if [ '$DB' = 'pgsql' ] || [ '$DB' = 'pdo/pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS ci_test;' -U postgres; fi" - sh -c "if [ '$DB' = 'pgsql' ] || [ '$DB' = 'pdo/pgsql' ]; then psql -c 'create database ci_test;' -U postgres; fi" - - sh -c "if [ '$DB' = 'mysql' ] || [ '$DB' = 'pdo/mysql' ]; then mysql -e 'create database IF NOT EXISTS ci_test;'; fi" + - sh -c "if [ '$DB' = 'mysql' ] || [ '$DB' = 'mysqli' ] || [ '$DB' = 'pdo/mysql' ]; then mysql -e 'create database IF NOT EXISTS ci_test;'; fi" script: phpunit --coverage-text --configuration tests/travis/$DB.phpunit.xml diff --git a/system/core/Lang.php b/system/core/Lang.php index 5d824cee6..9e6f43716 100644 --- a/system/core/Lang.php +++ b/system/core/Lang.php @@ -96,29 +96,40 @@ 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)) diff --git a/system/core/Loader.php b/system/core/Loader.php index 1e6eafe8a..651507470 100644 --- a/system/core/Loader.php +++ b/system/core/Loader.php @@ -669,6 +669,12 @@ class CI_Loader { return FALSE; } + if ( ! class_exists('CI_Driver_Library')) + { + // 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, // and typically identically named to the library if ( ! strpos($library, '/')) @@ -949,13 +955,6 @@ class CI_Loader { // Get the filename from the path $class = substr($class, $last_slash); - - // Check for match and driver base class - if (strtolower(trim($subdir, '/')) == strtolower($class) && ! class_exists('CI_Driver_Library')) - { - // We aren't instantiating an object here, just making the base class available - require BASEPATH.'libraries/Driver.php'; - } } // We'll test for both lowercase and capitalized versions of the file name diff --git a/system/core/Output.php b/system/core/Output.php index 6312ccd86..98deff55c 100644 --- a/system/core/Output.php +++ b/system/core/Output.php @@ -256,7 +256,7 @@ class CI_Output { { for ($i = 0, $c = count($this->headers); $i < $c; $i++) { - if (sscanf($this->headers[$i][0], 'Content-Type: %s', $content_type) === 1) + if (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1) { return $content_type; } @@ -268,6 +268,39 @@ 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 * * As of version 1.7.2, this is an alias for common function @@ -594,6 +627,45 @@ 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 diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php index bb1ee4809..59c3baf8b 100644 --- a/system/database/DB_forge.php +++ b/system/database/DB_forge.php @@ -402,7 +402,7 @@ abstract class CI_DB_forge { // Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL) if ($this->_create_table_keys === TRUE) { - $columns .= $this->_process_indexes(); + $columns .= $this->_process_indexes($table); } // _create_table will usually have the following format: "%s %s (%s\n)" @@ -962,14 +962,25 @@ abstract class CI_DB_forge { * @param string $table * @return string */ - protected function _process_indexes($table = NULL) + protected function _process_indexes($table) { $table = $this->db->escape_identifiers($table); $sqls = array(); for ($i = 0, $c = count($this->keys); $i < $c; $i++) { - if ( ! isset($this->fields[$this->keys[$i]])) + if (is_array($this->keys[$i])) + { + for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) + { + if ( ! isset($this->fields[$this->keys[$i][$i2]])) + { + unset($this->keys[$i][$i2]); + continue; + } + } + } + elseif ( ! isset($this->fields[$this->keys[$i]])) { unset($this->keys[$i]); continue; diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php index 9bd535b0e..e77fba63d 100644 --- a/system/database/DB_query_builder.php +++ b/system/database/DB_query_builder.php @@ -903,11 +903,12 @@ abstract class CI_DB_query_builder extends CI_DB_driver { } is_bool($escape) OR $escape = $this->_protect_identifiers; - $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) - ? $this->_group_get_type('') : $this->_group_get_type($type); foreach ($field as $k => $v) { + $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) + ? $this->_group_get_type('') : $this->_group_get_type($type); + $v = $this->escape_like_str($v); if ($side === 'none') diff --git a/system/database/DB_utility.php b/system/database/DB_utility.php index a32fd4455..c4140aef3 100644 --- a/system/database/DB_utility.php +++ b/system/database/DB_utility.php @@ -146,7 +146,7 @@ abstract class CI_DB_utility { if ($query !== FALSE) { $query = $query->result_array(); - return current($res); + return current($query); } return FALSE; diff --git a/system/database/drivers/cubrid/cubrid_forge.php b/system/database/drivers/cubrid/cubrid_forge.php index 9a7cdb805..2a737c5ed 100644 --- a/system/database/drivers/cubrid/cubrid_forge.php +++ b/system/database/drivers/cubrid/cubrid_forge.php @@ -179,13 +179,24 @@ class CI_DB_cubrid_forge extends CI_DB_forge { * @param string $table (ignored) * @return string */ - protected function _process_indexes($table = NULL) + protected function _process_indexes($table) { $sql = ''; for ($i = 0, $c = count($this->keys); $i < $c; $i++) { - if ( ! isset($this->fields[$this->keys[$i]])) + if (is_array($this->keys[$i])) + { + for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) + { + if ( ! isset($this->fields[$this->keys[$i][$i2]])) + { + unset($this->keys[$i][$i2]); + continue; + } + } + } + elseif ( ! isset($this->fields[$this->keys[$i]])) { unset($this->keys[$i]); continue; diff --git a/system/database/drivers/mysql/mysql_forge.php b/system/database/drivers/mysql/mysql_forge.php index 1e9145e4f..f4394c685 100644 --- a/system/database/drivers/mysql/mysql_forge.php +++ b/system/database/drivers/mysql/mysql_forge.php @@ -175,13 +175,24 @@ class CI_DB_mysql_forge extends CI_DB_forge { * @param string $table (ignored) * @return string */ - protected function _process_indexes($table = NULL) + protected function _process_indexes($table) { $sql = ''; for ($i = 0, $c = count($this->keys); $i < $c; $i++) { - if ( ! isset($this->fields[$this->keys[$i]])) + if (is_array($this->keys[$i])) + { + for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) + { + if ( ! isset($this->fields[$this->keys[$i][$i2]])) + { + unset($this->keys[$i][$i2]); + continue; + } + } + } + elseif ( ! isset($this->fields[$this->keys[$i]])) { unset($this->keys[$i]); continue; diff --git a/system/database/drivers/mysqli/mysqli_forge.php b/system/database/drivers/mysqli/mysqli_forge.php index 1a6e284ee..f29ea5ade 100644 --- a/system/database/drivers/mysqli/mysqli_forge.php +++ b/system/database/drivers/mysqli/mysqli_forge.php @@ -175,13 +175,24 @@ class CI_DB_mysqli_forge extends CI_DB_forge { * @param string $table (ignored) * @return string */ - protected function _process_indexes($table = NULL) + protected function _process_indexes($table) { $sql = ''; for ($i = 0, $c = count($this->keys); $i < $c; $i++) { - if ( ! isset($this->fields[$this->keys[$i]])) + if (is_array($this->keys[$i])) + { + for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) + { + if ( ! isset($this->fields[$this->keys[$i][$i2]])) + { + unset($this->keys[$i][$i2]); + continue; + } + } + } + elseif ( ! isset($this->fields[$this->keys[$i]])) { unset($this->keys[$i]); continue; diff --git a/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php b/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php index cb7d9e697..ebf4b15d6 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php +++ b/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php @@ -179,13 +179,24 @@ class CI_DB_pdo_cubrid_forge extends CI_DB_pdo_forge { * @param string $table (ignored) * @return string */ - protected function _process_indexes($table = NULL) + protected function _process_indexes($table) { $sql = ''; for ($i = 0, $c = count($this->keys); $i < $c; $i++) { - if ( ! isset($this->fields[$this->keys[$i]])) + if (is_array($this->keys[$i])) + { + for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) + { + if ( ! isset($this->fields[$this->keys[$i][$i2]])) + { + unset($this->keys[$i][$i2]); + continue; + } + } + } + elseif ( ! isset($this->fields[$this->keys[$i]])) { unset($this->keys[$i]); continue; diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php index 85d9445d3..5a6de6b2d 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php +++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php @@ -189,13 +189,24 @@ class CI_DB_pdo_mysql_forge extends CI_DB_pdo_forge { * @param string $table (ignored) * @return string */ - protected function _process_indexes($table = NULL) + protected function _process_indexes($table) { $sql = ''; for ($i = 0, $c = count($this->keys); $i < $c; $i++) { - if ( ! isset($this->fields[$this->keys[$i]])) + if (is_array($this->keys[$i])) + { + for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) + { + if ( ! isset($this->fields[$this->keys[$i][$i2]])) + { + unset($this->keys[$i][$i2]); + continue; + } + } + } + elseif ( ! isset($this->fields[$this->keys[$i]])) { unset($this->keys[$i]); continue; diff --git a/system/helpers/language_helper.php b/system/helpers/language_helper.php index 658be6de7..6d24a9ca6 100644 --- a/system/helpers/language_helper.php +++ b/system/helpers/language_helper.php @@ -45,18 +45,19 @@ if ( ! function_exists('lang')) * * Fetches a language variable and optionally outputs a form label * - * @param string the language line - * @param string the id of the form element + * @param string $line The language line + * @param string $for The "for" value (id of the form element) + * @param array $attributes Any additional HTML attributes * @return string */ - function lang($line, $id = '') + function lang($line, $for = '', $attributes = array()) { $CI =& get_instance(); $line = $CI->lang->line($line); - if ($id !== '') + if ($for !== '') { - $line = '<label for="'.$id.'">'.$line.'</label>'; + $line = '<label for="'.$for.'"'._stringify_attributes($attributes).'>'.$line.'</label>'; } return $line; diff --git a/system/libraries/Cache/Cache.php b/system/libraries/Cache/Cache.php index e76fdc557..48bd9581a 100644 --- a/system/libraries/Cache/Cache.php +++ b/system/libraries/Cache/Cache.php @@ -43,12 +43,12 @@ class CI_Cache extends CI_Driver_Library { * @var array */ protected $valid_drivers = array( - 'cache_apc', - 'cache_dummy', - 'cache_file', - 'cache_memcached', - 'cache_redis', - 'cache_wincache' + 'apc', + 'dummy', + 'file', + 'memcached', + 'redis', + 'wincache' ); /** diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php index 621d22631..8323e8f01 100644 --- a/system/libraries/Driver.php +++ b/system/libraries/Driver.php @@ -60,8 +60,8 @@ class CI_Driver_Library { * The first time a child is used it won't exist, so we instantiate it * subsequents calls will go straight to the proper child. * - * @param string Child class name - * @return object Child class + * @param string Child class name + * @return object Child class */ public function __get($child) { @@ -74,61 +74,109 @@ class CI_Driver_Library { * * Separate load_driver call to support explicit driver load by library or user * - * @param string Child class name - * @return object Child class + * @param string Driver name (w/o parent prefix) + * @return object Child class */ public function load_driver($child) { + // Get CodeIgniter instance and subclass prefix + $CI = get_instance(); + $prefix = (string) $CI->config->item('subclass_prefix'); + if ( ! isset($this->lib_name)) { - $this->lib_name = get_class($this); + // Get library name without any prefix + $this->lib_name = str_replace(array('CI_', $prefix), '', get_class($this)); } - // The class will be prefixed with the parent lib - $child_class = $this->lib_name.'_'.$child; + // The child will be prefixed with the parent lib + $child_name = $this->lib_name.'_'.$child; - // Remove the CI_ prefix and lowercase - $lib_name = ucfirst(strtolower(str_replace('CI_', '', $this->lib_name))); - $driver_name = strtolower(str_replace('CI_', '', $child_class)); + // See if requested child is a valid driver + if ( ! in_array($child, $this->valid_drivers)) + { + // The requested driver isn't valid! + $msg = 'Invalid driver requested: '.$child_name; + log_message('error', $msg); + show_error($msg); + } - if (in_array($driver_name, array_map('strtolower', $this->valid_drivers))) + // Get package paths and filename case variations to search + $paths = $CI->load->get_package_paths(TRUE); + + // Is there an extension? + $class_name = $prefix.$child_name; + $found = class_exists($class_name); + if ( ! $found) { - // check and see if the driver is in a separate file - if ( ! class_exists($child_class)) + // Check for subclass file + foreach ($paths as $path) { - // check application path first - foreach (get_instance()->load->get_package_paths(TRUE) as $path) + // Does the file exist? + $file = $path.'libraries/'.$this->lib_name.'/drivers/'.$prefix.$child_name.'.php'; + if (file_exists($file)) { - // loves me some nesting! - foreach (array(ucfirst($driver_name), $driver_name) as $class) + // Yes - require base class from BASEPATH + $basepath = BASEPATH.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php'; + if ( ! file_exists($basepath)) { - $filepath = $path.'libraries/'.$lib_name.'/drivers/'.$class.'.php'; - - if (file_exists($filepath)) - { - include_once $filepath; - break 2; - } + $msg = 'Unable to load the requested class: CI_'.$child_name; + log_message('error', $msg); + show_error($msg); } + + // Include both sources and mark found + include($basepath); + include($file); + $found = TRUE; + break; } + } + } - // it's a valid driver, but the file simply can't be found - if ( ! class_exists($child_class)) + // Do we need to search for the class? + if ( ! $found) + { + // Use standard class name + $class_name = 'CI_'.$child_name; + $found = class_exists($class_name); + if ( ! $found) + { + // Check package paths + foreach ($paths as $path) { - log_message('error', 'Unable to load the requested driver: '.$child_class); - show_error('Unable to load the requested driver: '.$child_class); + // Does the file exist? + $file = $path.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php'; + if (file_exists($file)) + { + // Include source + include($file); + break; + } } } + } - $obj = new $child_class; - $obj->decorate($this); - $this->$child = $obj; - return $this->$child; + // Did we finally find the class? + if ( ! class_exists($class_name)) + { + if (class_exists($child_name)) + { + $class_name = $child_name; + } + else + { + $msg = 'Unable to load the requested driver: '.$class_name; + log_message('error', $msg); + show_error($msg); + } } - // The requested driver isn't valid! - log_message('error', 'Invalid driver requested: '.$child_class); - show_error('Invalid driver requested: '.$child_class); + // Instantiate, decorate and add child + $obj = new $class_name(); + $obj->decorate($this); + $this->$child = $obj; + return $this->$child; } } diff --git a/system/libraries/Email.php b/system/libraries/Email.php index 92ccde60c..5d8fc8aea 100644 --- a/system/libraries/Email.php +++ b/system/libraries/Email.php @@ -2062,9 +2062,11 @@ class CI_Email { /** * Get Debug Message * + * @param array $include List of raw data chunks to include in the output + * Valid options are: 'headers', 'subject', 'body' * @return string */ - public function print_debugger() + public function print_debugger($include = array('headers', 'subject', 'body')) { $msg = ''; @@ -2076,7 +2078,26 @@ class CI_Email { } } - return $msg.'<pre>'.$this->_header_str."\n".htmlspecialchars($this->_subject)."\n".htmlspecialchars($this->_finalbody).'</pre>'; + // Determine which parts of our raw data needs to be printed + $raw_data = ''; + is_array($include) OR $include = array($include); + + if (in_array('headers', $include, TRUE)) + { + $raw_data = $this->_header_str."\n"; + } + + if (in_array('subject', $include, TRUE)) + { + $raw_data .= htmlspecialchars($this->_subject)."\n"; + } + + if (in_array('body', $include, TRUE)) + { + $raw_data .= htmlspecialchars($this->_finalbody); + } + + return $msg.($raw_data === '' ? '' : '<pre>'.$raw_data.'</pre>'); } // -------------------------------------------------------------------- diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php index d3d22c595..b7bd280ee 100644 --- a/system/libraries/Form_validation.php +++ b/system/libraries/Form_validation.php @@ -105,7 +105,7 @@ class CI_Form_validation { * * @var array */ - protected $validation_data = array(); + public $validation_data = array(); /** * Initialize Form_Validation class @@ -1084,6 +1084,48 @@ class CI_Form_validation { // -------------------------------------------------------------------- /** + * Valid URL + * + * @param string $str + * @return bool + */ + public function valid_url($str) + { + if (empty($str)) + { + return FALSE; + } + elseif (preg_match('/^(?:([^:]*)\:)?\/\/(.+)$/', $str, $matches)) + { + if (empty($matches[2])) + { + return FALSE; + } + elseif ( ! in_array($matches[1], array('http', 'https'), TRUE)) + { + return FALSE; + } + + $str = $matches[2]; + } + + $str = 'http://'.$str; + + // There's a bug affecting PHP 5.2.13, 5.3.2 that considers the + // underscore to be a valid hostname character instead of a dash. + // Reference: https://bugs.php.net/bug.php?id=51192 + if (version_compare(PHP_VERSION, '5.2.13', '==') === 0 OR version_compare(PHP_VERSION, '5.3.2', '==') === 0) + { + sscanf($str, 'http://%[^/]', $host); + $str = substr_replace($str, strtr($host, array('_' => '-', '-' => '_')), 7, strlen($host)); + } + + return (filter_var($str, FILTER_VALIDATE_URL) !== FALSE); + } + + // -------------------------------------------------------------------- + + /** * Valid Email * * @param string diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php index e96791cef..bf2d18e07 100644 --- a/system/libraries/Migration.php +++ b/system/libraries/Migration.php @@ -233,11 +233,6 @@ class CI_Migration { $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class); return FALSE; } - elseif ( ! is_callable(array($class, $method))) - { - $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class); - return FALSE; - } $previous = $number; @@ -247,8 +242,15 @@ class CI_Migration { ($method === 'down' && $number <= $current_version && $number > $target_version) ) { + $instance = new $class(); + if ( ! is_callable(array($instance, $method))) + { + $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class); + return FALSE; + } + log_message('debug', 'Migrating '.$method.' from version '.$current_version.' to version '.$number); - call_user_func(array(new $class, $method)); + call_user_func(array($instance, $method)); $current_version = $number; $this->_update_version($current_version); } diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php index 9b011dea3..85a483592 100644 --- a/system/libraries/Session/Session.php +++ b/system/libraries/Session/Session.php @@ -107,17 +107,15 @@ class CI_Session extends CI_Driver_Library { // Get valid drivers list $this->valid_drivers = array( - 'Session_native', - 'Session_cookie' + 'native', + 'cookie' ); $key = 'sess_valid_drivers'; $drivers = isset($params[$key]) ? $params[$key] : $CI->config->item($key); if ($drivers) { - is_array($drivers) OR $drivers = array($drivers); - // Add driver names to valid list - foreach ($drivers as $driver) + foreach ((array) $drivers as $driver) { if ( ! in_array(strtolower($driver), array_map('strtolower', $this->valid_drivers))) { @@ -134,9 +132,9 @@ class CI_Session extends CI_Driver_Library { $driver = 'cookie'; } - if ( ! in_array('session_'.strtolower($driver), array_map('strtolower', $this->valid_drivers))) + if ( ! in_array(strtolower($driver), array_map('strtolower', $this->valid_drivers))) { - $this->valid_drivers[] = 'Session_'.$driver; + $this->valid_drivers[] = $driver; } // Save a copy of parameters in case drivers need access @@ -178,17 +176,17 @@ class CI_Session extends CI_Driver_Library { /** * Select default session storage driver * - * @param string Driver classname + * @param string Driver name * @return void */ public function select_driver($driver) { // Validate driver name - $lowername = strtolower(str_replace('CI_', '', $driver)); - if (in_array($lowername, array_map('strtolower', $this->valid_drivers))) + $prefix = (string) get_instance()->config->item('subclass_prefix'); + $child = strtolower(str_replace(array('CI_', $prefix, $this->lib_name.'_'), '', $driver)); + if (in_array($child, array_map('strtolower', $this->valid_drivers))) { // See if driver is loaded - $child = str_replace($this->lib_name.'_', '', $driver); if (isset($this->$child)) { // See if driver is already current diff --git a/system/libraries/Session/drivers/Session_cookie.php b/system/libraries/Session/drivers/Session_cookie.php index 3c4848265..6d926ae3d 100644 --- a/system/libraries/Session/drivers/Session_cookie.php +++ b/system/libraries/Session/drivers/Session_cookie.php @@ -219,7 +219,7 @@ class CI_Session_cookie extends CI_Session_driver { : $this->CI->config->item($key); } - if ($this->encryption_key === '') + if (empty($this->encryption_key)) { show_error('In order to use the Cookie Session driver you are required to set an encryption key in your config file.'); } diff --git a/tests/codeigniter/core/Output_test.php b/tests/codeigniter/core/Output_test.php new file mode 100644 index 000000000..d8252403d --- /dev/null +++ b/tests/codeigniter/core/Output_test.php @@ -0,0 +1,37 @@ +<?php + +class Output_test extends CI_TestCase { + + public $output; + + public function set_up() + { + $this->ci_set_config('charset', 'UTF-8'); + $output = $this->ci_core_class('output'); + $this->output = new $output(); + } + + // -------------------------------------------------------------------- + + public function test_get_content_type() + { + $this->assertEquals('text/html', $this->output->get_content_type()); + } + + // -------------------------------------------------------------------- + + public function test_get_header() + { + $this->assertNull($this->output->get_header('Non-Existent-Header')); + + // TODO: Find a way to test header() values as well. Currently, + // PHPUnit prevents this by not using output buffering. + + $this->output->set_content_type('text/plain', 'WINDOWS-1251'); + $this->assertEquals( + 'text/plain; charset=windows-1251', // Character set is converted to lowercase + $this->output->get_header('content-type') // Case-insensitive comparison + ); + } + +}
\ No newline at end of file diff --git a/tests/codeigniter/database/DB_driver_test.php b/tests/codeigniter/database/DB_driver_test.php index 1f48ca984..c04c42b09 100644 --- a/tests/codeigniter/database/DB_driver_test.php +++ b/tests/codeigniter/database/DB_driver_test.php @@ -21,6 +21,11 @@ class DB_driver_test extends CI_TestCase { return new Mock_Database_Drivers_Mysql($config); } + protected function mysqli($config) + { + return new Mock_Database_Drivers_Mysqli($config); + } + protected function sqlite($config) { return new Mock_Database_Drivers_Sqlite($config); diff --git a/tests/codeigniter/helpers/language_helper_test.php b/tests/codeigniter/helpers/language_helper_test.php index 06932b9fd..176da689a 100644 --- a/tests/codeigniter/helpers/language_helper_test.php +++ b/tests/codeigniter/helpers/language_helper_test.php @@ -5,10 +5,12 @@ class Language_helper_test extends CI_TestCase { public function test_lang() { $this->helper('language'); - $this->ci_instance_var('lang', new Mock_Core_Lang()); + $lang = $this->getMock('CI_Lang', array('line')); + $lang->expects($this->any())->method('line')->will($this->returnValue(FALSE)); + $this->ci_instance_var('lang', $lang); $this->assertFalse(lang(1)); - $this->assertEquals('<label for="foo"></label>', lang(1, 'foo')); + $this->assertEquals('<label for="foo" class="bar"></label>', lang(1, 'foo', array('class' => 'bar'))); } }
\ No newline at end of file diff --git a/tests/codeigniter/libraries/Calendar_test.php b/tests/codeigniter/libraries/Calendar_test.php index 95668d70d..952e8a8d2 100644 --- a/tests/codeigniter/libraries/Calendar_test.php +++ b/tests/codeigniter/libraries/Calendar_test.php @@ -2,12 +2,12 @@ class Calendar_test extends CI_TestCase { - function __construct() + function set_up() { - $obj = new stdClass; - $obj->calendar = new Mock_Libraries_Calendar(); - - $this->calendar = $obj->calendar; + $lang = $this->getMock('CI_Lang', array('load', 'line')); + $lang->expects($this->any())->method('line')->will($this->returnValue(FALSE)); + $this->ci_instance_var('lang', $lang); + $this->calendar = new CI_Calendar(); } function test_initialize() @@ -20,9 +20,6 @@ class Calendar_test extends CI_TestCase { $this->assertEquals('monday', $this->calendar->start_day); } - /** - * @covers Mock_Libraries_Calendar::parse_template - */ function test_generate() { $no_events = '<table border="0" cellpadding="4" cellspacing="0"> diff --git a/tests/codeigniter/libraries/Driver_test.php b/tests/codeigniter/libraries/Driver_test.php new file mode 100644 index 000000000..fb5f3f020 --- /dev/null +++ b/tests/codeigniter/libraries/Driver_test.php @@ -0,0 +1,176 @@ +<?php + +/** + * Driver library base class unit test + */ +class Driver_test extends CI_TestCase { + /** + * Set up test framework + */ + public function set_up() + { + // Set our subclass prefix + $this->subclass = 'Mock_Libraries_'; + $this->ci_set_config('subclass_prefix', $this->subclass); + + // Mock Loader->get_package_paths + $paths = 'get_package_paths'; + $ldr = $this->getMock('CI_Loader', array($paths)); + $ldr->expects($this->any())->method($paths)->will($this->returnValue(array(APPPATH, BASEPATH))); + $this->ci_instance_var('load', $ldr); + + // Create mock driver library + $this->name = 'Driver'; + $this->lib = new Mock_Libraries_Driver(); + } + + /** + * Test driver child loading + */ + public function test_load_driver() + { + // Create driver file + $driver = 'basic'; + $file = $this->name.'_'.$driver; + $class = 'CI_'.$file; + $prop = 'called'; + $content = '<?php class '.$class.' extends CI_Driver { public $'.$prop.' = FALSE; '. + 'public function decorate($parent) { $this->'.$prop.' = TRUE; } }'; + $this->ci_vfs_create($file, $content, $this->ci_base_root, array('libraries', $this->name, 'drivers')); + + // Make driver valid + $this->lib->driver_list($driver); + + // Load driver + $this->assertNotNull($this->lib->load_driver($driver)); + + // Did lib name get set? + $this->assertEquals($this->name, $this->lib->get_name()); + + // Was driver loaded? + $this->assertObjectHasAttribute($driver, $this->lib); + $this->assertAttributeInstanceOf($class, $driver, $this->lib); + $this->assertAttributeInstanceOf('CI_Driver', $driver, $this->lib); + + // Was decorate called? + $this->assertObjectHasAttribute($prop, $this->lib->$driver); + $this->assertTrue($this->lib->$driver->$prop); + + // Do we get an error for an invalid driver? + $driver = 'unlisted'; + $this->setExpectedException('RuntimeException', 'CI Error: Invalid driver requested: '.$this->name.'_'.$driver); + $this->lib->load_driver($driver); + } + + /** + * Test loading lowercase from app path + */ + public function test_load_app_driver() + { + // Create driver file + $driver = 'lowpack'; + $file = $this->name.'_'.$driver; + $class = 'CI_'.$file; + $content = '<?php class '.$class.' extends CI_Driver { }'; + $this->ci_vfs_create($file, $content, $this->ci_app_root, + array('libraries', $this->name, 'drivers')); + + // Make valid list + $nodriver = 'absent'; + $this->lib->driver_list(array($driver, $nodriver)); + + // Load driver + $this->assertNotNull($this->lib->load_driver($driver)); + + // Was driver loaded? + $this->assertObjectHasAttribute($driver, $this->lib); + $this->assertAttributeInstanceOf($class, $driver, $this->lib); + $this->assertAttributeInstanceOf('CI_Driver', $driver, $this->lib); + + // Do we get an error for a non-existent driver? + $this->setExpectedException('RuntimeException', 'CI Error: Unable to load the requested driver: CI_'. + $this->name.'_'.$nodriver); + $this->lib->load_driver($nodriver); + } + + /** + * Test loading driver extension + */ + public function test_load_driver_ext() + { + // Create base file + $driver = 'extend'; + $base = $this->name.'_'.$driver; + $baseclass = 'CI_'.$base; + $content = '<?php class '.$baseclass.' extends CI_Driver { }'; + $this->ci_vfs_create($base, $content, $this->ci_base_root, array('libraries', $this->name, 'drivers')); + + // Create driver file + $class = $this->subclass.$base; + $content = '<?php class '.$class.' extends '.$baseclass.' { }'; + $this->ci_vfs_create($class, $content, $this->ci_app_root, array('libraries', $this->name, 'drivers')); + + // Make valid list + $this->lib->driver_list($driver); + + // Load driver + $this->assertNotNull($this->lib->load_driver($driver)); + + // Was driver loaded? + $this->assertObjectHasAttribute($driver, $this->lib); + $this->assertAttributeInstanceOf($class, $driver, $this->lib); + $this->assertAttributeInstanceOf($baseclass, $driver, $this->lib); + $this->assertAttributeInstanceOf('CI_Driver', $driver, $this->lib); + + // Create driver extension without base + $driver = 'baseless'; + $base = $this->name.'_'.$driver; + $class = $this->subclass.$base; + $content = '<?php class '.$class.' extends CI_Driver { }'; + $this->ci_vfs_create($class, $content, $this->ci_app_root, array('libraries', $this->name, 'drivers')); + $this->lib->driver_list($driver); + + // Do we get an error when base class isn't found? + $this->setExpectedException('RuntimeException', 'CI Error: Unable to load the requested class: CI_'.$base); + $this->lib->load_driver($driver); + } + + /** + * Test decorating driver with parent attributes + */ + public function test_decorate() + { + // Create parent with a method and property to access + $pclass = 'Test_Parent'; + $prop = 'parent_prop'; + $value = 'initial'; + $method = 'parent_func'; + $return = 'func return'; + $code = 'class '.$pclass.' { public $'.$prop.' = \''.$value.'\'; '. + 'public function '.$method.'() { return \''.$return.'\'; } }'; + eval($code); + $parent = new $pclass(); + + // Create child driver to decorate + $class = 'Test_Driver'; + eval('class '.$class.' extends CI_Driver { }'); + $child = new $class(); + + // Decorate child + $child->decorate($parent); + + // Do we get the initial parent property value? + $this->assertEquals($value, $child->$prop); + + // Can we change the parent property? + $newval = 'changed'; + $child->$prop = $newval; + $this->assertEquals($newval, $parent->$prop); + + // Do we get back the updated value? + $this->assertEquals($newval, $child->$prop); + + // Can we call the parent method? + $this->assertEquals($return, $child->$method()); + } +} diff --git a/tests/codeigniter/libraries/Session_test.php b/tests/codeigniter/libraries/Session_test.php index 50d263827..7ef3a3667 100644 --- a/tests/codeigniter/libraries/Session_test.php +++ b/tests/codeigniter/libraries/Session_test.php @@ -6,8 +6,8 @@ class Session_test extends CI_TestCase { protected $settings = array( 'use_cookies' => 0, - 'use_only_cookies' => 0, - 'cache_limiter' => false + 'use_only_cookies' => 0, + 'cache_limiter' => false ); protected $setting_vals = array(); protected $cookie_vals; @@ -28,11 +28,12 @@ class Session_test extends CI_TestCase { $this->cookie_vals = $_COOKIE; $_COOKIE = array(); + // Set subclass prefix to match our mock + $this->ci_set_config('subclass_prefix', 'Mock_Libraries_'); + // Establish necessary support classes - $cfg = $this->ci_core_class('cfg'); - $ldr = $this->ci_core_class('load'); $ci = $this->ci_instance(); - $ci->config = new $cfg(); + $ldr = $this->ci_core_class('load'); $ci->load = new $ldr(); $ci->input = new Mock_Core_Input(NULL, NULL); @@ -56,11 +57,7 @@ class Session_test extends CI_TestCase { 'sess_time_to_update' => 300, 'time_reference' => 'local', 'cookie_prefix' => '', - 'encryption_key' => 'foobar', - 'sess_valid_drivers' => array( - 'Mock_Libraries_Session_native', - 'Mock_Libraries_Session_cookie' - ) + 'encryption_key' => 'foobar' ); $this->session = new Mock_Libraries_Session($config); } @@ -83,9 +80,6 @@ class Session_test extends CI_TestCase { /** * Test set_userdata() function - * - * @covers CI_Session::set_userdata - * @covers CI_Session::userdata */ public function test_set_userdata() { @@ -117,8 +111,6 @@ class Session_test extends CI_TestCase { /** * Test the has_userdata() function - * - * @covers CI_Session::has_userdata */ public function test_has_userdata() { @@ -141,8 +133,6 @@ class Session_test extends CI_TestCase { /** * Test the all_userdata() function - * - * @covers CI_Session::all_userdata */ public function test_all_userdata() { @@ -150,16 +140,16 @@ class Session_test extends CI_TestCase { $cdata = array( 'one' => 'first', 'two' => 'second', - 'three' => 'third', - 'foo' => 'bar', - 'bar' => 'baz' + 'three' => 'third', + 'foo' => 'bar', + 'bar' => 'baz' ); $ndata = array( 'one' => 'gold', - 'two' => 'silver', - 'three' => 'bronze', - 'foo' => 'baz', - 'bar' => 'foo' + 'two' => 'silver', + 'three' => 'bronze', + 'foo' => 'baz', + 'bar' => 'foo' ); $this->session->cookie->set_userdata($cdata); $this->session->native->set_userdata($ndata); @@ -177,8 +167,6 @@ class Session_test extends CI_TestCase { /** * Test the unset_userdata() function - * - * @covers CI_Session::unset_userdata */ public function test_unset_userdata() { @@ -202,9 +190,6 @@ class Session_test extends CI_TestCase { /** * Test the flashdata() functions - * - * @covers CI_Session::set_flashdata - * @covers CI_Session::flashdata */ public function test_flashdata() { @@ -234,8 +219,6 @@ class Session_test extends CI_TestCase { /** * Test the keep_flashdata() function - * - * @covers CI_Session::keep_flashdata */ public function test_keep_flashdata() { @@ -321,25 +304,23 @@ class Session_test extends CI_TestCase { /** * Test the all_flashdata() function - * - * @covers CI_Session::all_flashdata */ public function test_all_flashdata() { // Set a specific series of data for each driver $cdata = array( 'one' => 'first', - 'two' => 'second', - 'three' => 'third', - 'foo' => 'bar', - 'bar' => 'baz' + 'two' => 'second', + 'three' => 'third', + 'foo' => 'bar', + 'bar' => 'baz' ); $ndata = array( 'one' => 'gold', - 'two' => 'silver', - 'three' => 'bronze', - 'foo' => 'baz', - 'bar' => 'foo' + 'two' => 'silver', + 'three' => 'bronze', + 'foo' => 'baz', + 'bar' => 'foo' ); $this->session->cookie->set_flashdata($cdata); $this->session->native->set_flashdata($ndata); @@ -353,9 +334,6 @@ class Session_test extends CI_TestCase { /** * Test the tempdata() functions - * - * @covers CI_Session::set_tempdata - * @covers CI_Session::tempdata */ public function test_set_tempdata() { @@ -382,8 +360,6 @@ class Session_test extends CI_TestCase { /** * Test the unset_tempdata() function - * - * @covers CI_Session::unset_tempdata */ public function test_unset_tempdata() { @@ -407,8 +383,6 @@ class Session_test extends CI_TestCase { /** * Test the sess_regenerate() function - * - * @covers CI_Session::sess_regenerate */ public function test_sess_regenerate() { @@ -428,8 +402,6 @@ class Session_test extends CI_TestCase { /** * Test the sess_destroy() function - * - * @covers CI_Session::sess_destroy */ public function test_sess_destroy() { diff --git a/tests/codeigniter/libraries/Upload_test.php b/tests/codeigniter/libraries/Upload_test.php index 546cebc59..1bd8f1430 100644 --- a/tests/codeigniter/libraries/Upload_test.php +++ b/tests/codeigniter/libraries/Upload_test.php @@ -7,7 +7,8 @@ class Upload_test extends CI_TestCase { $ci = $this->ci_instance(); $ci->upload = new Mock_Libraries_Upload(); $ci->security = new Mock_Core_Security(); - $ci->lang = new Mock_Core_Lang(); + $ci->lang = $this->getMock('CI_Lang', array('load', 'line')); + $ci->lang->expects($this->any())->method('line')->will($this->returnValue(FALSE)); $this->upload = $ci->upload; } diff --git a/tests/mocks/core/lang.php b/tests/mocks/core/lang.php deleted file mode 100644 index 27ea3faba..000000000 --- a/tests/mocks/core/lang.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -class Mock_Core_Lang extends CI_Lang { - - public function line($line = '') - { - return FALSE; - } - - public function load($langfile, $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '') - { - return; - } - -}
\ No newline at end of file diff --git a/tests/mocks/database/config/mysqli.php b/tests/mocks/database/config/mysqli.php new file mode 100644 index 000000000..5dd08abb2 --- /dev/null +++ b/tests/mocks/database/config/mysqli.php @@ -0,0 +1,34 @@ +<?php + +return array( + + // Typical Database configuration + 'mysqli' => array( + 'dsn' => '', + 'hostname' => 'localhost', + 'username' => 'travis', + 'password' => '', + 'database' => 'ci_test', + 'dbdriver' => 'mysqli' + ), + + // Database configuration with failover + 'mysqli_failover' => array( + 'dsn' => '', + 'hostname' => 'localhost', + 'username' => 'not_travis', + 'password' => 'wrong password', + 'database' => 'not_ci_test', + 'dbdriver' => 'mysqli', + 'failover' => array( + array( + 'dsn' => '', + 'hostname' => 'localhost', + 'username' => 'travis', + 'password' => '', + 'database' => 'ci_test', + 'dbdriver' => 'mysqli', + ) + ) + ) +);
\ No newline at end of file diff --git a/tests/mocks/database/drivers/mysqli.php b/tests/mocks/database/drivers/mysqli.php new file mode 100644 index 000000000..73c35b609 --- /dev/null +++ b/tests/mocks/database/drivers/mysqli.php @@ -0,0 +1,17 @@ +<?php + +class Mock_Database_Drivers_Mysqli extends Mock_Database_DB_Driver { + + /** + * Instantiate the database driver + * + * @param string DB Driver class name + * @param array DB configuration to set + * @return void + */ + public function __construct($config = array()) + { + parent::__construct('CI_DB_mysqli_driver', $config); + } + +}
\ No newline at end of file diff --git a/tests/mocks/libraries/calendar.php b/tests/mocks/libraries/calendar.php deleted file mode 100644 index 8fee5365e..000000000 --- a/tests/mocks/libraries/calendar.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -class Mock_Libraries_Calendar extends CI_Calendar { - - public function __construct($config = array()) - { - $this->CI = new stdClass; - $this->CI->lang = new Mock_Core_Lang(); - - if ( ! in_array('calendar_lang.php', $this->CI->lang->is_loaded, TRUE)) - { - $this->CI->lang->load('calendar'); - } - - $this->local_time = time(); - - if (count($config) > 0) - { - $this->initialize($config); - } - - log_message('debug', 'Calendar Class Initialized'); - } - -}
\ No newline at end of file diff --git a/tests/mocks/libraries/driver.php b/tests/mocks/libraries/driver.php new file mode 100644 index 000000000..91bb01596 --- /dev/null +++ b/tests/mocks/libraries/driver.php @@ -0,0 +1,27 @@ +<?php + +/** + * Mock library to subclass Driver for testing + */ +class Mock_Libraries_Driver extends CI_Driver_Library { + /** + * Set valid drivers list + */ + public function driver_list($drivers = NULL) + { + if (empty($drivers)) + { + return $this->valid_drivers; + } + + $this->valid_drivers = (array) $drivers; + } + + /** + * Get library name + */ + public function get_name() + { + return $this->lib_name; + } +} diff --git a/tests/mocks/libraries/session.php b/tests/mocks/libraries/session.php index c6e194f58..11b27cf67 100644 --- a/tests/mocks/libraries/session.php +++ b/tests/mocks/libraries/session.php @@ -4,7 +4,6 @@ * Mock library to add testing features to Session driver library */ class Mock_Libraries_Session extends CI_Session { - /** * Simulate new page load */ @@ -20,7 +19,6 @@ class Mock_Libraries_Session extends CI_Session { * Mock cookie driver to overload cookie setting */ class Mock_Libraries_Session_cookie extends CI_Session_cookie { - /** * Overload _setcookie to manage $_COOKIE values, since actual cookies can't be set in unit testing */ @@ -36,8 +34,3 @@ class Mock_Libraries_Session_cookie extends CI_Session_cookie { } } } - -/** - * Mock native driver (just for consistency in loading) - */ -class Mock_Libraries_Session_native extends CI_Session_native { }
\ No newline at end of file diff --git a/tests/travis/mysqli.phpunit.xml b/tests/travis/mysqli.phpunit.xml new file mode 100644 index 000000000..1364f8bfa --- /dev/null +++ b/tests/travis/mysqli.phpunit.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<phpunit + bootstrap="../Bootstrap.php" + colors="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + stopOnError="false" + stopOnFailure="false" + stopOnIncomplete="false" + stopOnSkipped="false"> + <php> + <const name="DB_DRIVER" value="mysqli"/> + </php> + <testsuites> + <testsuite name="CodeIgniter Core Test Suite"> + <directory suffix="test.php">../codeigniter</directory> + </testsuite> + </testsuites> + <filter> + <whitelist addUncoveredFilesFromWhitelist="false"> + <directory suffix=".php">../../system</directory> + </whitelist> + </filter> +</phpunit>
\ No newline at end of file diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst index 5d4757a7e..6570eb790 100644 --- a/user_guide_src/source/changelog.rst +++ b/user_guide_src/source/changelog.rst @@ -96,7 +96,8 @@ Release Date: Not Released - Deprecated function ``repeater()`` - it's just an alias for PHP's native ``str_repeat()``. - Deprecated function ``trim_slashes()`` - it's just an alias for PHP's native ``trim()`` (with a slash as its second argument). - Deprecated randomization type options **unique** and **encrypt** for funcion :php:func:`random_string()` (they are only aliases for **md5** and **sha1** respectively). - - :doc:`Directory Helper <helpers/directory_helper>` ``directory_map()`` will now append DIRECTORY_SEPARATOR to directory names in the returned array. + - :doc:`Directory Helper <helpers/directory_helper>` :php:func:`directory_map()` will now append ``DIRECTORY_SEPARATOR`` to directory names in the returned array. + - :doc:`Language Helper <helpers/language_helper>` :php:func:`lang()` now accepts an optional list of additional HTML attributes. - Deprecated the :doc:`Email Helper <helpers/email_helper>` as its ``valid_email()``, ``send_email()`` functions are now only aliases for PHP native functions ``filter_var()`` and ``mail()`` respectively. - Database @@ -220,11 +221,12 @@ Release Date: Not Released - Added method ``reset_validation()`` which resets internal validation variables in case of multiple validation routines. - Added support for setting error delimiters in the config file via ``$config['error_prefix']`` and ``$config['error_suffix']``. - ``_execute()`` now considers input data to be invalid if a specified rule is not found. - - Removed method ``is_numeric()`` as it exists as a native PHP function and ``_execute()`` will find and use that (the *is_numeric* rule itself is deprecated since 1.6.1). + - Removed method ``is_numeric()`` as it exists as a native PHP function and ``_execute()`` will find and use that (the **is_numeric** rule itself is deprecated since 1.6.1). - Native PHP functions used as rules can now accept an additional parameter, other than the data itself. - Updated ``set_rules()`` to accept an array of rules as well as a string. - Fields that have empty rules set no longer run through validation (and therefore are not considered erroneous). - - Added rule *differs* to check if the value of a field differs from the value of another field. + - Added rule **differs* to check if the value of a field differs from the value of another field. + - Added rule **valid_url**. - Added support for setting :doc:`Table <libraries/table>` class defaults in a config file. - :doc:`Caching Library <libraries/caching>` changes include: - Added Wincache driver. @@ -242,6 +244,7 @@ Release Date: Not Released - Default charset now relies on the global ``$config['charset']`` setting. - Removed unused protected method ``_get_ip()`` (:doc:`Input Library <libraries/input>`'s ``ip_address()`` should be used anyway). - Internal method ``_prep_q_encoding()`` now utilizes PHP's *mbstring* and *iconv* extensions (when available) and no longer has a second (``$from``) argument. + - Added an optional parameter to ``print_debugger()`` to allow specifying which parts of the message should be printed ('headers', 'subject', 'body'). - :doc:`Pagination Library <libraries/pagination>` changes include: - Added support for the anchor "rel" attribute. - Added support for setting custom attributes. @@ -252,9 +255,6 @@ Release Date: Not Released - :doc:`Encryption Library <libraries/encryption>` changes include: - Added support for hashing algorithms other than SHA1 and MD5. - Removed previously deprecated ``sha1()`` method. - - :doc:`Language Library <libraries/language>` changes include: - - Changed method ``load()`` to filter the language name with ``ctype_digit()``. - - Added an optional second parameter to method ``line()`` to disable error login for line keys that were not found. - :doc:`Profiler Library <general/profiling>` now also displays database object names. - :doc:`Migration Library <libraries/migration>` changes include: - Added support for timestamp-based migrations (enabled by default). @@ -275,7 +275,6 @@ Release Date: Not Released - Added method ``get_vars()`` to the Loader to retrieve all variables loaded with ``$this->load->vars()``. - ``_ci_autoloader()`` is now a protected method. - Added autoloading of drivers with ``$autoload['drivers']``. - - ``library()`` method will now load drivers as well, for backward compatibility of converted libraries (like :doc:`Session <libraries/sessions>`). - ``$config['rewrite_short_tags']`` now has no effect when using PHP 5.4 as ``<?=`` will always be available. - Changed method ``config()`` to return whatever ``CI_Config::load()`` returns instead of always being void. - :doc:`Input Library <libraries/input>` changes include: @@ -295,8 +294,9 @@ Release Date: Not Released - Added support for HTTP-Only cookies with new config option *cookie_httponly* (default FALSE). - Renamed method ``_call_hook()`` to ``call_hook()`` in the :doc:`Hooks Library <general/hooks>`. - :doc:`Output Library <libraries/output>` changes include: - - Added method ``get_content_type()``. - Added a second argument to method ``set_content_type()`` that allows setting the document charset as well. + - Added methods ``get_content_type()`` and ``get_header()``. + - Added method ``delete_cache()``. - ``$config['time_reference']`` now supports all timezone strings supported by PHP. - :doc:`Config Library <libraries/config>` changes include: - Changed ``site_url()`` method to accept an array as well. @@ -308,6 +308,10 @@ Release Date: Not Released - :doc:`URI Routing <general/routing>` changes include: - Added possibility to route requests using callbacks. - Added possibility to use dashes in the controller and method URI segments (translated to underscores). + - :doc:`Language Library <libraries/language>` changes include: + - Changed method ``load()`` to filter the language name with ``ctype_digit()``. + - Added an optional second parameter to method ``line()`` to disable error login for line keys that were not found. + - Language files are now loaded in a cascading style with the one in **system/** always loaded and overriden afterwards, if another one is found. Bug fixes for 3.0 ------------------ diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst index 8fb906052..65609c1cb 100644 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -345,23 +345,24 @@ if appropriate $this->db->like() ================= -This function enables you to generate **LIKE** clauses, useful for doing +This method enables you to generate **LIKE** clauses, useful for doing searches. -.. note:: All values passed to this function are escaped automatically. +.. note:: All values passed to this method are escaped automatically. #. **Simple key/value method:** :: - $this->db->like('title', 'match'); // Produces: WHERE title LIKE '%match%' + $this->db->like('title', 'match'); + // Produces: WHERE `title` LIKE '%match%' ESCAPE '!' - If you use multiple function calls they will be chained together with + If you use multiple method calls they will be chained together with AND between them:: $this->db->like('title', 'match'); $this->db->like('body', 'match'); - // WHERE title LIKE '%match%' AND body LIKE '%match% + // WHERE `title` LIKE '%match%' ESCAPE '!' AND `body` LIKE '%match% ESCAPE '!' If you want to control where the wildcard (%) is placed, you can use an optional third argument. Your options are 'before', 'after' and @@ -369,9 +370,9 @@ searches. :: - $this->db->like('title', 'match', 'before'); // Produces: WHERE title LIKE '%match' - $this->db->like('title', 'match', 'after'); // Produces: WHERE title LIKE 'match%' - $this->db->like('title', 'match', 'both'); // Produces: WHERE title LIKE '%match%' + $this->db->like('title', 'match', 'before'); // Produces: WHERE `title` LIKE '%match' ESCAPE '!' + $this->db->like('title', 'match', 'after'); // Produces: WHERE `title` LIKE 'match%' ESCAPE '!' + $this->db->like('title', 'match', 'both'); // Produces: WHERE `title` LIKE '%match%' ESCAPE '!' #. **Associative array method:** @@ -379,37 +380,37 @@ searches. $array = array('title' => $match, 'page1' => $match, 'page2' => $match); $this->db->like($array); - // WHERE title LIKE '%match%' AND page1 LIKE '%match%' AND page2 LIKE '%match%' + // WHERE `title` LIKE '%match%' ESCAPE '!' AND `page1` LIKE '%match%' ESCAPE '!' AND `page2` LIKE '%match%' ESCAPE '!' $this->db->or_like() ==================== -This function is identical to the one above, except that multiple +This method is identical to the one above, except that multiple instances are joined by OR:: $this->db->like('title', 'match'); $this->db->or_like('body', $match); - // WHERE title LIKE '%match%' OR body LIKE '%match%' + // WHERE `title` LIKE '%match%' ESCAPE '!' OR `body` LIKE '%match%' ESCAPE '!' -.. note:: or_like() was formerly known as orlike(), which has been removed. +.. note:: ``or_like()`` was formerly known as ``orlike()``, which has been removed. $this->db->not_like() ===================== -This function is identical to **like()**, except that it generates NOT -LIKE statements:: +This method is identical to ``like()``, except that it generates +NOT LIKE statements:: - $this->db->not_like('title', 'match'); // WHERE title NOT LIKE '%match% + $this->db->not_like('title', 'match'); // WHERE `title` NOT LIKE '%match% ESCAPE '!' $this->db->or_not_like() ======================== -This function is identical to **not_like()**, except that multiple +This method is identical to ``not_like()``, except that multiple instances are joined by OR:: $this->db->like('title', 'match'); $this->db->or_not_like('body', 'match'); - // WHERE title LIKE '%match% OR body NOT LIKE '%match%' + // WHERE `title` LIKE '%match% OR `body` NOT LIKE '%match%' ESCAPE '!' $this->db->group_by() ===================== @@ -1054,4 +1055,4 @@ run the query:: $data = $this->db->get()->result_array(); // Would execute and return an array of results of the following query: - // SELECT field1, field1 from mytable where field3 = 5; + // SELECT field1, field1 from mytable where field3 = 5;
\ No newline at end of file diff --git a/user_guide_src/source/general/caching.rst b/user_guide_src/source/general/caching.rst index c40f652ac..48385d6c9 100644 --- a/user_guide_src/source/general/caching.rst +++ b/user_guide_src/source/general/caching.rst @@ -56,5 +56,13 @@ If you no longer wish to cache a file you can remove the caching tag and it will no longer be refreshed when it expires. .. note:: Removing the tag will not delete the cache immediately. It will - have to expire normally. If you need to remove it earlier you - will need to manually delete it from your cache directory.
\ No newline at end of file + have to expire normally. + +If you need to manually delete the cache, you can use the ``delete_cache()`` +method:: + + // Deletes cache for the currently requested URI + $this->output->delete_cache(); + + // Deletes cache for /foo/bar + $this->output->delete_cache('/foo/bar');
\ No newline at end of file diff --git a/user_guide_src/source/helpers/language_helper.rst b/user_guide_src/source/helpers/language_helper.rst index 8b039374d..1911e3bfd 100644 --- a/user_guide_src/source/helpers/language_helper.rst +++ b/user_guide_src/source/helpers/language_helper.rst @@ -19,18 +19,18 @@ The following functions are available: lang() ====== -.. php:function:: lang($line, $id = '') +.. php:function:: lang($line, $for = '', $attributes = array()) :param string $line: Language line key - :param string $id: ID of the element we're creating a label for + :param string $for: HTML "for" attribute (ID of the element we're creating a label for) + :param array $attributes: Any additional HTML attributes :returns: string This function returns a line of text from a loaded language file with simplified syntax that may be more desirable for view files than ``CI_Lang::line()``. -The optional second parameter will also output a form label for you. Example:: - echo lang('language_key', 'form_item_id'); - // becomes <label for="form_item_id">language_key</label>
\ No newline at end of file + echo lang('language_key', 'form_item_id', array('class' => 'myClass'); + // Outputs: <label for="form_item_id" class="myClass">Language line</label>
\ No newline at end of file diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst index 0af21b11e..ef5fbdf71 100644 --- a/user_guide_src/source/installation/upgrade_300.rst +++ b/user_guide_src/source/installation/upgrade_300.rst @@ -42,9 +42,13 @@ or extensions to work, you need to move them to **application/core/**:: application/libraries/Log.php -> application/core/Log.php application/libraries/MY_Log.php -> application/core/MY_log.php -************************************************************** -Step 5: Add new session driver items to your config/config.php -************************************************************** +********************************************************* +Step 5: Convert your Session usage from library to driver +********************************************************* + +When you load (or autoload) the Session library, you must now load it as a driver instead of a library. This means +calling ``$this->load->driver('session')`` instead of ``$this->load->library('session')`` and/or listing 'session' +in ``$autoload['drivers']`` instead of ``$autoload['libraries']``. With the change from a single Session Library to the new Session Driver, two new config items have been added: @@ -58,6 +62,10 @@ As the new Session Driver library loads the classic Cookie driver by default and available as valid drivers, neither of these configuration items are required. However, it is recommended that you add them for clarity and ease of configuration in the future. +If you have written a Session extension, you must move it into a 'Session' sub-directory of 'libraries', following the +standard for Drivers. Also beware that some functions which are not part of the external Session API have moved into +the drivers, so your extension may have to be broken down into separate library and driver class extensions. + *************************************** Step 6: Update your config/database.php *************************************** @@ -314,4 +322,4 @@ You should now put AFTER clause field names in the field definition array instea sooner rather than later. .. note:: This is for MySQL and CUBRID databases only! Other drivers don't support this - clause and will silently ignore it. + clause and will silently ignore it.
\ No newline at end of file diff --git a/user_guide_src/source/libraries/email.rst b/user_guide_src/source/libraries/email.rst index da3bf2616..8643444f8 100644 --- a/user_guide_src/source/libraries/email.rst +++ b/user_guide_src/source/libraries/email.rst @@ -268,11 +268,21 @@ parameter as mime-type:: $this->email->attach($buffer, 'attachment', 'report.pdf', 'application/pdf'); $this->email->print_debugger() -------------------------------- +------------------------------ Returns a string containing any server messages, the email headers, and the email messsage. Useful for debugging. +You can optionally specify which parts of the message should be printed. +Valid options are: **headers**, **subject**, **body**. + +Example:: + + // Will only print the email headers, excluding the message subject and body + $this->email->print_debugger(array('headers')); + +.. note:: By default, all of the raw data will be printed. + Overriding Word Wrapping ======================== diff --git a/user_guide_src/source/libraries/form_validation.rst b/user_guide_src/source/libraries/form_validation.rst index b1f466f4c..7478ca0ef 100644 --- a/user_guide_src/source/libraries/form_validation.rst +++ b/user_guide_src/source/libraries/form_validation.rst @@ -894,6 +894,7 @@ Rule Parameter Description 0, 1, 2, 3, etc. **is_natural_no_zero** No Returns FALSE if the form element contains anything other than a natural number, but not zero: 1, 2, 3, etc. +**valid_url** No Returns FALSE if the form element does not contain a valid URL. **valid_email** No Returns FALSE if the form element does not contain a valid email address. **valid_emails** No Returns FALSE if any value provided in a comma separated list is not a valid email. **valid_ip** No Returns FALSE if the supplied IP is not valid. diff --git a/user_guide_src/source/libraries/language.rst b/user_guide_src/source/libraries/language.rst index 772f70d0b..d288cd65e 100644 --- a/user_guide_src/source/libraries/language.rst +++ b/user_guide_src/source/libraries/language.rst @@ -10,12 +10,11 @@ containing sets of language files. You can create your own language files as needed in order to display error and other messages in other languages. -Language files are typically stored in your system/language directory. -Alternately you can create a folder called language inside your -application folder and store them there. CodeIgniter will look first in -your application/language directory. If the directory does not exist or -the specified language is not located there CI will instead look in your -global system/language folder. +Language files are typically stored in your **system/language/** directory. +Alternately you can create a directory called language inside your +application folder and store them there. CodeIgniter will always load the +one in **system/language/** first and will then look for an override in +your **application/language/** directory. .. note:: Each language should be stored in its own folder. For example, the English files are located at: system/language/english @@ -23,14 +22,14 @@ global system/language folder. Creating Language Files ======================= -Language files must be named with _lang.php as the file extension. For +Language files must be named with **_lang.php** as the file extension. For example, let's say you want to create a file containing error messages. You might name it: error_lang.php Within the file you will assign each line of text to an array called -$lang with this prototype:: +``$lang`` with this prototype:: - $lang['language_key'] = "The actual message to be shown"; + $lang['language_key'] = 'The actual message to be shown'; .. note:: It's a good practice to use a common prefix for all messages in a given file to avoid collisions with similarly named items in other @@ -39,9 +38,9 @@ $lang with this prototype:: :: - $lang['error_email_missing'] = "You must submit an email address"; - $lang['error_url_missing'] = "You must submit a URL"; - $lang['error_username_missing'] = "You must submit a username"; + $lang['error_email_missing'] = 'You must submit an email address'; + $lang['error_url_missing'] = 'You must submit a URL'; + $lang['error_username_missing'] = 'You must submit a username'; Loading A Language File ======================= @@ -54,7 +53,7 @@ first. Loading a language file is done with the following code:: Where filename is the name of the file you wish to load (without the file extension), and language is the language set containing it (ie, english). If the second parameter is missing, the default language set -in your *application/config/config.php* file will be used. +in your **application/config/config.php** file will be used. .. note:: The *language* parameter can only consist of letters. @@ -80,17 +79,14 @@ Using language lines as form labels ----------------------------------- This feature has been deprecated from the language library and moved to -the lang() function of the :doc:`Language -helper <../helpers/language_helper>`. +the :php:func:`lang()` function of the :doc:`Language Helper +<../helpers/language_helper>`. Auto-loading Languages ====================== If you find that you need a particular language globally throughout your -application, you can tell CodeIgniter to -:doc:`auto-load <../general/autoloader>` it during system -initialization. This is done by opening the -application/config/autoload.php file and adding the language(s) to the -autoload array. - - +application, you can tell CodeIgniter to :doc:`auto-load +<../general/autoloader>` it during system initialization. This is done +by opening the **application/config/autoload.php** file and adding the +language(s) to the autoload array.
\ No newline at end of file diff --git a/user_guide_src/source/libraries/output.rst b/user_guide_src/source/libraries/output.rst index 82b1a56a5..a3d67b847 100644 --- a/user_guide_src/source/libraries/output.rst +++ b/user_guide_src/source/libraries/output.rst @@ -53,17 +53,37 @@ You can also set the character set of the document, by passing a second argument $this->output->set_content_type('css', 'utf-8'); -$this->output->get_content_type(); -========================================== +$this->output->get_content_type() +================================= -Returns the Content-Type HTTP header that's currently in use. +Returns the Content-Type HTTP header that's currently in use, +excluding the character set value. $mime = $this->output->get_content_type(); .. note:: If not set, the default return value is 'text/html'. -$this->output->get_output(); -============================= +$this->output->get_header() +=========================== + +Gets the requested HTTP header value, if set. + +If the header is not set, NULL will be returned. +If an empty value is passed to the method, it will return FALSE. + +Example:: + + $this->output->set_content_type('text/plain', 'UTF-8'); + echo $this->output->get_header('content-type'); + // Outputs: text/plain; charset=utf-8 + +.. note:: The header name is compared in a case-insensitive manner. + +.. note:: Raw headers sent via PHP's native ``header()`` function are + also detected. + +$this->output->get_output() +=========================== Permits you to manually retrieve any output that has been sent for storage in the output class. Usage example:: diff --git a/user_guide_src/source/libraries/sessions.rst b/user_guide_src/source/libraries/sessions.rst index aecad3164..36c7c1d32 100644 --- a/user_guide_src/source/libraries/sessions.rst +++ b/user_guide_src/source/libraries/sessions.rst @@ -28,10 +28,6 @@ use the $this->load->driver function:: Once loaded, the Sessions library object will be available using: $this->session -.. note:: For backward compatibility, the Session class may stil be loaded - using the $this->load->library function, but converting your applications - to use $this->load->driver is strongly recommended. - How do Sessions work? ===================== @@ -487,4 +483,3 @@ without making it the initially loaded driver, set 'sess_valid_drivers' in your config.php file to an array including your driver name:: $config['sess_valid_drivers'] = array('sess_driver'); - |