* Copyright 2001-2012 Strangecode, LLC * * This file is part of The Strangecode Codebase. * * The Strangecode Codebase is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your option) * any later version. * * The Strangecode Codebase is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * The Strangecode Codebase. If not, see . */ /** * FormValidator.inc.php * * The FormValidator class provides a method for validating input from * http requests and displaying errors. * * @requires codebase/lib/Validator.inc.php * @author Quinn Comendant * @version 1.8 * * Example of use: --------------------------------------------------------------------- // The object that validates form input. require_once 'codebase/lib/FormValidator.inc.php'; $fv = new FormValidator(); $fv->empty('field_name', sprintf(_("%s cannot be blank."), _("Field name")), MSG_ERR, __FILE__, __LINE__); $fv->stringLength('field_name', 0, 255, sprintf(_("%s must be %d-to-%d characters in length."), _("Field name"), 0, 255), MSG_ERR, __FILE__, __LINE__); $fv->isInteger('field_name', sprintf(_("%s must be an integer."), _("Field name")), MSG_ERR, __FILE__, __LINE__); $fv->checkRegex('field_name', '/^\d{4}$|^$/', true, sprintf(_("%s must be in MMYY format."), _("Field name")), MSG_ERR, __FILE__, __LINE__); $fv->numericRange('field_name', 0, 65535, sprintf(_("%s must be a number between %d and %d."), _("Field name"), 0, 65535), MSG_ERR, __FILE__, __LINE__); $fv->validatePhone('field_name', MSG_ERR, __FILE__, __LINE__); $fv->validateEmail('field_name', MSG_ERR, __FILE__, __LINE__); $fv->validateStrDate('field_name', sprintf(_("%s must be a valid date in YYYY-MM-DD format."), _("Field name")), MSG_ERR, __FILE__, __LINE__); if (is_null($var)) { $fv->addError('field_name', sprintf(_("%s is invalid."), _("Field name")), MSG_ERR, __FILE__, __LINE__); } if ($fv->anyErrors()) { // Errors! } --------------------------------------------------------------------- */ // Credit card types are defined in class Validator. require_once 'codebase/lib/Validator.inc.php'; class FormValidator { // Class parameters. protected $_params = array( 'error' => ' sc-msg-error ', 'warning' => ' sc-msg-warning ', 'notice' => ' sc-msg-notice ', 'success' => ' sc-msg-success ', 'use_raise_msg' => false, ); // Array filling with error messages. public $errors = array(); /** * FormValidator constructor. * * @access public * @param array $params Configuration parameters for this object. */ public function __construct($params=array()) { // Set custom parameters. $this->setParam($params); } /** * Set (or overwrite existing) parameters by passing an array of new parameters. * * @access public * @param array $params Array of parameters (key => val pairs). */ public function setParam($params) { $app =& App::getInstance(); if (isset($params) && is_array($params)) { // Merge new parameters with old overriding only those passed. $this->_params = array_merge($this->_params, $params); } else { $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__); } } /** * Return the value of a parameter, if it exists. * * @access public * @param string $param Which parameter to return. * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return mixed Configured parameter value. */ public function getParam($param) { $app =& App::getInstance(); if (array_key_exists($param, $this->_params)) { return $this->_params[$param]; } else { $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__); return null; } } /** * Return the current list of errors. * * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @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 */ public 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__. */ public function addError($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null) { $app =& App::getInstance(); if (true === $this->getParam('use_raise_msg')) { $app->raiseMsg($msg, $type, $file, $line); } $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 * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if any errors were found, or if found for * a variable of $form_name, false otherwise */ public function anyErrors($form_name=null) { if (isset($form_name)) { foreach ($this->errors as $err) { if ($err['name'] == $form_name) { return $err['type']; } } return false; } else { return (sizeof($this->errors) > 0); } } /** * Reset the error list. */ public function resetErrorList() { $this->errors = array(); } /** * Prints the HTML for displaying error messages. * * @param string $above Additional message to print above error messages (e.g. "Oops!"). * @param string $below Additional message to print below error messages (e.g. "Please fix and resubmit"). * @param string $print_gotohash_js Print a line of javascript that scrolls the browser window down to view any error messages. * @param string $hash The #hashtag to scroll to. * @access public * @author Quinn Comendant * @since 15 Jul 2005 01:39:14 */ public function printErrorMessages($above='', $below='', $print_gotohash_js=false, $hash='sc-msg-formvalidator') { $app =& App::getInstance(); if ($this->anyErrors()) { // data-gotohash="…" is to be used by sites that refuse inline JS (via CSP) but needs to be referenced from an external JS. ?>
getErrorList() as $e) { if ('' != $e['message'] && is_string($e['message'])) { if (error_reporting() > 0 && $app->getParam('display_errors') && isset($e['file']) && isset($e['line'])) { echo "\n'; } switch ($e['type']) { case MSG_ERR: echo '
' . $e['message'] . '
'; break; case MSG_WARNING: echo '
' . $e['message'] . '
'; break; case MSG_SUCCESS: echo '
' . $e['message'] . '
'; break; case MSG_NOTICE: default: echo '
' . $e['message'] . '
'; break; } } } if ('' != $below) { ?>
anyErrors($form_name))) { if (isset($marker)) { echo $marker; } else { switch ($type) { case MSG_ERR: default: echo $this->getParam('error'); break; case MSG_WARNING: echo $this->getParam('warning'); break; case MSG_NOTICE: echo $this->getParam('notice'); break; case MSG_SUCCESS: echo $this->getParam('success'); break; } } } } /** * Ensure the length of string is non-zero. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if form is not empty, false otherwise. */ public function notEmpty($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null) { if (Validator::notEmpty(getFormData($form_name), LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, $msg, $type, $file, $line); return false; } } /* * We were using the isEmpty method *wrong* for years and should have been using notEmpty because it is more grammatically correct. * Because the only use is to ensure a value is not empty, we're simply going to alias this method to notEmpty(). * @since 03 Jun 2006 22:56:46 */ public function isEmpty($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null) { return $this->notEmpty($form_name, $msg, $type, $file, $line); } /** * 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 * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if form is a string, false otherwise. */ public function isString($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null) { if (Validator::isString(getFormData($form_name), LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, $msg, $type, $file, $line); return false; } } /** * 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 * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if no errors found, false otherwise */ public function isNumber($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null) { if (Validator::isNumber(getFormData($form_name), LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, $msg, $type, $file, $line); return false; } } /** * 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 * @param bool $negative_ok Set to true if negative numbers will be allowed. * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if value is an integer */ public function isInteger($form_name, $msg='', $negative_ok=false, $type=MSG_ERR, $file=null, $line=null) { if (Validator::isInteger(getFormData($form_name), $negative_ok, LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, $msg, $type, $file, $line); return false; } } /** * 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 * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if value is a float */ public function isFloat($form_name, $msg='', $negative_ok=false, $type=MSG_ERR, $file=null, $line=null) { if (Validator::isFloat(getFormData($form_name), $negative_ok, LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, $msg, $type, $file, $line); return false; } } /** * Check whether input is a Decimal or Fixed type. Use to check values to be stored in mysql decimal, numeric, num, or fixed types. * The arguments $max and $dec should match M and D of the column definition "DECIMAL(M,D)". * Note: some integers and floats will also pass this test. * https://dev.mysql.com/doc/refman/5.5/en/fixed-point-types.html * * @param string $form_name The name of the incoming form variable * @param int $max Total max number of digits. * @param int $dec Total max number of digits after the decimal place. * @param string $msg The message to display on error * @param bool $negative_ok If the value can be unsigned. * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool True if value is a decimal, false otherwise. */ public function isDecimal($form_name, $max=10, $dec=2, $negative_ok=false, $msg='', $type=MSG_ERR, $file=null, $line=null) { if (Validator::isDecimal(getFormData($form_name), $max, $dec, $negative_ok, LOG_NOTICE, $file, $line)) { return true; } else { // Set the example to a sequence of Ns with $max number of digits and a faction part length of $dec. $msg = str_replace('{EX}', sprintf('%s.%s', str_repeat('N', $max - $dec), str_repeat('N', $dec)), $msg); $this->addError($form_name, $msg, $type, $file, $line); return false; } } /** * 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 * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if value is an array */ public function isArray($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null) { if (Validator::isArray(getFormData($form_name), LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, $msg, $type, $file, $line); return false; } } /** * 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 $valid_on_match Set to true to be valid if match, or false to be valid if the match fails. * @param string $msg The message to display on error * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if value passes regex test (or false if $valid_on_match=false) */ public function checkRegex($form_name, $regex, $valid_on_match=true, $msg='', $type=MSG_ERR, $file=null, $line=null) { if (Validator::checkRegex(getFormData($form_name), $regex, $valid_on_match, LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, $msg, $type, $file, $line); return false; } } /** * 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 * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if string length is within given boundaries */ public function stringLength($form_name, $min, $max, $msg='', $type=MSG_ERR, $file=null, $line=null) { if (Validator::stringLength(getFormData($form_name), $min, $max, LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, $msg, $type, $file, $line); return false; } } /** * 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 * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if no errors found, false otherwise */ public function numericRange($form_name, $min, $max, $msg='', $type=MSG_ERR, $file=null, $line=null) { if (Validator::numericRange(getFormData($form_name), $min, $max, LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, $msg, $type, $file, $line); return false; } } /** * Validates an email address based on the recommendations in RFC 3696. * Is more loose than restrictive, to allow the many valid variants of * email addresses while catching the most common mistakes. * http://www.faqs.org/rfcs/rfc822.html * http://www.faqs.org/rfcs/rfc2822.html * http://www.faqs.org/rfcs/rfc3696.html * http://www.faqs.org/rfcs/rfc1035.html * * @access public * @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) * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool Validity of address. * @author Quinn Comendant */ public function validateEmail($form_name, $strict=false, $type=MSG_ERR, $file=null, $line=null) { $app =& App::getInstance(); $email = getFormData($form_name); if ('' == trim($email)) { // No email address provided, and that's okay. return true; } // Validator::validateEmail() returns a value that relates to the Validate::EMAIL_* constants (defined in Validator.inc.php). switch (Validator::validateEmail($email, $strict, LOG_NOTICE, $file, $line)) { case Validator::EMAIL_REGEX_FAIL: // Failed regex match. $this->addError($form_name, sprintf(_("The email address %s is formatted incorrectly."), oTxt($email)), $type, $file, $line); $app->logMsg(sprintf('The email address %s is not valid.', oTxt($email)), LOG_DEBUG, __FILE__, __LINE__); return false; case Validator::EMAIL_LENGTH_FAIL: // Failed length requirements. $this->addError($form_name, sprintf(_("The email address %s is too long (email addresses must have fewer than 256 characters)."), oTxt($email)), $type, $file, $line); $app->logMsg(sprintf('The email address %s must contain less than 256 characters.', oTxt($email)), LOG_DEBUG, __FILE__, __LINE__); return false; case Validator::EMAIL_MX_FAIL: // Failed MX record test. $this->addError($form_name, sprintf(_("The email address %s does not have a valid domain name"), oTxt($email)), $type, $file, $line); $app->logMsg(sprintf('The email address %s does not have a valid domain name.', oTxt($email)), LOG_NOTICE, __FILE__, __LINE__); return false; case Validator::EMAIL_SUCCESS: default : 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) 555-1212 * * @param string $form_name the name of the incoming form variable * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if no errors found, false otherwise */ public function validatePhone($form_name, $type=MSG_ERR, $file=null, $line=null) { $app =& App::getInstance(); $phone = getFormData($form_name); // Validator::validateEmail() returns a value that relates to the Validate::PHONE_* constants (defined in Validator.inc.php). switch (Validator::validatePhone($phone, LOG_NOTICE, $file, $line)) { case Validator::PHONE_REGEX_FAIL: // Failed regex match. $this->addError($form_name, sprintf(_("The phone number %s is not valid."), oTxt($phone)), $type, $file, $line); $app->logMsg(sprintf('The phone number %s is not valid.', oTxt($phone)), LOG_DEBUG, __FILE__, __LINE__); return false; case Validator::PHONE_LENGTH_FAIL: // Failed length requirements. $this->addError($form_name, sprintf(_("The phone number %s is too long (phone number must have fewer than 25 characters)."), oTxt($phone)), $type, $file, $line); $app->logMsg(sprintf('The phone number %s must contain less than 25 characters.', oTxt($phone)), LOG_DEBUG, __FILE__, __LINE__); return false; case Validator::PHONE_SUCCESS: default : return true; } } /** * 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 * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if no errors found, false otherwise */ public function validateStrDate($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null) { $app =& App::getInstance(); if (Validator::validateStrDate(getFormData($form_name, ''), LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, $msg, $type, $file, $line); $app->logMsg(sprintf('The string date %s is not valid.', getFormData($form_name)), LOG_DEBUG, __FILE__, __LINE__); return false; } } /** * Verifies credit card number using the Luhn (mod 10) algorithm. * http://en.wikipedia.org/wiki/Luhn_algorithm * * @param string $form_name The name of the incoming form variable. * @param string $cc_type Optional, card type to do specific checks. One of the Validator::CC_TYPE_* constants. * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if no errors found, false otherwise */ public function validateCCNumber($form_name, $cc_type=null, $type=MSG_ERR, $file=null, $line=null) { $cc_num = getFormData($form_name); if (Validator::validateCCNumber($cc_num, $cc_type, LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, sprintf(_("The credit card number you entered is not valid. Please check the number and try again."), $cc_num), $type, $file, $line); return false; } } /** * Check whether a file was selected for uploading. If file is missing, it's an error. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if no errors found, false otherwise */ public function fileUploaded($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null) { if (Validator::fileUploaded($form_name, LOG_NOTICE, $file, $line)) { return true; } else { $this->addError($form_name, $msg, $type, $file, $line); return false; } } /** * Check whether a file was selected for uploading. If file is missing, it's an error. * * @param string $form_name the name of the incoming form variable * @param string $msg the message to display on error * @param const $type A LOG_* constant (see App->logMsg()) * @param const $file Filename to log (usually __FILE__) * @param const $line Line number to log (usually __LINE__) * @return bool true if no errors found, false otherwise */ public function fileUploadSize($form_name, $msg='', $type=MSG_ERR, $file=null, $line=null) { if (Validator::fileUploadSize($form_name, LOG_NOTICE, $file, $line)) { return true; } else { $msg = '' == $msg ? sprintf(_("Maximum filesize exceeded. Got %s, but limit is %s."), humanFileSize($_SERVER['CONTENT_LENGTH']), humanFileSize(phpIniGetBytes('upload_max_filesize'))) : $msg; $this->addError($form_name, $msg, $type, $file, $line); return false; } } } // THE END