diff options
Diffstat (limited to 'system/libraries/Form_validation.php')
-rw-r--r-- | system/libraries/Form_validation.php | 1292 |
1 files changed, 749 insertions, 543 deletions
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php index 3839fe42b..dd1685db1 100644 --- a/system/libraries/Form_validation.php +++ b/system/libraries/Form_validation.php @@ -1,19 +1,42 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); +<?php /** * CodeIgniter * - * An open source application development framework for PHP 5.1.6 or newer + * An open source application development framework for PHP * - * @package CodeIgniter - * @author ExpressionEngine Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. - * @license http://codeigniter.com/user_guide/license.html - * @link http://codeigniter.com - * @since Version 1.0 + * This content is released under the MIT License (MIT) + * + * Copyright (c) 2019 - 2022, CodeIgniter Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @package CodeIgniter + * @author EllisLab Dev Team + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/) + * @copyright Copyright (c) 2019 - 2022, CodeIgniter Foundation (https://codeigniter.com/) + * @license https://opensource.org/licenses/MIT MIT License + * @link https://codeigniter.com + * @since Version 1.0.0 * @filesource */ - -// ------------------------------------------------------------------------ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * Form Validation Class @@ -21,41 +44,103 @@ * @package CodeIgniter * @subpackage Libraries * @category Validation - * @author ExpressionEngine Dev Team - * @link http://codeigniter.com/user_guide/libraries/form_validation.html + * @author EllisLab Dev Team + * @link https://codeigniter.com/userguide3/libraries/form_validation.html */ class CI_Form_validation { + /** + * Reference to the CodeIgniter instance + * + * @var object + */ protected $CI; - protected $_field_data = array(); - protected $_config_rules = array(); - protected $_error_array = array(); - protected $_error_messages = array(); - protected $_error_prefix = '<p>'; - protected $_error_suffix = '</p>'; - protected $error_string = ''; - protected $_safe_form_data = FALSE; /** - * Constructor + * Validation data for the current form submission + * + * @var array + */ + protected $_field_data = array(); + + /** + * Validation rules for the current form + * + * @var array + */ + protected $_config_rules = array(); + + /** + * Array of validation errors + * + * @var array + */ + protected $_error_array = array(); + + /** + * Array of custom error messages + * + * @var array + */ + protected $_error_messages = array(); + + /** + * Start tag for error wrapping + * + * @var string + */ + protected $_error_prefix = '<p>'; + + /** + * End tag for error wrapping + * + * @var string + */ + protected $_error_suffix = '</p>'; + + /** + * Custom error message + * + * @var string + */ + protected $error_string = ''; + + /** + * Custom data to validate + * + * @var array + */ + public $validation_data = array(); + + /** + * Initialize Form_Validation class + * + * @param array $rules + * @return void */ public function __construct($rules = array()) { $this->CI =& get_instance(); + // applies delimiters set in config file. + if (isset($rules['error_prefix'])) + { + $this->_error_prefix = $rules['error_prefix']; + unset($rules['error_prefix']); + } + if (isset($rules['error_suffix'])) + { + $this->_error_suffix = $rules['error_suffix']; + unset($rules['error_suffix']); + } + // Validation rules can be stored in a config file. $this->_config_rules = $rules; // Automatically load the form helper $this->CI->load->helper('form'); - // Set the character encoding in MB. - if (function_exists('mb_internal_encoding')) - { - mb_internal_encoding($this->CI->config->item('charset')); - } - - log_message('debug', "Form Validation Class Initialized"); + log_message('info', 'Form Validation Class Initialized'); } // -------------------------------------------------------------------- @@ -64,86 +149,99 @@ class CI_Form_validation { * Set Rules * * This function takes an array of field names and validation - * rules as input, validates the info, and stores it + * rules as input, any custom error messages, validates the info, + * and stores it * - * @access public - * @param mixed - * @param string - * @return void + * @param mixed $field + * @param string $label + * @param mixed $rules + * @param array $errors + * @return CI_Form_validation */ - public function set_rules($field, $label = '', $rules = '') + public function set_rules($field, $label = null, $rules = null, $errors = array()) { // No reason to set rules if we have no POST data - if (count($_POST) == 0) + // or a validation array has not been specified + if ($this->CI->input->method() !== 'post' && empty($this->validation_data)) { return $this; } - // If an array was passed via the first parameter instead of indidual string + // If an array was passed via the first parameter instead of individual string // values we cycle through it and recursively call this function. if (is_array($field)) { foreach ($field as $row) { // Houston, we have a problem... - if ( ! isset($row['field']) OR ! isset($row['rules'])) + if ( ! isset($row['field'], $row['rules'])) { continue; } // If the field label wasn't passed we use the field name - $label = ( ! isset($row['label'])) ? $row['field'] : $row['label']; + $label = isset($row['label']) ? $row['label'] : $row['field']; + + // Add the custom error message array + $errors = (isset($row['errors']) && is_array($row['errors'])) ? $row['errors'] : array(); // Here we go! - $this->set_rules($row['field'], $label, $row['rules']); + $this->set_rules($row['field'], $label, $row['rules'], $errors); } + return $this; } + elseif ( ! isset($rules)) + { + throw new BadMethodCallException('Form_validation: set_rules() called without a $rules parameter'); + } - // No fields? Nothing to do... - if ( ! is_string($field) OR ! is_string($rules) OR $field == '') + // No fields or no rules? Nothing to do... + if ( ! is_string($field) OR $field === '' OR empty($rules)) { - return $this; + throw new RuntimeException('Form_validation: set_rules() called with an empty $rules parameter'); + } + elseif ( ! is_array($rules)) + { + // BC: Convert pipe-separated rules string to an array + if ( ! is_string($rules)) + { + throw new InvalidArgumentException('Form_validation: set_rules() expect $rules to be string or array; '.gettype($rules).' given'); + } + + $rules = preg_split('/\|(?![^\[]*\])/', $rules); } // If the field label wasn't passed we use the field name - $label = ($label == '') ? $field : $label; + $label = ($label === '') ? $field : $label; + + $indexes = array(); - // Is the field name an array? We test for the existence of a bracket "[" in - // the field name to determine this. If it is an array, we break it apart + // Is the field name an array? If it is an array, we break it apart // into its components so that we can fetch the corresponding POST data later - if (strpos($field, '[') !== FALSE AND preg_match_all('/\[(.*?)\]/', $field, $matches)) + if (($is_array = (bool) preg_match_all('/\[(.*?)\]/', $field, $matches)) === TRUE) { - // Note: Due to a bug in current() that affects some versions - // of PHP we can not pass function call directly into it - $x = explode('[', $field); - $indexes[] = current($x); + sscanf($field, '%[^[][', $indexes[0]); - for ($i = 0; $i < count($matches['0']); $i++) + for ($i = 0, $c = count($matches[0]); $i < $c; $i++) { - if ($matches['1'][$i] != '') + if ($matches[1][$i] !== '') { - $indexes[] = $matches['1'][$i]; + $indexes[] = $matches[1][$i]; } } - - $is_array = TRUE; - } - else - { - $indexes = array(); - $is_array = FALSE; } // Build our master array $this->_field_data[$field] = array( - 'field' => $field, - 'label' => $label, - 'rules' => $rules, - 'is_array' => $is_array, - 'keys' => $indexes, - 'postdata' => NULL, - 'error' => '' + 'field' => $field, + 'label' => $label, + 'rules' => $rules, + 'errors' => $errors, + 'is_array' => $is_array, + 'keys' => $indexes, + 'postdata' => NULL, + 'error' => '' ); return $this; @@ -152,15 +250,39 @@ class CI_Form_validation { // -------------------------------------------------------------------- /** + * By default, form validation uses the $_POST array to validate + * + * If an array is set through this method, then this array will + * be used instead of the $_POST array + * + * Note that if you are validating multiple arrays, then the + * reset_validation() function should be called after validating + * each array due to the limitations of CI's singleton + * + * @param array $data + * @return CI_Form_validation + */ + public function set_data(array $data) + { + if ( ! empty($data)) + { + $this->validation_data = $data; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** * Set Error Message * - * Lets users set their own error messages on the fly. Note: The key - * name has to match the function name that it corresponds to. + * Lets users set their own error messages on the fly. Note: + * The key name has to match the function name that it corresponds to. * - * @access public - * @param string + * @param array * @param string - * @return string + * @return CI_Form_validation */ public function set_message($lang, $val = '') { @@ -170,7 +292,6 @@ class CI_Form_validation { } $this->_error_messages = array_merge($this->_error_messages, $lang); - return $this; } @@ -181,16 +302,14 @@ class CI_Form_validation { * * Permits a prefix/suffix to be added to each error message * - * @access public * @param string * @param string - * @return void + * @return CI_Form_validation */ public function set_error_delimiters($prefix = '<p>', $suffix = '</p>') { $this->_error_prefix = $prefix; $this->_error_suffix = $suffix; - return $this; } @@ -201,23 +320,24 @@ class CI_Form_validation { * * Gets the error message associated with a particular field * - * @access public - * @param string the field name - * @return void + * @param string $field Field name + * @param string $prefix HTML start tag + * @param string $suffix HTML end tag + * @return string */ - public function error($field = '', $prefix = '', $suffix = '') + public function error($field, $prefix = '', $suffix = '') { - if ( ! isset($this->_field_data[$field]['error']) OR $this->_field_data[$field]['error'] == '') + if (empty($this->_field_data[$field]['error'])) { return ''; } - if ($prefix == '') + if ($prefix === '') { $prefix = $this->_error_prefix; } - if ($suffix == '') + if ($suffix === '') { $suffix = $this->_error_suffix; } @@ -228,29 +348,42 @@ class CI_Form_validation { // -------------------------------------------------------------------- /** + * Get Array of Error Messages + * + * Returns the error messages as an array + * + * @return array + */ + public function error_array() + { + return $this->_error_array; + } + + // -------------------------------------------------------------------- + + /** * Error String * * Returns the error messages as a string, wrapped in the error delimiters * - * @access public * @param string * @param string - * @return str + * @return string */ public function error_string($prefix = '', $suffix = '') { - // No errrors, validation passes! + // No errors, validation passes! if (count($this->_error_array) === 0) { return ''; } - if ($prefix == '') + if ($prefix === '') { $prefix = $this->_error_prefix; } - if ($suffix == '') + if ($suffix === '') { $suffix = $this->_error_suffix; } @@ -259,7 +392,7 @@ class CI_Form_validation { $str = ''; foreach ($this->_error_array as $val) { - if ($val != '') + if ($val !== '') { $str .= $prefix.$val.$suffix."\n"; } @@ -275,43 +408,39 @@ class CI_Form_validation { * * This function does all the work. * - * @access public + * @param string $config + * @param array $data * @return bool */ - public function run($group = '') + public function run($config = NULL, &$data = NULL) { - // Do we even have any data to process? Mm? - if (count($_POST) == 0) - { - return FALSE; - } + $validation_array = empty($this->validation_data) + ? $_POST + : $this->validation_data; // Does the _field_data array containing the validation rules exist? // If not, we look to see if they were assigned via a config file - if (count($this->_field_data) == 0) + if (count($this->_field_data) === 0) { // No validation rules? We're done... - if (count($this->_config_rules) == 0) + if (empty($this->_config_rules)) { return FALSE; } - // Is there a validation rule for the particular URI being accessed? - $uri = ($group == '') ? trim($this->CI->uri->ruri_string(), '/') : $group; - - if ($uri != '' AND isset($this->_config_rules[$uri])) - { - $this->set_rules($this->_config_rules[$uri]); - } - else + if (empty($config)) { - $this->set_rules($this->_config_rules); + // Is there a validation rule for the particular URI being accessed? + $config = trim($this->CI->uri->ruri_string(), '/'); + isset($this->_config_rules[$config]) OR $config = $this->CI->router->class.'/'.$this->CI->router->method; } - // We're we able to set the rules correctly? - if (count($this->_field_data) == 0) + $this->set_rules(isset($this->_config_rules[$config]) ? $this->_config_rules[$config] : $this->_config_rules); + + // Were we able to set the rules correctly? + if (count($this->_field_data) === 0) { - log_message('debug', "Unable to find validation rules"); + log_message('debug', 'Unable to find validation rules'); return FALSE; } } @@ -319,47 +448,108 @@ class CI_Form_validation { // Load the language file containing error messages $this->CI->lang->load('form_validation'); - // Cycle through the rules for each field, match the - // corresponding $_POST item and test for errors - foreach ($this->_field_data as $field => $row) + // Cycle through the rules for each field and match the corresponding $validation_data item + foreach ($this->_field_data as $field => &$row) { - // Fetch the data from the corresponding $_POST array and cache it in the _field_data array. + // Fetch the data from the validation_data array item and cache it in the _field_data array. // Depending on whether the field name is an array or a string will determine where we get it from. - - if ($row['is_array'] == TRUE) + if ($row['is_array'] === TRUE) { - $this->_field_data[$field]['postdata'] = $this->_reduce_array($_POST, $row['keys']); + $this->_field_data[$field]['postdata'] = $this->_reduce_array($validation_array, $row['keys']); } - else + elseif (isset($validation_array[$field])) { - if (isset($_POST[$field]) AND $_POST[$field] != "") - { - $this->_field_data[$field]['postdata'] = $_POST[$field]; - } + $this->_field_data[$field]['postdata'] = $validation_array[$field]; } + } - $this->_execute($row, explode('|', $row['rules']), $this->_field_data[$field]['postdata']); + // Execute validation rules + // Note: A second foreach (for now) is required in order to avoid false-positives + // for rules like 'matches', which correlate to other validation fields. + foreach ($this->_field_data as $field => &$row) + { + // Don't try to validate if we have no rules set + if (empty($row['rules'])) + { + continue; + } + + $this->_execute($row, $row['rules'], $row['postdata']); } - // Did we end up with any errors? - $total_errors = count($this->_error_array); + if ( ! empty($this->_error_array)) + { + return FALSE; + } - if ($total_errors > 0) + // Fill $data if requested, otherwise modify $_POST, as long as + // set_data() wasn't used (yea, I know it sounds confusing) + if (func_num_args() >= 2) { - $this->_safe_form_data = TRUE; + $data = empty($this->validation_data) ? $_POST : $this->validation_data; + $this->_reset_data_array($data); + return TRUE; } - // Now we need to re-set the POST data with the new, processed data - $this->_reset_post_array(); + empty($this->validation_data) && $this->_reset_data_array($_POST); + return TRUE; + } - // No errors, validation passes! - if ($total_errors == 0) + // -------------------------------------------------------------------- + + /** + * Prepare rules + * + * Re-orders the provided rules in order of importance, so that + * they can easily be executed later without weird checks ... + * + * "Callbacks" are given the highest priority (always called), + * followed by 'required' (called if callbacks didn't fail), + * and then every next rule depends on the previous one passing. + * + * @param array $rules + * @return array + */ + protected function _prepare_rules($rules) + { + $new_rules = array(); + $callbacks = array(); + + foreach ($rules as &$rule) { - return TRUE; + // Let 'required' always be the first (non-callback) rule + if ($rule === 'required') + { + array_unshift($new_rules, 'required'); + } + // 'isset' is a kind of a weird alias for 'required' ... + elseif ($rule === 'isset' && (empty($new_rules) OR $new_rules[0] !== 'required')) + { + array_unshift($new_rules, 'isset'); + } + // The old/classic 'callback_'-prefixed rules + elseif (is_string($rule) && strncmp('callback_', $rule, 9) === 0) + { + $callbacks[] = $rule; + } + // Proper callables + elseif (is_callable($rule)) + { + $callbacks[] = $rule; + } + // "Named" callables; i.e. array('name' => $callable) + elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1])) + { + $callbacks[] = $rule; + } + // Everything else goes at the end of the queue + else + { + $new_rules[] = $rule; + } } - // Validation fails - return FALSE; + return array_merge($callbacks, $new_rules); } // -------------------------------------------------------------------- @@ -367,34 +557,20 @@ class CI_Form_validation { /** * Traverse a multidimensional $_POST array index until the data is found * - * @access private * @param array * @param array - * @param integer + * @param int * @return mixed */ protected function _reduce_array($array, $keys, $i = 0) { - if (is_array($array)) + if (is_array($array) && isset($keys[$i])) { - if (isset($keys[$i])) - { - if (isset($array[$keys[$i]])) - { - $array = $this->_reduce_array($array[$keys[$i]], $keys, ($i+1)); - } - else - { - return NULL; - } - } - else - { - return $array; - } + return isset($array[$keys[$i]]) ? $this->_reduce_array($array[$keys[$i]], $keys, ($i+1)) : NULL; } - return $array; + // NULL must be returned for empty fields + return ($array === '') ? NULL : $array; } // -------------------------------------------------------------------- @@ -402,54 +578,36 @@ class CI_Form_validation { /** * Re-populate the _POST array with our finalized and processed data * - * @access private - * @return null + * @return void */ - protected function _reset_post_array() + protected function _reset_data_array(&$data) { foreach ($this->_field_data as $field => $row) { - if ( ! is_null($row['postdata'])) + if ($row['postdata'] !== NULL) { - if ($row['is_array'] == FALSE) + if ($row['is_array'] === FALSE) { - if (isset($_POST[$row['field']])) - { - $_POST[$row['field']] = $this->prep_for_form($row['postdata']); - } + isset($data[$field]) && $data[$field] = is_array($row['postdata']) ? NULL : $row['postdata']; } else { - // start with a reference - $post_ref =& $_POST; + $data_ref =& $data; // before we assign values, make a reference to the right POST key - if (count($row['keys']) == 1) + if (count($row['keys']) === 1) { - $post_ref =& $post_ref[current($row['keys'])]; + $data_ref =& $data[current($row['keys'])]; } else { foreach ($row['keys'] as $val) { - $post_ref =& $post_ref[$val]; + $data_ref =& $data_ref[$val]; } } - if (is_array($row['postdata'])) - { - $array = array(); - foreach ($row['postdata'] as $k => $v) - { - $array[$k] = $this->prep_for_form($v); - } - - $post_ref = $array; - } - else - { - $post_ref = $this->prep_for_form($row['postdata']); - } + $data_ref = $row['postdata']; } } } @@ -460,92 +618,38 @@ class CI_Form_validation { /** * Executes the Validation routines * - * @access private * @param array * @param array * @param mixed - * @param integer + * @param int * @return mixed */ protected function _execute($row, $rules, $postdata = NULL, $cycles = 0) { + $allow_arrays = in_array('is_array', $rules, TRUE); + // If the $_POST data is an array we will run a recursive call - if (is_array($postdata)) + // + // Note: We MUST check if the array is empty or not! + // Otherwise empty arrays will always pass validation. + if ($allow_arrays === FALSE && is_array($postdata) && ! empty($postdata)) { foreach ($postdata as $key => $val) { - $this->_execute($row, $rules, $val, $cycles); - $cycles++; - } - - return; - } - - // -------------------------------------------------------------------- - - // If the field is blank, but NOT required, no further tests are necessary - $callback = FALSE; - if ( ! in_array('required', $rules) AND is_null($postdata)) - { - // Before we bail out, does the rule contain a callback? - if (preg_match("/(callback_\w+(\[.*?\])?)/", implode(' ', $rules), $match)) - { - $callback = TRUE; - $rules = (array('1' => $match[1])); - } - else - { - return; - } - } - - // -------------------------------------------------------------------- - - // Isset Test. Typically this rule will only apply to checkboxes. - if (is_null($postdata) AND $callback == FALSE) - { - if (in_array('isset', $rules, TRUE) OR in_array('required', $rules)) - { - // Set the message type - $type = (in_array('required', $rules)) ? 'required' : 'isset'; - - if ( ! isset($this->_error_messages[$type])) - { - if (FALSE === ($line = $this->CI->lang->line($type))) - { - $line = 'The field was not set'; - } - } - else - { - $line = $this->_error_messages[$type]; - } - - // Build the error message - $message = sprintf($line, $this->_translate_fieldname($row['label'])); - - // Save the error message - $this->_field_data[$row['field']]['error'] = $message; - - if ( ! isset($this->_error_array[$row['field']])) - { - $this->_error_array[$row['field']] = $message; - } + $this->_execute($row, $rules, $val, $key); } return; } - // -------------------------------------------------------------------- - - // Cycle through each rule and run it - foreach ($rules As $rule) + $rules = $this->_prepare_rules($rules); + foreach ($rules as $rule) { $_in_array = FALSE; // We set the $postdata variable with the current data in our master array so that // each cycle of the loop is dealing with the processed data from the last cycle - if ($row['is_array'] == TRUE AND is_array($this->_field_data[$row['field']]['postdata'])) + if ($row['is_array'] === TRUE && is_array($this->_field_data[$row['field']]['postdata'])) { // We shouldn't need this safety, but just in case there isn't an array index // associated with this cycle we'll bail out @@ -557,120 +661,158 @@ class CI_Form_validation { $postdata = $this->_field_data[$row['field']]['postdata'][$cycles]; $_in_array = TRUE; } + // If we get an array field, but it's not expected - then it is most likely + // somebody messing with the form on the client side, so we'll just consider + // it an empty field + elseif ($allow_arrays === FALSE && is_array($this->_field_data[$row['field']]['postdata'])) + { + $postdata = NULL; + } else { $postdata = $this->_field_data[$row['field']]['postdata']; } - // -------------------------------------------------------------------- - // Is the rule a callback? - $callback = FALSE; - if (substr($rule, 0, 9) == 'callback_') + $callback = $callable = FALSE; + if (is_string($rule)) + { + if (strpos($rule, 'callback_') === 0) + { + $rule = substr($rule, 9); + $callback = TRUE; + } + } + elseif (is_callable($rule)) + { + $callable = TRUE; + } + elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1])) { - $rule = substr($rule, 9); - $callback = TRUE; + // We have a "named" callable, so save the name + $callable = $rule[0]; + $rule = $rule[1]; } // Strip the parameter (if exists) from the rule // Rules can contain a parameter: max_length[5] $param = FALSE; - if (preg_match("/(.*?)\[(.*)\]/", $rule, $match)) + if ( ! $callable && preg_match('/(.*?)\[(.*)\]/', $rule, $match)) + { + $rule = $match[1]; + $param = $match[2]; + } + + // Ignore empty, non-required inputs with a few exceptions ... + if ( + ($postdata === NULL OR ($allow_arrays === FALSE && $postdata === '')) + && $callback === FALSE + && $callable === FALSE + && ! in_array($rule, array('required', 'isset', 'matches'), TRUE) + ) { - $rule = $match[1]; - $param = $match[2]; + continue; } // Call the function that corresponds to the rule - if ($callback === TRUE) + if ($callback OR $callable !== FALSE) { - if ( ! method_exists($this->CI, $rule)) + if ($callback) { - continue; + if ( ! method_exists($this->CI, $rule)) + { + log_message('debug', 'Unable to find callback validation rule: '.$rule); + $result = FALSE; + } + else + { + // Run the function and grab the result + $result = $this->CI->$rule($postdata, $param); + } } + else + { + $result = is_array($rule) + ? $rule[0]->{$rule[1]}($postdata) + : $rule($postdata); - // Run the function and grab the result - $result = $this->CI->$rule($postdata, $param); + // Is $callable set to a rule name? + if ($callable !== FALSE) + { + $rule = $callable; + } + } // Re-assign the result to the master data array - if ($_in_array == TRUE) + if ($_in_array === TRUE) { - $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result; + $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result; } else { - $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result; - } - - // If the field isn't required and we just processed a callback we'll move on... - if ( ! in_array('required', $rules, TRUE) AND $result !== FALSE) - { - continue; + $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result; } } - else + elseif ( ! method_exists($this, $rule)) { - if ( ! method_exists($this, $rule)) + // If our own wrapper function doesn't exist we see if a native PHP function does. + // Users can use any native PHP function call that has one param. + if (function_exists($rule)) { - // If our own wrapper function doesn't exist we see if a native PHP function does. - // Users can use any native PHP function call that has one param. - if (function_exists($rule)) - { - $result = $rule($postdata); + // Native PHP functions issue warnings if you pass them more parameters than they use + $result = ($param !== FALSE) ? $rule($postdata, $param) : $rule($postdata); - if ($_in_array == TRUE) - { - $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result; - } - else - { - $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result; - } + if ($_in_array === TRUE) + { + $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result; } else { - log_message('debug', "Unable to find validation rule: ".$rule); + $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result; } - - continue; } - + else + { + log_message('debug', 'Unable to find validation rule: '.$rule); + $result = FALSE; + } + } + else + { $result = $this->$rule($postdata, $param); - if ($_in_array == TRUE) + if ($_in_array === TRUE) { - $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result; + $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result; } else { - $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result; + $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result; } } - // Did the rule test negatively? If so, grab the error. + // Did the rule test negatively? If so, grab the error. if ($result === FALSE) { - if ( ! isset($this->_error_messages[$rule])) + // Callable rules might not have named error messages + if ( ! is_string($rule)) { - if (FALSE === ($line = $this->CI->lang->line($rule))) - { - $line = 'Unable to access an error message corresponding to your field name.'; - } + $line = $this->CI->lang->line('form_validation_error_message_not_set').'(Anonymous function)'; } else { - $line = $this->_error_messages[$rule]; + $line = $this->_get_error_message($rule, $row['field']); } // Is the parameter we are inserting into the error message the name - // of another field? If so we need to grab its "field label" - if (isset($this->_field_data[$param]) AND isset($this->_field_data[$param]['label'])) + // of another field? If so we need to grab its "field label" + if (isset($this->_field_data[$param], $this->_field_data[$param]['label'])) { $param = $this->_translate_fieldname($this->_field_data[$param]['label']); } // Build the error message - $message = sprintf($line, $this->_translate_fieldname($row['label']), $param); + $message = $this->_build_error_msg($line, $this->_translate_fieldname($row['label']), $param); // Save the error message $this->_field_data[$row['field']]['error'] = $message; @@ -688,26 +830,47 @@ class CI_Form_validation { // -------------------------------------------------------------------- /** + * Get the error message for the rule + * + * @param string $rule The rule name + * @param string $field The field name + * @return string + */ + protected function _get_error_message($rule, $field) + { + // check if a custom message is defined through validation config row. + if (isset($this->_field_data[$field]['errors'][$rule])) + { + return $this->_field_data[$field]['errors'][$rule]; + } + // check if a custom message has been set using the set_message() function + elseif (isset($this->_error_messages[$rule])) + { + return $this->_error_messages[$rule]; + } + elseif (FALSE !== ($line = $this->CI->lang->line('form_validation_'.$rule))) + { + return $line; + } + + return $this->CI->lang->line('form_validation_error_message_not_set').'('.$rule.')'; + } + + // -------------------------------------------------------------------- + + /** * Translate a field name * - * @access private * @param string the field name * @return string */ protected function _translate_fieldname($fieldname) { - // Do we need to translate the field name? - // We look for the prefix lang: to determine this - if (substr($fieldname, 0, 5) == 'lang:') + // Do we need to translate the field name? We look for the prefix 'lang:' to determine this + // If we find one, but there's no translation for the string - just return it + if (sscanf($fieldname, 'lang:%s', $line) === 1 && FALSE === ($fieldname = $this->CI->lang->line($line, FALSE))) { - // Grab the variable - $line = substr($fieldname, 5); - - // Were we able to translate the field name? If not we use $line - if (FALSE === ($fieldname = $this->CI->lang->line($line))) - { - return $line; - } + return $line; } return $fieldname; @@ -716,25 +879,60 @@ class CI_Form_validation { // -------------------------------------------------------------------- /** + * Build an error message using the field and param. + * + * @param string The error message line + * @param string A field's human name + * @param mixed A rule's optional parameter + * @return string + */ + protected function _build_error_msg($line, $field = '', $param = '') + { + // Check for %s in the string for legacy support. + if (strpos($line, '%s') !== FALSE) + { + return sprintf($line, $field, $param); + } + + return str_replace(array('{field}', '{param}'), array($field, $param), $line); + } + + // -------------------------------------------------------------------- + + /** + * Checks if the rule is present within the validator + * + * Permits you to check if a rule is present within the validator + * + * @param string the field name + * @return bool + */ + public function has_rule($field) + { + return isset($this->_field_data[$field]); + } + + // -------------------------------------------------------------------- + + /** * Get the value from a form * * Permits you to repopulate a form field with the value it was submitted * with, or, if that value doesn't exist, with the default * - * @access public * @param string the field name * @param string - * @return void + * @return string */ public function set_value($field = '', $default = '') { - if ( ! isset($this->_field_data[$field])) + if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata'])) { return $default; } // If the data is an array output them one at a time. - // E.g: form_input('name[]', set_value('name[]'); + // E.g: form_input('name[]', set_value('name[]'); if (is_array($this->_field_data[$field]['postdata'])) { return array_shift($this->_field_data[$field]['postdata']); @@ -751,37 +949,36 @@ class CI_Form_validation { * Enables pull-down lists to be set to the value the user * selected in the event of an error * - * @access public * @param string * @param string + * @param bool * @return string */ public function set_select($field = '', $value = '', $default = FALSE) { - if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) + if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata'])) { - if ($default === TRUE AND count($this->_field_data) === 0) - { - return ' selected="selected"'; - } - return ''; + return ($default === TRUE && count($this->_field_data) === 0) ? ' selected="selected"' : ''; } $field = $this->_field_data[$field]['postdata']; - + $value = (string) $value; if (is_array($field)) { - if ( ! in_array($value, $field)) + // Note: in_array('', array(0)) returns TRUE, do not use it + foreach ($field as &$v) { - return ''; + if ($value === $v) + { + return ' selected="selected"'; + } } + + return ''; } - else + elseif (($field === '' OR $value === '') OR ($field !== $value)) { - if (($field == '' OR $value == '') OR ($field != $value)) - { - return ''; - } + return ''; } return ' selected="selected"'; @@ -795,37 +992,36 @@ class CI_Form_validation { * Enables radio buttons to be set to the value the user * selected in the event of an error * - * @access public * @param string * @param string + * @param bool * @return string */ public function set_radio($field = '', $value = '', $default = FALSE) { - if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) + if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata'])) { - if ($default === TRUE AND count($this->_field_data) === 0) - { - return ' checked="checked"'; - } - return ''; + return ($default === TRUE && count($this->_field_data) === 0) ? ' checked="checked"' : ''; } $field = $this->_field_data[$field]['postdata']; - + $value = (string) $value; if (is_array($field)) { - if ( ! in_array($value, $field)) + // Note: in_array('', array(0)) returns TRUE, do not use it + foreach ($field as &$v) { - return ''; + if ($value === $v) + { + return ' checked="checked"'; + } } + + return ''; } - else + elseif (($field === '' OR $value === '') OR ($field !== $value)) { - if (($field == '' OR $value == '') OR ($field != $value)) - { - return ''; - } + return ''; } return ' checked="checked"'; @@ -839,40 +1035,15 @@ class CI_Form_validation { * Enables checkboxes to be set to the value the user * selected in the event of an error * - * @access public * @param string * @param string + * @param bool * @return string */ public function set_checkbox($field = '', $value = '', $default = FALSE) { - if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) - { - if ($default === TRUE AND count($this->_field_data) === 0) - { - return ' checked="checked"'; - } - return ''; - } - - $field = $this->_field_data[$field]['postdata']; - - if (is_array($field)) - { - if ( ! in_array($value, $field)) - { - return ''; - } - } - else - { - if (($field == '' OR $value == '') OR ($field != $value)) - { - return ''; - } - } - - return ' checked="checked"'; + // Logic is exactly the same as for radio fields + return $this->set_radio($field, $value, $default); } // -------------------------------------------------------------------- @@ -880,20 +1051,14 @@ class CI_Form_validation { /** * Required * - * @access public * @param string * @return bool */ public function required($str) { - if ( ! is_array($str)) - { - return (trim($str) == '') ? FALSE : TRUE; - } - else - { - return ( ! empty($str)); - } + return is_array($str) + ? (empty($str) === FALSE) + : (trim((string) $str) !== ''); } // -------------------------------------------------------------------- @@ -901,19 +1066,13 @@ class CI_Form_validation { /** * Performs a Regular Expression match test. * - * @access public * @param string - * @param regex + * @param string regex * @return bool */ public function regex_match($str, $regex) { - if ( ! preg_match($regex, $str)) - { - return FALSE; - } - - return TRUE; + return (bool) preg_match($regex, $str); } // -------------------------------------------------------------------- @@ -921,64 +1080,68 @@ class CI_Form_validation { /** * Match one field to another * - * @access public - * @param string - * @param field + * @param string $str string to compare against + * @param string $field * @return bool */ public function matches($str, $field) { - if ( ! isset($_POST[$field])) - { - return FALSE; - } + return isset($this->_field_data[$field], $this->_field_data[$field]['postdata']) + ? ($str === $this->_field_data[$field]['postdata']) + : FALSE; + } - $field = $_POST[$field]; + // -------------------------------------------------------------------- - return ($str !== $field) ? FALSE : TRUE; + /** + * Differs from another field + * + * @param string + * @param string field + * @return bool + */ + public function differs($str, $field) + { + return ! (isset($this->_field_data[$field]) && $this->_field_data[$field]['postdata'] === $str); } - + // -------------------------------------------------------------------- /** - * Match one field to another + * Is Unique * - * @access public - * @param string - * @param field + * Check if the input value doesn't already exist + * in the specified database field. + * + * @param string $str + * @param string $field * @return bool */ public function is_unique($str, $field) { - list($table, $field)=explode('.', $field); - $query = $this->CI->db->limit(1)->get_where($table, array($field => $str)); - - return $query->num_rows() === 0; - } + sscanf($field, '%[^.].%[^.]', $table, $field); + return isset($this->CI->db) + ? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0) + : FALSE; + } // -------------------------------------------------------------------- /** * Minimum Length * - * @access public * @param string - * @param value + * @param string * @return bool */ public function min_length($str, $val) { - if (preg_match("/[^0-9]/", $val)) + if ( ! is_numeric($val)) { return FALSE; } - if (function_exists('mb_strlen')) - { - return (mb_strlen($str) < $val) ? FALSE : TRUE; - } - - return (strlen($str) < $val) ? FALSE : TRUE; + return ($val <= mb_strlen($str)); } // -------------------------------------------------------------------- @@ -986,24 +1149,18 @@ class CI_Form_validation { /** * Max Length * - * @access public * @param string - * @param value + * @param string * @return bool */ public function max_length($str, $val) { - if (preg_match("/[^0-9]/", $val)) + if ( ! is_numeric($val)) { return FALSE; } - if (function_exists('mb_strlen')) - { - return (mb_strlen($str) > $val) ? FALSE : TRUE; - } - - return (strlen($str) > $val) ? FALSE : TRUE; + return ($val >= mb_strlen($str)); } // -------------------------------------------------------------------- @@ -1011,24 +1168,64 @@ class CI_Form_validation { /** * Exact Length * - * @access public * @param string - * @param value + * @param string * @return bool */ public function exact_length($str, $val) { - if (preg_match("/[^0-9]/", $val)) + if ( ! is_numeric($val)) + { + return FALSE; + } + + return (mb_strlen($str) === (int) $val); + } + + // -------------------------------------------------------------------- + + /** + * 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(strtolower($matches[1]), array('http', 'https'), TRUE)) + { + return FALSE; + } + + $str = $matches[2]; + } + + // Apparently, FILTER_VALIDATE_URL doesn't reject digit-only names for some reason ... + // See https://github.com/bcit-ci/CodeIgniter/issues/5755 + if (ctype_digit($str)) { return FALSE; } - if (function_exists('mb_strlen')) + // PHP 7 accepts IPv6 addresses within square brackets as hostnames, + // but it appears that the PR that came in with https://bugs.php.net/bug.php?id=68039 + // was never merged into a PHP 5 branch ... https://3v4l.org/8PsSN + if (preg_match('/^\[([^\]]+)\]/', $str, $matches) && ! is_php('7') && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== FALSE) { - return (mb_strlen($str) != $val) ? FALSE : TRUE; + $str = 'ipv6.host'.substr($str, strlen($matches[1]) + 2); } - return (strlen($str) != $val) ? FALSE : TRUE; + return (filter_var('http://'.$str, FILTER_VALIDATE_URL) !== FALSE); } // -------------------------------------------------------------------- @@ -1036,13 +1233,24 @@ class CI_Form_validation { /** * Valid Email * - * @access public * @param string * @return bool */ public function valid_email($str) { - return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $str)) ? FALSE : TRUE; + if (function_exists('idn_to_ascii') && preg_match('#\A([^@]+)@(.+)\z#', $str, $matches)) + { + $domain = defined('INTL_IDNA_VARIANT_UTS46') + ? idn_to_ascii($matches[2], 0, INTL_IDNA_VARIANT_UTS46) + : idn_to_ascii($matches[2]); + + if ($domain !== FALSE) + { + $str = $matches[1].'@'.$domain; + } + } + + return (bool) filter_var($str, FILTER_VALIDATE_EMAIL); } // -------------------------------------------------------------------- @@ -1050,7 +1258,6 @@ class CI_Form_validation { /** * Valid Emails * - * @access public * @param string * @return bool */ @@ -1063,7 +1270,7 @@ class CI_Form_validation { foreach (explode(',', $str) as $email) { - if (trim($email) != '' && $this->valid_email(trim($email)) === FALSE) + if (trim($email) !== '' && $this->valid_email(trim($email)) === FALSE) { return FALSE; } @@ -1077,10 +1284,9 @@ class CI_Form_validation { /** * Validate IP Address * - * @access public * @param string - * @param string "ipv4" or "ipv6" to validate a specific ip format - * @return string + * @param string 'ipv4' or 'ipv6' to validate a specific IP format + * @return bool */ public function valid_ip($ip, $which = '') { @@ -1090,15 +1296,39 @@ class CI_Form_validation { // -------------------------------------------------------------------- /** + * Validate MAC address + * + * @param string $mac + * @return bool + */ + public function valid_mac($mac) + { + if ( ! is_php('5.5')) + { + // Most common format, with either dash or colon delimiters + if (preg_match('#\A[0-9a-f]{2}(?<delimiter>[:-])([0-9a-f]{2}(?P=delimiter)){4}[0-9a-f]{2}\z#i', $mac)) + { + return TRUE; + } + + // The less common format; e.g. 0123.4567.89ab + return (bool) preg_match('#((\A|\.)[0-9a-f]{4}){3}\z#i', $mac); + } + + return (bool) filter_var($mac, FILTER_VALIDATE_MAC); + } + + // -------------------------------------------------------------------- + + /** * Alpha * - * @access public * @param string * @return bool */ public function alpha($str) { - return ( ! preg_match("/^([a-z])+$/i", $str)) ? FALSE : TRUE; + return ctype_alpha($str); } // -------------------------------------------------------------------- @@ -1106,56 +1336,52 @@ class CI_Form_validation { /** * Alpha-numeric * - * @access public * @param string * @return bool */ public function alpha_numeric($str) { - return ( ! preg_match("/^([a-z0-9])+$/i", $str)) ? FALSE : TRUE; + return ctype_alnum((string) $str); } // -------------------------------------------------------------------- /** - * Alpha-numeric with underscores and dashes + * Alpha-numeric w/ spaces * - * @access public * @param string * @return bool */ - public function alpha_dash($str) + public function alpha_numeric_spaces($str) { - return ( ! preg_match("/^([-a-z0-9_-])+$/i", $str)) ? FALSE : TRUE; + return (bool) preg_match('/^[A-Z0-9 ]+$/i', $str); } // -------------------------------------------------------------------- /** - * Numeric + * Alpha-numeric with underscores and dashes * - * @access public * @param string * @return bool */ - public function numeric($str) + public function alpha_dash($str) { - return (bool)preg_match( '/^[\-+]?[0-9]*\.?[0-9]+$/', $str); - + return (bool) preg_match('/^[a-z0-9_-]+$/i', $str); } // -------------------------------------------------------------------- /** - * Is Numeric + * Numeric * - * @access public * @param string * @return bool */ - public function is_numeric($str) + public function numeric($str) { - return ( ! is_numeric($str)) ? FALSE : TRUE; + return (bool) preg_match('/^[\-+]?[0-9]*\.?[0-9]+$/', $str); + } // -------------------------------------------------------------------- @@ -1163,7 +1389,6 @@ class CI_Form_validation { /** * Integer * - * @access public * @param string * @return bool */ @@ -1177,7 +1402,6 @@ class CI_Form_validation { /** * Decimal number * - * @access public * @param string * @return bool */ @@ -1189,19 +1413,29 @@ class CI_Form_validation { // -------------------------------------------------------------------- /** - * Greather than + * Greater than * - * @access public * @param string + * @param int * @return bool */ public function greater_than($str, $min) { - if ( ! is_numeric($str)) - { - return FALSE; - } - return $str > $min; + return is_numeric($str) ? ($str > $min) : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Equal to or Greater than + * + * @param string + * @param int + * @return bool + */ + public function greater_than_equal_to($str, $min) + { + return is_numeric($str) ? ($str >= $min) : FALSE; } // -------------------------------------------------------------------- @@ -1209,104 +1443,83 @@ class CI_Form_validation { /** * Less than * - * @access public * @param string + * @param int * @return bool */ public function less_than($str, $max) { - if ( ! is_numeric($str)) - { - return FALSE; - } - return $str < $max; + return is_numeric($str) ? ($str < $max) : FALSE; } // -------------------------------------------------------------------- /** - * Is a Natural number (0,1,2,3, etc.) + * Equal to or Less than * - * @access public * @param string + * @param int * @return bool */ - public function is_natural($str) + public function less_than_equal_to($str, $max) { - return (bool) preg_match( '/^[0-9]+$/', $str); + return is_numeric($str) ? ($str <= $max) : FALSE; } // -------------------------------------------------------------------- /** - * Is a Natural number, but not a zero (1,2,3, etc.) + * Value should be within an array of values * - * @access public + * @param string * @param string * @return bool */ - public function is_natural_no_zero($str) + public function in_list($value, $list) { - if ( ! preg_match( '/^[0-9]+$/', $str)) - { - return FALSE; - } + return in_array($value, explode(',', $list), TRUE); + } - if ($str == 0) - { - return FALSE; - } + // -------------------------------------------------------------------- - return TRUE; + /** + * Is a Natural number (0,1,2,3, etc.) + * + * @param string + * @return bool + */ + public function is_natural($str) + { + return ctype_digit((string) $str); } // -------------------------------------------------------------------- /** - * Valid Base64 - * - * Tests a string for characters outside of the Base64 alphabet - * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045 + * Is a Natural number, but not a zero (1,2,3, etc.) * - * @access public * @param string * @return bool */ - public function valid_base64($str) + public function is_natural_no_zero($str) { - return (bool) ! preg_match('/[^a-zA-Z0-9\/\+=]/', $str); + return ($str != 0 && ctype_digit((string) $str)); } // -------------------------------------------------------------------- /** - * Prep data for form + * Valid Base64 * - * This function allows HTML to be safely shown in a form. - * Special characters are converted. + * Tests a string for characters outside of the Base64 alphabet + * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045 * - * @access public * @param string - * @return string + * @return bool */ - public function prep_for_form($data = '') + public function valid_base64($str) { - if (is_array($data)) - { - foreach ($data as $key => $val) - { - $data[$key] = $this->prep_for_form($val); - } - - return $data; - } - - if ($this->_safe_form_data == FALSE OR $data === '') - { - return $data; - } - - return str_replace(array("'", '"', '<', '>'), array("'", """, '<', '>'), stripslashes($data)); + return (base64_encode(base64_decode($str)) === $str); } // -------------------------------------------------------------------- @@ -1314,20 +1527,14 @@ class CI_Form_validation { /** * Prep URL * - * @access public * @param string * @return string */ public function prep_url($str = '') { - if ($str == 'http://' OR $str == '') - { - return ''; - } - - if (substr($str, 0, 7) != 'http://' && substr($str, 0, 8) != 'https://') + if ($str !== '' && stripos($str, 'http://') !== 0 && stripos($str, 'https://') !== 0) { - $str = 'http://'.$str; + return 'http://'.$str; } return $str; @@ -1338,45 +1545,44 @@ class CI_Form_validation { /** * Strip Image Tags * - * @access public * @param string * @return string */ public function strip_image_tags($str) { - return $this->CI->input->strip_image_tags($str); + return $this->CI->security->strip_image_tags($str); } // -------------------------------------------------------------------- /** - * XSS Clean + * Convert PHP tags to entities * - * @access public * @param string * @return string */ - public function xss_clean($str) + public function encode_php_tags($str) { - return $this->CI->security->xss_clean($str); + return str_replace(array('<?', '?>'), array('<?', '?>'), $str); } // -------------------------------------------------------------------- /** - * Convert PHP tags to entities + * Reset validation vars * - * @access public - * @param string - * @return string + * Prevents subsequent validation routines from being affected by the + * results of any previous validation routine due to the CI singleton. + * + * @return CI_Form_validation */ - public function encode_php_tags($str) + public function reset_validation() { - return str_replace(array('<?php', '<?PHP', '<?', '?>'), array('<?php', '<?PHP', '<?', '?>'), $str); + $this->_field_data = array(); + $this->_error_array = array(); + $this->_error_messages = array(); + $this->error_string = ''; + return $this; } } -// END Form Validation Class - -/* End of file Form_validation.php */ -/* Location: ./system/libraries/Form_validation.php */ |