isEmpty('location_name', _("Location name cannot be blank.")); // $fv->checkRegex('cc_exp', '/^\d{4}$|^$/', true, _("CC exp date must be in MMYY format.")); // $fv->isInteger('client_id', _("Client id must be an integer.")); // $fv->numericRange('client_id', -32768, 32767, _("Client id must be a number between -32768 and 32767.")); // $fv->stringLength('zip', 0, 255, _("Zip must contain less than 256 characters.")); // $fv->validateEmail('invoice_email'); // $fv->validatePhone('phone1'); /** * The FormValidator:: class provides a method for validating input from * http requests and displaying errors. * * @requires This class requires App.inc.php * @requires This class requires Utilities.inc.php * @author Quinn Comendant * @version 1.5 */ require_once dirname(__FILE__) . '/App.inc.php'; require_once dirname(__FILE__) . '/Utilities.inc.php'; require_once dirname(__FILE__) . '/Prefs.inc.php'; class FormValidator { /** * Array filling with errors. The key will be the name of the form where * the data came from. The value will be a message to print to the user. */ var $errors = array(); /** * Return the current list of errors. * * @return array an array of errors in the following arrangement: * keys: the name of the variable with an error * vals: the message to display for that error */ function getErrorList() { return $this->errors; } /** * Add an error to the errors stack. * * @param string $form_name The name of the incoming form variable. * @param string $msg The error message for that form. * @param int $type The type of message: MSG_NOTICE, * MSG_SUCCESS, MSG_WARNING, or MSG_ERR. * @param string $file __FILE__. * @param string $line __LINE__. */ function addError($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null) { $this->errors[] = array( 'name' => $form_name, 'message' => $msg, 'type' => $type, 'file' => $file, 'line' => $line ); } /** * Check whether any errors have been triggered. * * @param string $form_name the name of the incoming form variable * * @return bool true if any errors were found, or if found for * a variable of $form_name, false otherwise */ function anyErrors($form_name=null) { if (isset($form_name)) { foreach ($this->errors as $err) { if ($err['name'] == $form_name) { return true; } } return false; } return (sizeof($this->errors) > 0); } /** * Reset the error list. */ function resetErrorList() { $this->errors = array(); } /** * If this form has an error, print an error marker like "<<". * * @param string $form_name the name of the incoming form variable * @param string $marker A string to print if there is an error. if * not provided, use default. */ function err($form_name, $marker=null) { global $CFG; if ($this->anyErrors($form_name)) { if (isset($marker)) { echo $marker; } else { echo '«'; } } } /** * Check whether input has a value. To be used when a value must be empty * under certain circumstances. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * * @return bool true if form is not empty, false otherwise. */ function notEmpty($form_name, $msg='') { $val = getFormData($form_name); if (is_array($val)) { if (!empty($val)) { $this->addError($form_name, $msg); return true; } else { return false; } } else { if (trim($val) != '') { $this->addError($form_name, $msg); return true; } else { return false; } } } /** * Check whether input is blank. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * * @return bool true if form is empty, false otherwise. */ function isEmpty($form_name, $msg='') { $val = getFormData($form_name); if (is_array($val)) { if (empty($val)) { $this->addError($form_name, $msg); return true; } else { return false; } } else { if (trim($val) == '') { $this->addError($form_name, $msg); return true; } else { return false; } } } /** * Check whether input is a string. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * * @return bool true if form is a string, false otherwise. */ function isString($form_name, $msg='') { $val = getFormData($form_name); if (!is_string($val) && $val != '') { $this->addError($form_name, $msg); return false; } else { return true; } } /** * Check whether input is a number. Allows negative numbers. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * * @return bool true if no errors found, false otherwise */ function isNumber($form_name, $msg='') { $val = getFormData($form_name); if (!is_numeric($val) && $val != '') { $this->addError($form_name, $msg); return false; } else { return true; } } /** * addError if input is NOT an integer. Don't just use is_int() because the * data coming from the user is *really* a string. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * * @return bool true if value is an integer */ function isInteger($form_name, $msg='', $negative_ok=false) { $val = getFormData($form_name); $pattern = $negative_ok ? '/^-?[[:digit:]]+$/' : '/^[[:digit:]]+$/'; if ((!is_numeric($val) || !preg_match($pattern, $val)) && $val != '') { $this->addError($form_name, $msg); return false; } else { return true; } } /** * Check whether input is a float. Don't just use is_float() because the * data coming from the user is *really* a string. Integers will also * pass this test. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * * @return bool true if value is a float */ function isFloat($form_name, $msg='', $negative_ok=false) { $val = getFormData($form_name); $pattern = $negative_ok ? '/^-?[[:digit:]]*(?:\.?[[:digit:]]+)$/' : '/^[[:digit:]]*(?:\.?[[:digit:]]+)$/'; if ((!is_numeric($val) || !preg_match($pattern, $val)) && $val != '') { $this->addError($form_name, $msg); return false; } else { return true; } } /** * Check whether input is an array. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * * @return bool true if value is a float */ function isArray($form_name, $msg='') { $val = getFormData($form_name); if (!is_array($val) && !empty($val)) { $this->addError($form_name, $msg); return false; } else { return true; } } /** * Check whether input matches the specified perl regular expression * pattern. * * @param string $form_name the name of the incoming form variable * @param int $regex perl regex that the string must match * @param bool $not set to false to be valid if match, or true * to be valid on no match * @param string $msg the message to display on error * * @return bool true if value passes regex test */ function checkRegex($form_name, $regex, $not, $msg='') { $val = getFormData($form_name); if ($not) { if (!preg_match($regex, $val)) { $this->addError($form_name, $msg); return false; } else { return true; } } else { if (preg_match($regex, $val)) { $this->addError($form_name, $msg); return false; } else { return true; } } } /** * Tests if the string length is between specified values. Whitespace excluded for min. * * @param string $form_name the name of the incoming form variable * @param int $min minimum length of string, inclusive * @param int $max maximum length of string, inclusive * @param string $msg the message to display on error * * @return bool true if string length is within given boundaries */ function stringLength($form_name, $min, $max, $msg='') { $val = getFormData($form_name); if (strlen(trim($val)) < $min || strlen($val) > $max) { $this->addError($form_name, $msg); return false; } else { return true; } } /** * Check whether input is within a valid numeric range. * * @param string $form_name the name of the incoming form variable * @param int $min minimum value of number, inclusive * @param int $max maximum value of number, inclusive * @param string $msg the message to display on error * * @return bool true if no errors found, false otherwise */ function numericRange($form_name, $min, $max, $msg='') { $val = getFormData($form_name); if ($val != '' && is_numeric($val)) { if ($val < $min || $val > $max) { $this->addError($form_name, $msg); return false; } return true; } else { // Not a number! return false; } } /** * Validates email address length, domain name existence, format. * * @param string $form_name The name of the incoming form variable * @param bool $strict Run strict tests (check if the domain exists and has an MX record assigned) * @return bool true if no errors found, false otherwise */ function validateEmail($form_name, $strict=false) { $email = getFormData($form_name); if ('' == trim($email)) { return false; } $regex = '/^(?:[^,@]*\s+|[^,@]*(<)|)' // Display name . '((?:[^.<>\s@\",\[\]]+[^<>\s@\",\[\]])*[^.<>\s@\",\[\]]+)' // Local-part . '@' // @ . '((?:(\[)|[A-Z0-9]?)' // Domain, first char . '(?(4)' // Domain conditional for if first domain char is [ . '(?:[0-9]{1,3}\.){3}[0-9]{1,3}\]' // TRUE, matches IP address . '|' . '[.-]?(?:[A-Z0-9]+[-.])*(?:[A-Z0-9]+\.)+[A-Z]{2,6}))' // FALSE, matches domain name . '(?(1)' // Comment conditional for if initial < exists . '(?:>\s*|>\s+\([^,@]+\)\s*)' // TRUE, ensure ending > . '|' . '(?:|\s*|\s+\([^,@]+\)\s*))$/i'; // Test email address format. if (!preg_match($regex, getFormData($form_name), $e_parts)) { $this->addError($form_name, sprintf(_("The email address %s is formatted incorrectly."), oTxt(getFormData($form_name))), MSG_ERR, __FILE__, __LINE__); logMsg(sprintf('The email address %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__); return false; } // We have a match! Here are the captured subpatterns, on which further tests are run. // The part before the @. $local = $e_parts[2]; // The part after the @. // If domain is an IP [XXX.XXX.XXX.XXX] strip off the brackets. $domain = $e_parts[3]{0} == '[' ? mb_substr($e_parts[3], 1, -1) : $e_parts[3]; // Test length. if (mb_strlen($local) > 64 || mb_strlen($domain) > 191) { $this->addError($form_name, sprintf(_("The email address %s is too long."), oTxt(getFormData($form_name))), MSG_ERR, __FILE__, __LINE__); logMsg(sprintf('The email address %s is too long.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__); return false; } if ($strict) { // Strict tests. if (ip2long($domain) === false && function_exists('checkdnsrr') && !checkdnsrr($domain . '.', 'MX') && gethostbyname($domain) == $domain) { // Check domain exists: It's a domain if ip2long fails; checkdnsrr ensures a MX record exists; gethostbyname() ensures the domain exists. $this->addError($form_name, sprintf(_("The email address %s does not have a valid domain name."), oTxt(getFormData($form_name))), MSG_ERR, __FILE__, __LINE__); logMsg(sprintf('%s (line %s) failed: %s', __METHOD__, __LINE__, getDump($email))); return false; } } return true; } /** * Check whether input is a valid phone number. Notice: it is now set * to allow characters like - or () or + so people can type in a phone * number that looks like: +1 (530) 624-4410 * * @param string $form_name the name of the incoming form variable * * @return bool true if no errors found, false otherwise */ function validatePhone($form_name) { $phone = getFormData($form_name); if ($this->checkRegex($form_name, '/^[0-9 +().-]*$/', true, sprintf(_("The phone number %s is not valid."), $phone)) && $this->stringLength($form_name, 0, 25, sprintf(_("The phone number %s is too long"), $phone))) { return true; } return false; } /** * Verifies that date can be processed by the strtotime function. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * * @return bool true if no errors found, false otherwise */ function validateStrDate($form_name, $msg='') { if (($timestamp = strtotime(getFormData($form_name))) === -1) { $this->addError($form_name, $msg); logMsg(sprintf('The string date %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__); return false; } else { return true; } } /** * Verifies credit card number. * * @param string $form_name The name of the incoming form variable. * @param string $cc_num Card number to verify. * @param string $cc_type Optional, card type to do specific checks. * * @return bool true if no errors found, false otherwise */ function validateCCNumber($form_name, $cc_num=null, $cc_type=null) { if (!isset($cc_num)) { $cc_num = getFormData($form_name); } if ('' == $cc_num) { return false; } // Innocent until proven guilty $card_is_valid = true; // Get rid of any non-digits $cc_num = preg_replace('/[^\d]/', '', $cc_num); // Perform card-specific checks, if applicable switch (strtolower($cc_type)) { case 'visa' : $card_is_valid = preg_match('/^4\d{15}$|^4\d{12}$/', $cc_num); break; case 'mastercard' : case 'mc' : $card_is_valid = preg_match('/^5[1-5]\d{14}$/', $cc_num); break; case 'american_express' : case 'american_ex' : case 'americanexpress' : case 'americanex' : case 'am_ex' : case 'amex' : case 'ae' : $card_is_valid = preg_match('/^3[47]\d{13}$/', $cc_num); break; case 'discover' : $card_is_valid = preg_match('/^6011\d{12}$/', $cc_num); break; case 'diners_club' : case 'dinersclub' : case 'diners' : case 'diner' : case 'dc' : $card_is_valid = preg_match('/^30[0-5]\d{11}$|^3[68]\d{12}$/', $cc_num); break; case 'jcb' : $card_is_valid = preg_match('/^3\d{15}$|^2131|1800\d{11}$/', $cc_num); break; } // The Luhn formula works right to left, so reverse the number. $cc_num = strrev($cc_num); $luhn_total = 0; $num = strlen($cc_num); for ($i=0; $i<$num; $i++) { // Get each digit. $digit = substr($cc_num,$i,1); // If it's an odd digit, double it. if ($i / 2 != floor($i / 2)) { $digit *= 2; } // If the result is two digits, add them. if (strlen($digit) == 2) { $digit = substr($digit,0,1) + substr($digit,1,1); } // Add the current digit to the $luhn_total. $luhn_total += $digit; } // If it passed (or bypassed) the card-specific check and the Total is evenly divisible by 10, it's cool! if ($card_is_valid && $luhn_total % 10 == 0) { return true; } else { $this->addError($form_name, _("The credit card number you entered is not valid.")); return false; } } /** * Check whether uploaded file is valid. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * * @return bool true if no errors found, false otherwise */ function validateFile($form_name, $msg='') { if (!isset($_FILES[$form_name]['tmp_name']) || '' == trim($_FILES[$form_name]['tmp_name'])) { $this->addError($form_name, $msg); return false; } else { return true; } } } // THE END ?>