Add support for callable form validation rules
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php
index 0c4f949..0a80888 100644
--- a/system/libraries/Form_validation.php
+++ b/system/libraries/Form_validation.php
@@ -144,7 +144,7 @@
* Set Rules
*
* This function takes an array of field names and validation
- * rules as input, any custom error messages, validates the info,
+ * rules as input, any custom error messages, validates the info,
* and stores it
*
* @param mixed $field
@@ -153,7 +153,7 @@
* @param array $errors
* @return CI_Form_validation
*/
- public function set_rules($field, $label = '', $rules = '', $errors = array())
+ public function set_rules($field, $label = '', $rules = array(), $errors = array())
{
// No reason to set rules if we have no POST data
// or a validation array has not been specified
@@ -187,26 +187,32 @@
return $this;
}
- // Convert an array of rules to a string
- if (is_array($rules))
- {
- $rules = implode('|', $rules);
- }
-
// No fields? Nothing to do...
- if ( ! is_string($field) OR ! is_string($rules) OR $field === '')
+ if ( ! is_string($field) OR $field === '')
{
return $this;
}
+ elseif ( ! is_array($rules))
+ {
+ // BC: Convert pipe-separated rules string to an array
+ if (is_string($rules))
+ {
+ $rules = explode('|', $rules);
+ }
+ else
+ {
+ return $this;
+ }
+ }
// If the field label wasn't passed we use the field name
$label = ($label === '') ? $field : $label;
// 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
- $indexes = array();
- if (preg_match_all('/\[(.*?)\]/', $field, $matches))
+ if (($is_array = (bool) preg_match_all('/\[(.*?)\]/', $field, $matches)) === TRUE)
{
+ $indexes = array();
sscanf($field, '%[^[][', $indexes[0]);
for ($i = 0, $c = count($matches[0]); $i < $c; $i++)
@@ -219,10 +225,6 @@
$is_array = TRUE;
}
- else
- {
- $is_array = FALSE;
- }
// Build our master array
$this->_field_data[$field] = array(
@@ -468,7 +470,7 @@
continue;
}
- $this->_execute($row, explode('|', $row['rules']), $this->_field_data[$field]['postdata']);
+ $this->_execute($row, $row['rules'], $this->_field_data[$field]['postdata']);
}
// Did we end up with any errors?
@@ -591,19 +593,33 @@
if ( ! in_array('required', $rules) && ($postdata === NULL OR $postdata === ''))
{
// Before we bail out, does the rule contain a callback?
- if (preg_match('/(callback_\w+(\[.*?\])?)/', implode(' ', $rules), $match))
+ foreach ($rules as &$rule)
{
- $callback = TRUE;
- $rules = array(1 => $match[1]);
+ if (is_string($rule))
+ {
+ if (strncmp($rule, 'callback_', 9) === 0)
+ {
+ $callback = TRUE;
+ $rules = array(1 => $rule);
+ break;
+ }
+ }
+ elseif (is_callable($rule))
+ {
+ $callback = TRUE;
+ $rules = array(1 => $rule);
+ break;
+ }
}
- else
+
+ if ( ! $callback)
{
return;
}
}
// Isset Test. Typically this rule will only apply to checkboxes.
- if (($postdata === NULL OR $postdata === '') && $callback === FALSE)
+ if (($postdata === NULL OR $postdata === '') && ! $callback)
{
if (in_array('isset', $rules, TRUE) OR in_array('required', $rules))
{
@@ -668,39 +684,55 @@
// somebody messing with the form on the client side, so we'll just consider
// it an empty field
$postdata = is_array($this->_field_data[$row['field']]['postdata'])
- ? NULL
- : $this->_field_data[$row['field']]['postdata'];
+ ? NULL
+ : $this->_field_data[$row['field']]['postdata'];
}
// Is the rule a callback?
- $callback = FALSE;
- if (strpos($rule, 'callback_') === 0)
+ $callback = $callable = FALSE;
+ if (is_string($rule))
{
- $rule = substr($rule, 9);
- $callback = TRUE;
+ if (strpos($rule, 'callback_') === 0)
+ {
+ $rule = substr($rule, 9);
+ $callback = TRUE;
+ }
+ }
+ elseif (is_callable($rule))
+ {
+ $callable = TRUE;
}
// 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];
}
// Call the function that corresponds to the rule
- if ($callback === TRUE)
+ if ($callback OR $callable)
{
- if ( ! method_exists($this->CI, $rule))
+ if ($callback)
{
- log_message('debug', 'Unable to find callback validation rule: '.$rule);
- $result = FALSE;
+ 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
{
- // Run the function and grab the result
- $result = $this->CI->$rule($postdata, $param);
+ $result = is_array($rule)
+ ? $rule[0]->{$rule[1]}($postdata, $param)
+ : $rule($postdata, $param);
}
// Re-assign the result to the master data array
@@ -725,6 +757,7 @@
// Users can use any native PHP function call that has one param.
if (function_exists($rule))
{
+ // 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)