Just to top off my daily posts, I have been perfecting a PHP Validation class over the past few months.
At work I tend to do a lot of registration forms for competitions, subscriptions, newsletters, contact forms etc.
So I went to the trouble of creating a very quick and easy validation class that I could include into my projects at a drop of a hat. Inspired by CodeIgniters validation class, it has very similar features. Including helper functions.
It’s even UTF-8 friendly (except for the email function). Please feel free to download and test it out yourself.
Here is a dump of the code:
<?php
/************************************************************
Validation Class
Author: Jason Ashdown
Updated: 12:12 24/07/2008
Version 0.2j
--------------------------------------------------------
Change Log:-
v0.2j - Added 'decode' function to form helpers. To help
pass UTF-8 characters back to a form from the DB.
v0.2h - Removed 'spaces' from the regexp strings
v0.2g - Email address function is now in compliant
with RFC 2822
v0.2f - Added $masks for allErrors to be more
user-friendly, updated loop_clean to use
htmlspecialchars and supports UTF-8
v0.2e - Fixed allErrors() to include wrappers
v0.2d - Changed "required" to check for empty
strings properly
v0.2c - Added "not_equal" and "selected" functions
v0.2b - Tidied comments; fixed email mx check
v0.2a - Added loop_clean (multi-array sanitiser)
V0.2 - Added new helper functions (label, check)
- - -
v0.1 - Initial Release
*************************************************************/
class validateForm
{
var $input = array();
var $error = array();
var $error_wrapper;
var $pass;
// You can call the validated inputs directly from this class
// when you come to inserting them into the db, e.g.
// $form = new validateForm($_POST);
// $form->input['fullname']; etc...
function validateForm($input=array()) // Yes, post the the whole $_POST/$_GET array into the function
{
$this->input = $input; // Clone form inputs array into here
$this->pass = true; // Flag changes if theres an error
// Config
$this->error_wrapper['start'] = "<br /><span class=\"error\">";
$this->error_wrapper['end'] = "</span>";
// Sanitise our arrays
$this->loop_clean($this->input);
}
//
// We can even perform some security checks here if we wish
//
function loop_clean(&$data)
{
foreach ($data as $key => $value)
{
if ( !is_array($value) )
{
// Well formatted string; PHP4 requires "stripslashes" on all input fields
$data[$key] = trim(htmlspecialchars(strip_tags(stripslashes($value)), ENT_QUOTES, 'UTF-8'));
}
else
{
$this->loop_clean($value);
$data[$key] = $value;
}
}
}
/************************************/
/* Error Functions */
/************************************/
// Set error
function error($item, $desc)
{
$this->pass = false;
$this->error[$item] .= $desc." "; // Append multiple error messages
}
// Return the error
function showError($item)
{
return $this->error_wrapper['start'].trim($this->error[$item]).$this->error_wrapper['end'];
}
function allErrors($masks=array())
{
foreach ( $this->error as $key => $value )
{
// Mask field names with more appropriate User friendly names
$key = $masks[$key] != '' ? $masks[$key] : $key;
echo $this->error_wrapper['start']."<b>".ucfirst($key)."</b>: ".trim($value).$this->error_wrapper['end'];
}
}
/************************************/
/* Debugging */
/************************************/
function showInputs()
{
print_r($this->input);
}
/************************************/
/* Validation Functions */
/************************************/
function not_equal($string, $field)
{
if ( is_string($string) )
{
if ($string == $this->input[$field])
{
$msg = "You must select a different option other than \"$string\"";
$this->error($field, $msg);
return false;
}
return true;
}
return false;
}
function min_length($min=0, $field)
{
if( strlen($this->input[$field]) < (int) $min )
{
$msg = "This field cannot be shorter than $min characters.";
$this->error($field, $msg);
return false;
}
return true;
}
function max_length($max=0, $field)
{
if ( strlen($this->input[$field]) > (int) $max )
{
$msg = "This field cannot be longer than $max characters.";
$this->error($field, $msg);
return false;
}
return true;
}
function alpha($field)
{
if ( !preg_match("/^([a-z])+$/i", $this->input[$field]) )
{
$msg = "This field can only contain letters (A-Z). No foreign characters allowed.";
$this->error($field, $msg);
return false;
}
return true;
}
// Double-barrel names and marital status will require this
function alpha_dotdash($field)
{
if ( !preg_match("/^([a-z\-\.])+$/i", $this->input[$field]) )
{
$msg = "This field can only contain characters (A-Z-.). No foreign characters allowed.";
$this->error($field, $msg);
return false;
}
return true;
}
// Useful for Addresses or fields that may contain unusual but still valid chars
function alpha_special($field)
{
if ( !preg_match("/^([a-z0-9\-+\.,_='\"@#])+$/i", $this->input[$field]) )
{
$msg = "This field has illegal characters. You can use letters, numbers and (._-+='\"@#).";
$this->error($field, $msg);
return false;
}
return true;
}
function numeric($field)
{
if ( !preg_match("/^[\-+]?[0-9]*\.?[0-9]+$/", $this->input[$field]) )
{
$msg = "This field must contain only numbers.";
$this->error($field, $msg);
return false;
}
return true;
}
function alpha_numeric($field)
{
if( !preg_match("/^([a-z0-9])+$/i", $this->input[$field]) )
{
$msg = "This field can only contain letters and numbers.";
$this->error($field, $msg);
return false;
}
return true;
}
function required($field)
{
if ( !isset($this->input[$field]) OR $this->input[$field] == '' )
{
$this->error($field, 'This field is required.');
return false;
}
elseif ( is_array($this->input[$field]) )
{
$this->error($field, 'This is an array and won\'t be passed.');
return false;
}
return true;
}
/************************************/
/* Alias Functions */
/************************************/
function fullname($field, $req=true)
{
if ( $req == true AND !$this->required($field) )
return false;
return $this->alpha_dotdash($field);
}
function address($field, $req=true)
{
if ( $req == true AND !$this->required($field) )
return false;
return $this->alpha_special($field);
}
function telephone($field, $req=true)
{
if ( $req == true AND !$this->required($field) )
return false;
if ( $this->numeric($field) AND $this->min_length(11, $field) AND $this->max_length(14, $field) )
{
return true;
}
return false;
}
function mobile($field, $req=true)
{
if ( $req == true AND !$this->required($field) )
return false;
return $this->telephone($field);
}
function postcode($field, $req=true)
{
if ( $req == true AND !$this->required($field) )
return false;
if ( !preg_match("/^[a-zA-Z]{1,3}[0-9]{1,3} [0-9]{1}[a-zA-Z]{2}$/i", $this->input[$field]) )
{
$msg = "Postcode must follow the format of \"XX1 1XX\".";
$this->error($field, $msg);
return false;
}
return true;
}
function email($field, $req=true, $mx_records=false)
{
if ( $req == true AND !$this->required($field) )
return false;
// Function from: http://www.ilovejackdaniels.com/php/email-address-validation/
// Complies with the email address specification guidelines: RFC 2822
// First, we check that there's one @ symbol, and that the lengths are right
if (!ereg("^[^@]{1,64}@[^@]{1,255}$", $this->input[$field]))
{
// Email invalid because wrong number of characters in one section, or wrong number of @ symbols.
$msg = "Your email address is the wrong length.";
$this->error($field, $msg);
return false;
}
// Split it into sections to make life easier
$email_array = explode("@", $this->input[$field]);
$local_array = explode(".", $email_array[0]);
for ($i = 0; $i < sizeof($local_array); $i++)
{
if (!ereg("^(([A-Za-z0-9!#$%&amp;amp;amp;'*+/=?^_`{|}~-][A-Za-z0-9!#$%&amp;amp;amp;'*+/=?^_`{|}~\.-]{0,63})|(\"[^(\\|\")]{0,62}\"))$", $local_array[$i]))
{
$msg = "The first part of your email is malformed.";
$this->error($field, $msg);
return false;
}
}
if (!ereg("^\[?[0-9\.]+\]?$", $email_array[1])) // Check if domain is IP. If not, it should be valid domain name
{
$domain_array = explode(".", $email_array[1]);
if (sizeof($domain_array) < 2)
{
$msg = "Your email doesn't have a valid domain.";
$this->error($field, $msg);
return false; // Not enough parts to domain
}
for ($i = 0; $i < sizeof($domain_array); $i++)
{
if (!ereg("^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]+))$", $domain_array[$i]))
{
$msg = "Your email doesn't have a valid domain.";
$this->error($field, $msg);
return false;
}
}
}
// Check online to see if this is a real email host!
if ( $mx_records != false )
{
$host = $email_array[1]; //The whooole domain
getmxrr($host, $mxhosts);
if ( count($mxhosts) < 1 )
{
$msg = "There is no email host associated with your email. This probably means its fake.";
$this->error($field, $msg);
return false;
}
}
return true;
}
/************************************/
/* Helper Functions */
/************************************/
function decode($field)
{
return html_entity_decode($this->input[$field]);
}
function label($text, $id)
{
return "<label for=\"$id\">$text</label>";
}
function check($field, $value, $default=false)
{
if ( $default == true AND empty($this->input[$field]) )
return 'checked="checked"';
return $this->input[$field] == $value ? 'checked="checked"' : '';
}
function selected($field, $value, $default=false)
{
if ( $default == true AND empty($this->input[$field]) )
return 'selected="selected"';
return $this->input[$field] == $value ? 'selected="selected"' : '';
}
}
/*
Example:
You can just edit $this->error_wrapper['start'] AND $this->error_wrapper['end'] to make the errors display how you want.
*/
if ( $_SERVER['REQUEST_METHOD'] == 'POST' )
{
$form = new validateForm($_POST);
$form->required('firstname');
$form->required('lastname');
$form->mobile('mobile');
$form->email('email', true); // Validate email AND make it required
$form->required('t-and-c'); // Terms &amp;amp;amp; Conditions
if ( $form->pass == true )
{
// Insert data into DB ...
echo "Success";
}
}
else
{
$form = new validateForm();
}
?>
<div class="error">
<?php
// Masks are used if you have an unfriendly named field that may cause the user confusion.
// You just specify the names and the fields in the array with the text you want to replace it with.
$masks = array(
't-and-c' => 'Terms &amp;amp;amp; Conditions');
$form->allErrors($masks);
?>
</div>
<form method="post">
<p>*First Name: <input type="text" name="firstname" value="<?php echo $form->input['firstname'];?>" maxlength="80" />
<?php
// Show inidividual errors
echo $form->showError('firstname');
?>
</p>
<p>*Last Name: <input type="text" name="lastname" value="<?php echo $form->input['lastname'];?>" maxlength="80" /></p>
<?php
// OR Specify your own error message
if ( $form->showError('lastname') )
{
echo 'You must fill in the "Last Name" field.';
}
?>
<p>Mobile: <input type="text" name="mobile" value="<?php echo $form->input['mobile'];?>" /></p>
<p>*Email: <input type="text" name="email" value="<?php echo $form->input['email'];?>" /></p>
<p>Comment: <textarea name="comment">
< ?php
// If you ever send data that has UTF-8 in it, you can use the decode helper if retrieving it from a DB
echo $form->decode('comment');
?></textarea></p>
<p><input type="checkbox" name="t-and-c" id="t-and-c" value="true" <?php echo $form->check('t-and-c', true);?>/> <?php echo $form->label('Terms and Conditions*' ,'t-and-c');?></p>
<p><strong>* Required</strong></p>
<p><input type="submit" value="Send" /></p>
</form>
A really good idea, I have started using it and like it. So far so good!
I have been looking at this script. My only problem is, that it looks like you should have used it on your site against these spammers.. :(
My god! Finally sorted through all that spam. I’ve turned Akimet on now to save me the hassle on future.
This class doesn’t deal with spam but is useful for validating input (as it says).
I have actually updated this class a few times now, I shall update this version on my website soon.
Hey, do you still read and respond to your post’s here?
This is excellent by the way but i have a couple of questions about it.
Cheers
Yup, I still respond :) What do you want to ask?
Hey,
thanks for getting back to me. I wasn’t holding out for a reply to be honest with your last posting being 2 years ago.
I’m having a bit trouble modifying your script to meet my needs and i’m fed up of bashing my head against the wall.
I’m trying to do the following things to your script:
1) my biggest problem. at line 56 you have some code to cleanse the input data. I have added an apostrophe to the preg match characters to allow for foreign surnames (al’habi for example). Due to the htmlspecialcharacters cleansing the data inputted the source code becomes ‘&#;’ to represent the apostrophe. Obviously i’m not allowing for those characters so the preg match fails. What i’d like to do is to cleanse the data only after it has conducted the preg_match but i’m struggling with that. I need the preg_match to see the apostrophe, match that to the allowed characters and THEN cleanse the data. Hope that makes sense.
2) I want to have a password field and would require the user to enter the data twice in order to confirm the data matches in both input boxes. (I think i can do this but haven’t tried yet as i’ve been trying to sort problem 1.
3) line 430 specifying your own warning doesn’t seem to work for me. It displays my own warning regardless of whether the inputted data meets the requirements or not.
So if you could help with any of the above then i’d really appreciate it, if not then thanks for an excellent script! Was just a shot in the dark that you might still reply here and help out.
Also, do you accept donations? I’ve used this script quite a bit and wouldn’t mind paying a small donation as a thanks!
Cheers
sorted number 2! Gave up on number 1… every time i move something i break it!
Did you get any further with improvements? I’m considering giving this script a bit of a tidy.
how can i used helper function in this scripts