PHP 5 Form Validation class
I'm here today for introducing you the new version of my PHP Form Validation Class.
Just another validation class? Maybe. But I've been using it for many years when I don't use other frameworks to develop my applications. And I find it really useful and easy to use. So today I want to share it with you as it could be useful to you too.
So I've decided to release it licensed as GPL to allow you to use it for free and also change it to best fit your own needs.
So, first I'm giving you the source code, then I'll show you some practical examples for implementing it into your code and some suggestions for using it at its top.
First of all, what are its stronger features? Well, it can easily validate your html form simply by passing your data to validate and a validation map as arrays.
Then you can pass another array with a translation map, so it can be easily localized amd error messages are translated into your desired language.
Another feature is made by the static validators, that can be used to validate data without creating any class instance. And, subclassing it, you can easily overwrite my validators with your own.
So, let's see some code.
Code:
<?php/*** Class FormValidate** Form Validation class* This new version includes many new features and a completed re-engeneering in PHP 5** @category FormValidation* @package IWEPhpFramework* @author Alessandro Perrone <info@iweconsulting.net>* @copyright 2008-2013 IWE Consulting* @license http://opensource.org/licenses/GPL-3.0 GNU General Public License, version 3 (GPL-3.0)* @version 1.4**/class FormValidate {/*** Translations array* @access protected* @var array*/protected $translations = array();/*** Translations array* @access protected* @var array*/protected $error_messages = array();/*** Email confirmation* @access protected* @var string*/protected static $confirm_email = NULL;/*** Password confirmation (if the form treats this kind of information)* @access protected* @var string*/protected static $confirm_password = NULL;/*** Recaptcha private key* @access protected* @var string*/protected static $recaptcha_pkey = NULL;/*** Data associative array (contains data to validate)* @access protected* @var array (associative)*/protected $data;/*** E-mail check switch* @access protected* @var boolean*/protected static $email_processed = false;/*** Password check switch* @access protected* @var boolean*/protected static $password_processed = false;/*** Validation schema* @access protected* @var array (multidimensional)*/protected $validator;/*** Password fields minimum lenght* @access public* @var int*/public static $password_min_lenght = 8;/*** Static Errors* @access public* @var boolean*/public static $errors = false;/*** Static Errors list* @access public* @var array*/public static $errors_list = array();/*** Static Validation success* @access public* @var boolean*/public static $success = false;/*** Static Warnings* @access public* @var unknown*/public static $warnings = false;/*** Static Warnings list* @access public* @var unknown*/public static $warnings_list = array();/*** FormValidate constructor* @param array $validation_array (multidimensional with validation schema)* @param array $data (ssociative with data to be validated)* @access public*/public function __construct(Array $validation_array, Array $data, Array $translations = array()){/*foreach($validation_array as $key => $value) echo "$key: $value<br />\n";echo "---------------------<br />";foreach($data as $key => $value) echo "$key: $value<br />\n";*/$this->confirm_email = NULL;$this->confirm_password = NULL;$this->email_processed = false;$this->password_processed = false;$this->data = $data;$this->validator = $validation_array;$this->initErrorMessages();$this->setTranslations($translations);}/*** Initialize error messages array* @access private*/final private function initErrorMessages(){$this->error_messages = array('FIELD_PRIVACY' => '','FIELD_LENGHT' => '','FIELD_MAX_LENGHT' => '','FIELD_MIN_LENGHT' => '','FIELD_REQUIRED' => '','FIELD_TYPE_STRING' => '','FIELD_TYPE_NUMBER' => '','FIELD_TYPE_MONEY' => '','FIELD_TYPE_TELEPHONE' => '','FIELD_TYPE_CONFIRM_PASSWORD' => '','FIELD_TYPE_SIMPLE_PASSWORD' => '','FIELD_TYPE_ALPHANUMERIC' => '','FIELD_TYPE_PASSWORD' => '','FIELD_TYPE_PASSWORD_HINT' => '','FIELD_TYPE_EMAIL' => '','FIELD_TYPE_CONFIRM_EMAIL' => '','FIELD_TYPE_TEXT' => '','FIELD_TYPE_DATE' => '','FIELD_TYPE_CAPTCHA' => '','FIELD_TYPE_RECAPTCHA' => '',);}/*** Setup error messages translations.* This can be done in two ways: passing an array with translated messages (only specified keys will be* translated) as argument for this method or, if subclassing, overwriting this method and setting up* a new translations array in the class $translations member* @access protected* @param array $translations*/protected function setTranslations(Array $translations = array()){$this->translations = array('FIELD_PRIVACY' => "You must agree with the privacy agreement.",'FIELD_LENGHT' => "Field <em>'%s'</em> should be %s chars long.",'FIELD_MAX_LENGHT' => "Field <em>'%s'</em> could contain only %s chars.",'FIELD_MIN_LENGHT' => "Field <em>'%s'</em> could contain only %s chars.",'FIELD_REQUIRED' => "Field <em>'%s'</em> is required.",'FIELD_TYPE_STRING' => "Field <em>'%s'</em> should be a string.",'FIELD_TYPE_NUMBER' => "Field <em>'%s'</em> should be a number.",'FIELD_TYPE_MONEY' => "Field <em>'%s'</em> is not a valid money data.",'FIELD_TYPE_TELEPHONE' => "Field <em>'%s'</em> is not a valid telephone number.",'FIELD_TYPE_CONFIRM_PASSWORD' => "Check password is different from password.",'FIELD_TYPE_SIMPLE_PASSWORD' => "Field <em>'%s'</em> is too short (must be " . self::$password_min_lenght . " chars long).",'FIELD_TYPE_ALPHANUMERIC' => "Field <em>'%s'</em> should be alphanumeric.",'FIELD_TYPE_PASSWORD' => "Field <em>'%s'</em> is not a valida password.",'FIELD_TYPE_PASSWORD_HINT' => "Field <em>'%s'</em> may contain special chars to increase security (i.e. \"passWord.25\")",'FIELD_TYPE_EMAIL' => "Field <em>'%s'</em> is not a valid e-mail.",'FIELD_TYPE_CONFIRM_EMAIL' => "Check e-mail is different from e-mail.",'FIELD_TYPE_TEXT' => "Field <em>'%s'</em> should contain more information.",'FIELD_TYPE_DATE' => "Field <em>'%s'</em> is not a valid date: valid format is mm-gg-aaaa.",'FIELD_TYPE_CAPTCHA' => "Captcha text is not valid!",'FIELD_TYPE_RECAPTCHA' => "Invalid Recaptcha!",);if(!empty($translations)){foreach($translations as $key => $value){if(array_key_exists($key, $this->translations)){$this->translations[$key] = $value;}}}$this->setErrorMessages();}/*** Create object of error messages to use as validation output* @access private*/final private function setErrorMessages(){$tmp = array();foreach($this->translations as $key => $value){if(array_key_exists($key, $this->error_messages) && !empty($value)){$tmp[strtolower($key)] = $value;}else{$tmp[strtolower($key)] = $this->error_messages[$key];}}$this->error_messages = $tmp;unset($tmp);$this->error_messages = $this->__toObject($this->error_messages);}/*** validate method* execute data validation for each data of the $data array parsing each time the relative validation schema* @param array $validation_array (multidimensional with validation schema)* @param array $data (associative with data to be validated)* @access public* @return array/boolean*/public function validate(){## INIT VARIABLES$errors = $warnings = array();$return = array();## CREATE VALIDATION MAP$data_to_validate = array();foreach($this->validator as $key => $value){//if(!in_array(strtolower($key), $this->data))$key = strtolower($key);if(!array_key_exists($key, $this->data))return false;else$data_to_validate[$key] = $this->data[$key];}## EXECUTE VALIDATION ON MAPPED POST DATA//if(count($this->validator) == count($this->data)){//foreach($this->data as $key=>$value){foreach($data_to_validate as $key => $value){//echo $this->validator[$key] . "<br />";$validate = $this->check_data($key, $value, $this->validator[$key]);if(count($validate) > 0){if(array_key_exists('error_description', $validate)){self::$errors = true;$errors[] = array('field' => $key, 'message' => $validate['error_description']);}if(array_key_exists('warning', $validate)){self::$warnings = true;$warnings[] = array('field' => $key, 'message' => $validate['warning']);}}}## IF AN ERROR OCCURS RETURN FALSEif(!empty($errors) && !empty($warnings)){$return['success'] = false;$return['errors'] = self::$errors_list = array_merge($errors, $warnings);return $return;}elseif(!empty($errors)){$return['success'] = false;$return['errors'] = self::$errors_list = $errors;return $return;## IF A WARNING OCCURS RETURN TRUE GIVING THE DEVELOPER CHOICE TO GIVE USER A SECOND CHANCE}elseif(!empty($warnings)){$return['success'] = self::$success = true;$return['warnings'] = self::$warnings_list = $warnings;return $return;## IF ALL IS OK RETURN TRUE}elsereturn true;/*}elsereturn false;*/}/*** check_data method* execute data validation for the data passed referring to the data validation schema.* @param string $field (name of the current field/variable to be parsed)* @param mixed $data (data to be checked)* @param array $check_table (validation schema)* @access private* @return array*/protected function check_data($field, $data, $check_table){## INIT VARIABLES$error = $warning = "";$return = array();$lenght = array_key_exists('lenght', $check_table) ? $check_table['lenght'] : false;$minlenght = array_key_exists('minlenght', $check_table) ? $check_table['minlenght'] : false;$maxlenght = array_key_exists('maxlenght', $check_table) ? $check_table['maxlenght'] : false;$required = array_key_exists('required', $check_table) ? $check_table['required'] : false;$type = array_key_exists('type', $check_table) ? $check_table['type'] : false;self::$recaptcha_pkey = array_key_exists('recaptcha_pk', $check_table) ? $check_table['recaptcha_pk'] : false;## IF THE FIELD IS REQUIRED CHECK IF EMPTY AND RETURN AN ERRORif($required){if(empty($data)){$error .= ($field == "privacy") || ($field == "consenso_privacy") ? $this->error_messages->field_privacy : sprintf($this->error_messages->field_required, ucfirst($field));//$return['error_description'] = nl2br($error);$return['error_description'] = $error;return $return;}}## IF THE FIELD IS REQUIRED AND CONTAINS DATA OR IS OPTIONAL AND CONTAINS DATA EXECUTE LENGHT AND DATATYPE CHECKS# OR DO NOTHING IF IT'S A VOID (NULL) OPTIONAL FIELDif(!empty($data) && $data != "-"){## TRIM SPACES$data = trim($data);## EVALUATE LENGHT IF REQUIREDif(!empty($lenght)){if(strlen($data) < $lenght) $error .= sprintf($this->error_messages->field_lenght, ucfirst($field), $lenght);}if(!empty($minlenght)){if(strlen($data) < $minlenght) $error .= sprintf($this->error_messages->field_minlenght, ucfirst($field), $minlenght);}if(!empty($maxlenght)){if(strlen($data) > $maxlenght) $error .= sprintf($this->error_messages->field_maxlenght, ucfirst($field), $maxlenght);}## DATATYPE VALIDATIONswitch($type){case 'string':$validation = $this->validateString($data);if($validation === false){$error .= sprintf($this->error_messages->field_lenght, ucfirst($field));}else if($validation === -1){$error .= sprintf($this->error_messages->field_required, ucfirst($field));}break;case 'int':case 'number':if(!$this->validateNumber($data))$error .= sprintf($this->error_messages->field_type_number, ucfirst($field));break;case 'money':if(!$this->validateMoney($data))$error .= sprintf($this->error_messages->field_type_money, ucfirst($field));break;case 'telephone':if(!$this->validateTelephone($data))$error .= sprintf($this->error_messages->field_type_telephone, ucfirst($field));break;case 'confirm_password':if(!$this->validateConfirmPassword($data))$error .= $this->error_messages->field_type_confirm_password;break;case 'simple_password':if(!$this->validateSimplePassword($data))$error .= sprintf($this->error_messages->field_type_simple_password, ucfirst($field));break;case 'password':$validation = $this->validatePassword($data);if($validation === false){$error .= sprintf($this->error_messages->field_type_simple_password, ucfirst($field));}else if($validation === -1){$warning .= sprintf($this->error_messages->field_type_password_hint, ucfirst($field));}else if($validation === -2){$error .= sprintf($this->error_messages->field_type_alphanumeric, ucfirst($field));}break;case 'email':if(!$this->validateEmail($data))$error .= sprintf($this->error_messages->field_type_email, ucfirst($field));break;case 'confirm_email':if(!$this->validateConfirmEmail($data))$error .= $this->error_messages->field_type_confirm_email;break;case 'text':if(!$this->validateText($data))$error .= sprintf($this->error_messages->field_type_text, ucfirst($field));break;case 'date':if(!$this->validateDate($data))$error .= sprintf($this->error_messages->field_type_date, ucfirst($field));break;case 'checktext':if(!$this->validateCaptcha($data))$error .= $this->error_messages->field_type_captcha;break;case 'recaptcha':// remember to include recaptchalib on your main file!$validation = $this->validateRecaptcha($data);if($validation === false){$error .= $this->error_messages->field_type_recaptcha;}else if($validation !== true){$error .= $validation;}break;case 'privacy':if(!$this->validatePrivacy($data))$error .= $this->error_messages->field_type_privacy;break;case NULL:$error .= NULL;$warning .= NULL;break;}}## FILL RETURN DATA IF ERRORS OR WARNINGS OCCUREDif(!empty($warning)){$return['warning'] = nl2br($warning);}if(!empty($error)){$return['error_description'] = nl2br($error);}## RETURN VALIDATED DATAreturn $return;}/*** Validate String* @param string $string* @return Ambigous <boolean, number>* @access public*/public static function validateString($string = null){$return = false;if(is_string($string) || strstr($string, "...") === false)$return = true;else if(strstr($string, "...") !== false)$return = -1;return $return;}/*** Validate Privacy* @param string $privacy* @return boolean* @access public*/public static function validatePrivacy($privacy = null){$return = false;if(intval($privacy) == 1){$return = true;}return $return;}/*** Validate Numeric field* @param string $number* @return boolean* @access public*/public static function validateNumber($number = null){$return = false;if(is_numeric($number)){$return = true;}return $return;}/*** Validate Email* @param string $email* @return boolean* @access public*/public static function validateEmail($email = null){$return = false;self::$email_processed = true;if(preg_match("/^([\w.-]+)@(([a-zA-Z0-9_-])+|([a-zA-Z0-9_-])+.([a-zA-Z0-9_-])+)\.(\w){2,4}$/", $email)){self::$confirm_email = $email;$return = true;}return $return;}/*** Validate Confirmation Email* @param string $email* @return boolean* @access public*/public static function validateConfirmEmail($email = null){$return = false;if(self::$email_processed && !empty(self::$confirm_email)){if($email == self::$confirm_email){$return = true;}}return $return;}/*** Validate Simple Password* @param string $password* @return boolean* @access public*/public static function validateSimplePassword($password = null){$return = false;self::$password_processed = true;if(preg_match("/^(.){" . self::$password_min_lenght . ",}$/", $password)){self::$confirm_password = $password;$return = true;}return $return;}/*** Validate Password* @param string $password* @return Ambigous <boolean, number>* @access public*/public static function validatePassword($password = null){$return = false;self::$password_processed = true;if(preg_match("/^(.){" . self::$password_min_lenght . ",}$/", $password)){if(!preg_match("/^(\D)+$/", $password)){self::$confirm_password = $password;if(preg_match("/[\/\^!?\"'[\]+\-.:,;()|\\]+/", $password)){$return = true;}else{$return = -1;}}else{$return = -2;}}return $return;}/*** Validate Confirmation Password* @param string $password* @return boolean* @access public*/public static function validateConfirmPassword($password = null){$return = false;if(self::$password_processed && !empty(self::$confirm_password)){if($password == self::$confirm_password){$return = true;}}return $return;}/*** Validate Money data type* @param string $money* @return boolean* @access public*/public static function validateMoney($money = null){$return = false;if(preg_match('/^([\d]+)(\.|,)([\d]{2})$/', $money)){$return = true;}return $return;}/*** Validate Telephone number* @param string $telephone* @return boolean* @access public*/public static function validateTelephone($telephone = null){$return = false;if(preg_match("/^(\+\d\d)?[\s.\-\/]?([0]?[1-9]{1,3})[\s.\-\/]?([0-9]{4,8})$/", $telephone)){$return = true;}return $return;}/*** Validate Text field (long text)* @param string $text* @return boolean* @access public*/public static function validateText($text = null){$return = false;if(is_string($text) && strlen($text) >= 10){$return = true;}return $return;}/*** Validate Date data type* @param string $date* @return boolean* @access public*/public static function validateDate($date = null){$return = false;if(preg_match("/^(0?[1-9]|1[012])[\-.\/](0?[1-9]|[12][0-9]|3[01])[\-.\/]((19|20)\d\d)$/", $date)){$return = true;}return $return;}/*** Validate Captcha string* @param string $string* @return boolean* @access public*/public static function validateCaptcha($string = null){$return = false;session_start();if(strtolower($string) == strtolower($_SESSION['randomStr'])){$return = true;}return $return;}/*** Validate Recaptcha field* @param string $string* @return boolean* @access public*/public static function validateRecaptcha($string = null){// remember to include recaptchalib on your main file!$return = false;if(!empty(self::$recaptcha_pkey)){$fields = preg_split("/\[\]/", $string);$resp = recaptcha_check_answer(self::$recaptcha_pkey,$_SERVER["REMOTE_ADDR"],$fields[1],$fields[0]);if($resp->is_valid){$return = true;}else{$return = $resp->error;}}return $return;}/*** Return an array as object notation* @return Object* @access private*/final private function __toObject(Array $array){$obj = new stdClass;foreach($array as $k => $v){if(is_array($v)){$obj->{$k} = $this->__toObject($v); //RECURSION}else{$obj->{$k} = $v;}}return $obj;}}
You can download the class file and see its code here.
Ok, that's the class. And what now? How to use it? As I told you before, you have to create your html form, then you must create two arrays, one for the data to validate and one with a validation map that the class has to follow to complete validation.
Pay attention to your form inputs, they must contain an id attribute to match the validation key map.
Here's a basic validation example:
Code:
<?phpif(!empty($_POST)){$data = array('name' => $_POST['name'],'surname' => $_POST['surname'],'phone' => $_POST['phone'],'email' => $_POST['email'],'email_confirmation' => $_POST['email_confirmation'],'password' => $_POST['password'],'password_confirmation' => $_POST['password_confirmation'],'address' => $_POST['address'],'city' => $_POST['city'],'birth_date' => $_POST['birthdate']['month'].'-'.$_POST['birthdate']['day'].'-'.$_POST['birthdate']['year'],);$validation_key = array('name' => array('required' => true, 'type' => 'string'),'surname' => array('required' => true, 'type' => 'string'),'phone' => array('required' => true, 'type' => 'telephone'),'email' => array('required' => true, 'type' => 'email'),'email_confirmation' => array('required' => true, 'type' => 'confirm_email'),'password' => array('required' => true, 'type' => 'password'),'password_confirmation' => array('required' => true, 'type' => 'confirm_password'),'address' => array('required' => true, 'type' => 'string'),'city' => array('required' => true, 'type' => 'string'),'birth_date' => array('required' => false, 'type' => 'date'),);$validator = new FormValidate($validation_key, $data);if($validator === true){// SEND FORM DATA OR PERFORM OTHER ACTIONS}else{// VALIDATION ERRORS}}else{// SHOW HTML FORM}
As you can see, the class return true on a totally valid form, else it returns an array, containing a success key and errors and/or warnings keys: if there are errors the success is false, if there are warnings the success is true and you will decide what to do with you form.
You can also checking for errors and warnings with the public class members $errors and $warnings and access error and warning messages statically using the class members $warnings_list and $errors_list. Obviously you have to do this only after calling the validate() method.
You can manage validation also from Ajax forms, you only have to remember to pass your data with the right keys names. Here's an example using JQuery:
Code:
$('#your_form').submit(function(e){e.preventDefault();// gather fields datadata = {name: $('#name').val(),surname: $('#surname').val(),phone: $('#phone').val(),email: $('#email').val(),email_confirmation: $('#email_confirmation').val(),password: $('#password').val(),password_confirmation: $('#password_confirmation').val(),address: $('#address').val(),city: $('#city').val(),birthdate: {day: $('#birthdate_day').val(), month: $('#birthdate_month').val(), year: $('#birthdate_year').val()},};$.ajax({type: 'POST',dataType: 'json',url: $(this).attr('action') + '/validate',data: data,success: function(json){var success = json === true ? json : json.success;if(!success){var errors = json.errors;console.log(errors);}else{var warnings = json.warnings;if(typeof warnings !== 'undefined'){console.log(warnings);}else{// send form}}}});});
Another cool feature is the translation/customization of error message. You can setup your own error messages creating an associative array that must match the internal translation keys and passing it to the constructor:
Code:
<?php$translations = array('FIELD_PRIVACY' => 'Your translation','FIELD_LENGHT' => 'Your translation','FIELD_MAX_LENGHT' => 'Your translation','FIELD_MIN_LENGHT' => 'Your translation','FIELD_REQUIRED' => 'Your translation','FIELD_TYPE_STRING' => 'Your translation','FIELD_TYPE_NUMBER' => 'Your translation','FIELD_TYPE_MONEY' => 'Your translation','FIELD_TYPE_TELEPHONE' => 'Your translation','FIELD_TYPE_CONFIRM_PASSWORD' => 'Your translation','FIELD_TYPE_SIMPLE_PASSWORD' => 'Your translation','FIELD_TYPE_ALPHANUMERIC' => 'Your translation','FIELD_TYPE_PASSWORD' => 'Your translation','FIELD_TYPE_PASSWORD_HINT' => 'Your translation','FIELD_TYPE_EMAIL' => 'Your translation','FIELD_TYPE_CONFIRM_EMAIL' => 'Your translation','FIELD_TYPE_TEXT' => 'Your translation','FIELD_TYPE_DATE' => 'Your translation','FIELD_TYPE_CAPTCHA' => 'Your translation','FIELD_TYPE_RECAPTCHA' => 'Your translation',);$validator = new FormValidate($validation_key, $data, $translations);
You can only overwrite the values you need as well, without customizing all the translation keys.
Please, make sure to keep variables where required (take a look at the internal translation for this). For example, for field lenght you will find an error message like "Field '%s' should be %s chars long." You can find a working example of this class at http://examples.iweconsulting.net/formvalidate/.I hope this class can be useful to you. Please, leave feedbacks! Thanks.
2 comments
Line 295 is now correct. In previous code it contained a wrong reference to the static variable $recaptcha_pkey ($this was used in place of self).
Records 1 - 2 of 2 total
Login or register to add a comment (registered users only)
Alessandro | Mar 25, 2013 5:23:54 PM
The working example is now at your disposal, I\'m sorry if someone wasn\'t able to access it before.