Hi michatmaster7,
Okay, I've attached an actual tested version of the code. :-) I'll include this in the next Core version.
Let me know if you have any problems.
- Ben
EDIT: Weird, the file attachment option seems to have disappeared for me since I upgraded the forums a couple of days ago. Jeez. I'll just post the entire page code below.
Okay, I've attached an actual tested version of the code. :-) I'll include this in the next Core version.
Let me know if you have any problems.
- Ben
EDIT: Weird, the file attachment option seems to have disappeared for me since I upgraded the forums a couple of days ago. Jeez. I'll just post the entire page code below.
PHP Code:
<?php
/**
* validation.php
* --------------
*
* v2.3.4, Jan 2012
*
* This script provides generic validation for any web form. For a discussion and example usage
* of this script, go to http://www.benjaminkeen.com/software/php_validation
*
* This script is written by Ben Keen with additional code contributed by Mihai Ionescu and
* Nathan Howard. It is free to distribute, to re-write - to do what ever you want with it. The RSV
* script, found in /global/scripts is the client-side counterpart to this script. Both work exactly
* the same way, so once you write validation for one, you can literally & paste them to use them in
* the other. See the user doc for more info.
*/
// ------------------------------------------------------------------------------------------------
/**
* Generic form field validation.
*
* @param array fields the POST / GET fields from a form which need to be validated
* @param array rules an array of the validation rules. Each rule is a string of the form:
*
* "[if:FIELDNAME=VALUE,]REQUIREMENT,fieldname[,fieldname2 [,fieldname3, date_flag]],error message"
*
* if:FIELDNAME=VALUE, This allows us to only validate a field
* only if a fieldname FIELDNAME has a value VALUE. This
* option allows for nesting; i.e. you can have multiple
* if clauses, separated by a comma. They will be examined
* in the order in which they appear in the line.
*
* Valid REQUIREMENT strings are:
* "required" - field must be filled in
* "digits_only" - field must contain digits only
* "is_alpha" - field must only contain alphanumeric characters (0-9, a-Z)
* "custom_alpha" - field must be of the custom format specified.
* fieldname: the name of the field
* fieldname2: a character or sequence of special characters. These characters are:
* L An uppercase Letter. V An uppercase Vowel.
* l A lowercase letter. v A lowercase vowel.
* D A letter (upper or lower). F A vowel (upper or lower).
* C An uppercase Consonant. x Any number, 0-9.
* c A lowercase consonant. X Any number, 1-9.
* E A consonant (upper or lower).
* "reg_exp" - field must match the supplied regular expression.
* fieldname: the name of the field
* fieldname2: the regular expression
* fieldname3: (optional) flags for the reg exp (like i for case insensitive
* "letters_only" - field must only contains letters (a-Z)
*
* "length=X" - field has to be X characters long
* "length=X-Y" - field has to be between X and Y (inclusive) characters long
* "length>X" - field has to be greater than X characters long
* "length>=X" - field has to be greater than or equal to X characters long
* "length<X" - field has to be less than X characters long
* "length<=X" - field has to be less than or equal to X characters long*
*
* "valid_email" - field has to be valid email address
* "valid_date" - field has to be a valid date
* fieldname: MONTH
* fieldname2: DAY
* fieldname3: YEAR
* date_flag: "later_date" / "any_date"
* "same_as" - fieldname is the same as fieldname2 (for password comparison)
*
* "range=X-Y" - field must be a number between the range of X and Y inclusive
* "range>X" - field must be a number greater than X
* "range>=X" - field must be a number greater than or equal to X
* "range<X" - field must be a number less than X
* "range<=X" - field must be a number less than or equal to X
*
*
* Comments: With both digits_only, valid_email and is_alpha options, if the empty string is passed
* in it won't generate an error, thus allowing validation of non-required fields. So,
* for example, if you want a field to be a valid email address, provide validation for
* both "required" and "valid_email".
*/
function validate_fields($fields, $rules)
{
$errors = array();
// loop through rules
for ($i=0; $i<count($rules); $i++)
{
// split row into component parts
$row = explode(",", $rules[$i]);
// while the row begins with "if:..." test the condition. If true, strip the if:..., part and
// continue evaluating the rest of the line. Keep repeating this while the line begins with an
// if-condition. If it fails any of the conditions, don't bother validating the rest of the line
$satisfies_if_conditions = true;
while (preg_match("/^if:/", $row[0]))
{
$condition = preg_replace("/^if:/", "", $row[0]);
// check if it's a = or != test
$comparison = "equal";
$parts = array();
if (preg_match("/!=/", $condition))
{
$parts = explode("!=", $condition);
$comparison = "not_equal";
}
else
$parts = explode("=", $condition);
$field_to_check = $parts[0];
$value_to_check = $parts[1];
if ($comparison == "equal")
{
$fail = false;
// if the field being compared against doesn't exist, we reasonably say that the value won't be the same
if (!array_key_exists($field_to_check, $fields))
$fail = true;
// if the value being passed is an array (e.g. a checkbox / multi-select field), what would be considered
// "equal"? I think as long as the array contains AT LEAST that value, it's fair to pass this test
else if (is_array($fields[$field_to_check]))
{
if (!in_array($fields[$field_to_check], $fields))
$fail = true;
}
// lastly, do a straight string test
else if ($fields[$field_to_check] != $value_to_check)
$fail = true;
if ($fail)
{
$satisfies_if_conditions = false;
break;
}
else
{
array_shift($row);
}
}
else if ($comparison == "not_equal")
{
$fail = true;
// if the field doesn't even exist, we can say they're not equal (i.e. doesn't fail!)
if (!array_key_exists($field_to_check, $fields))
$fail = false;
else if (is_array($fields[$field_to_check]))
{
if (!in_array($fields[$field_to_check], $fields))
$fail = false;
}
else if ($fields[$field_to_check] != $value_to_check)
$fail = false;
if ($fail)
{
$satisfies_if_conditions = false;
break;
}
else
{
array_shift($row);
}
}
else
array_shift($row); // remove this if-condition from line, and continue validating line
}
if (!$satisfies_if_conditions)
continue;
$requirement = $row[0];
$field_name = $row[1];
// depending on the validation test, store the incoming strings for use later...
if (count($row) == 6) // valid_date
{
$field_name2 = $row[2];
$field_name3 = $row[3];
$date_flag = $row[4];
$error_message = $row[5];
}
else if (count($row) == 5) // reg_exp (WITH flags like g, i, m)
{
$field_name2 = $row[2];
$field_name3 = $row[3];
$error_message = $row[4];
}
else if (count($row) == 4) // same_as, custom_alpha, reg_exp (without flags like g, i, m)
{
$field_name2 = $row[2];
$error_message = $row[3];
}
else
$error_message = $row[2]; // everything else!
// if the requirement is "length=...", rename requirement to "length" for switch statement
if (preg_match("/^length/", $requirement))
{
$length_requirements = $requirement;
$requirement = "length";
}
// if the requirement is "range=...", rename requirement to "range" for switch statement
if (preg_match("/^range/", $requirement))
{
$range_requirements = $requirement;
$requirement = "range";
}
// now, validate whatever is required of the field
switch ($requirement)
{
case "required":
if (!isset($fields[$field_name]) || $fields[$field_name] == "")
$errors[] = $error_message;
break;
case "digits_only":
if (isset($fields[$field_name]) && preg_match("/\D/", $fields[$field_name]))
$errors[] = $error_message;
break;
case "letters_only":
if (isset($fields[$field_name]) && preg_match("/[^a-zA-Z]/", $fields[$field_name]))
$errors[] = $error_message;
break;
// doesn't fail if field is empty
case "valid_email":
$regexp="/^[a-z0-9]+([_\\.-][a-z0-9]+)*@([a-z0-9]+([\.-][a-z0-9]+)*)+\\.[a-z]{2,}$/i";
if (isset($fields[$field_name]) && !empty($fields[$field_name]) && !preg_match($regexp, $fields[$field_name]))
$errors[] = $error_message;
break;
case "length":
$comparison_rule = "";
$rule_string = "";
if (preg_match("/length=/", $length_requirements))
{
$comparison_rule = "equal";
$rule_string = preg_replace("/length=/", "", $length_requirements);
}
else if (preg_match("/length>=/", $length_requirements))
{
$comparison_rule = "greater_than_or_equal";
$rule_string = preg_replace("/length>=/", "", $length_requirements);
}
else if (preg_match("/length<=/", $length_requirements))
{
$comparison_rule = "less_than_or_equal";
$rule_string = preg_replace("/length<=/", "", $length_requirements);
}
else if (preg_match("/length>/", $length_requirements))
{
$comparison_rule = "greater_than";
$rule_string = preg_replace("/length>/", "", $length_requirements);
}
else if (preg_match("/length</", $length_requirements))
{
$comparison_rule = "less_than";
$rule_string = preg_replace("/length</", "", $length_requirements);
}
switch ($comparison_rule)
{
case "greater_than_or_equal":
if (!(strlen($fields[$field_name]) >= $rule_string))
$errors[] = $error_message;
break;
case "less_than_or_equal":
if (!(strlen($fields[$field_name]) <= $rule_string))
$errors[] = $error_message;
break;
case "greater_than":
if (!(strlen($fields[$field_name]) > $rule_string))
$errors[] = $error_message;
break;
case "less_than":
if (!(strlen($fields[$field_name]) < $rule_string))
$errors[] = $error_message;
break;
case "equal":
// if the user supplied two length fields, make sure the field is within that range
if (preg_match("/-/", $rule_string))
{
list($start, $end) = explode("-", $rule_string);
if (strlen($fields[$field_name]) < $start || strlen($fields[$field_name]) > $end)
$errors[] = $error_message;
}
// otherwise, check it's EXACTLY the size the user specified
else
{
if (strlen($fields[$field_name]) != $rule_string)
$errors[] = $error_message;
}
break;
}
break;
case "range":
$comparison_rule = "";
$rule_string = "";
if (preg_match("/range=/", $range_requirements))
{
$comparison_rule = "equal";
$rule_string = preg_replace("/range=/", "", $range_requirements);
}
else if (preg_match("/range>=/", $range_requirements))
{
$comparison_rule = "greater_than_or_equal";
$rule_string = preg_replace("/range>=/", "", $range_requirements);
}
else if (preg_match("/range<=/", $range_requirements))
{
$comparison_rule = "less_than_or_equal";
$rule_string = preg_replace("/range<=/", "", $range_requirements);
}
else if (preg_match("/range>/", $range_requirements))
{
$comparison_rule = "greater_than";
$rule_string = preg_replace("/range>/", "", $range_requirements);
}
else if (preg_match("/range</", $range_requirements))
{
$comparison_rule = "less_than";
$rule_string = preg_replace("/range</", "", $range_requirements);
}
switch ($comparison_rule)
{
case "greater_than":
if (!($fields[$field_name] > $rule_string))
$errors[] = $error_message;
break;
case "less_than":
if (!($fields[$field_name] < $rule_string))
$errors[] = $error_message;
break;
case "greater_than_or_equal":
if (!($fields[$field_name] >= $rule_string))
$errors[] = $error_message;
break;
case "less_than_or_equal":
if (!($fields[$field_name] <= $rule_string))
$errors[] = $error_message;
break;
case "equal":
list($start, $end) = explode("-", $rule_string);
if (($fields[$field_name] < $start) || ($fields[$field_name] > $end))
$errors[] = $error_message;
break;
}
break;
case "same_as":
if ($fields[$field_name] != $fields[$field_name2])
$errors[] = $error_message;
break;
case "valid_date":
// this is written for future extensibility of isValidDate function to allow
// checking for dates BEFORE today, AFTER today, IS today and ANY day.
$is_later_date = false;
if ($date_flag == "later_date")
$is_later_date = true;
else if ($date_flag == "any_date")
$is_later_date = false;
if (!is_valid_date($fields[$field_name], $fields[$field_name2], $fields[$field_name3], $is_later_date))
$errors[] = $error_message;
break;
case "is_alpha":
if (preg_match('/[^A-Za-z0-9]/', $fields[$field_name]))
$errors[] = $error_message;
break;
case "custom_alpha":
$chars = array();
$chars["L"] = "[A-Z]";
$chars["V"] = "[AEIOU]";
$chars["l"] = "[a-z]";
$chars["v"] = "[aeiou]";
$chars["D"] = "[a-zA-Z]";
$chars["F"] = "[aeiouAEIOU]";
$chars["C"] = "[BCDFGHJKLMNPQRSTVWXYZ]";
$chars["x"] = "[0-9]";
$chars["c"] = "[bcdfghjklmnpqrstvwxyz]";
$chars["X"] = "[1-9]";
$chars["E"] = "[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]";
$reg_exp_str = "";
for ($j=0; $j<strlen($field_name2); $j++)
{
if (array_key_exists($field_name2[$j], $chars))
$reg_exp_str .= $chars[$field_name2[$j]];
else
$reg_exp_str .= $field_name2[$j];
}
if (!empty($fields[$field_name]) && !preg_match("/$reg_exp_str/", $fields[$field_name]))
$errors[] = $error_message;
break;
case "reg_exp":
$reg_exp_str = $field_name2;
// rather crumby, but...
if (count($row) == 5)
$reg_exp = "/" . $reg_exp_str . "/" . $row[3];
else
$reg_exp = "/" . $reg_exp_str . "/";
if (!empty($fields[$field_name]) && !preg_match($reg_exp, $fields[$field_name]))
$errors[] = $error_message;
break;
default:
die("Unknown requirement flag in validate_fields(): $requirement");
break;
}
}
return $errors;
}
/**
* Checks a date is valid / is later than current date
*
* @param integer $month an integer between 1 and 12
* @param integer $day an integer between 1 and 31 (depending on month)
* @param integer $year a 4-digit integer value
* @param boolean $is_later_date if true, the function verifies the date being passed in is LATER
* than the current date.
*/
function is_valid_date($month, $day, $year, $is_later_date)
{
// depending on the year, calculate the number of days in the month
if ($year % 4 == 0) // LEAP YEAR
$days_in_month = array(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
else
$days_in_month = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
// first, check the incoming month and year are valid.
if (!$month || !$day || !$year) return false;
if (1 > $month || $month > 12) return false;
if ($year < 0) return false;
if (1 > $day || $day > $days_in_month[$month-1]) return false;
// if required, verify the incoming date is LATER than the current date.
if ($is_later_date)
{
// get current date
$today = date("U");
$date = mktime(0, 0, 0, $month, $day, $year);
if ($date < $today)
return false;
}
return true;
}