Samuel Sjöberg's weblog

Skip to navigation

Form validation on client and server

This summer I once again faced the awkward task of validating user input. This tedious task has a tendency to be time-consuming and often you try to reuse code but end up customizing it for hours anyway.

While the backbone of validation should rely on server-side technologies, the client can't be ignored because of its support for instant feedback. Javascript can provide hints and feedback in an unobtrusive way which eases the interaction. Since I'm into interaction I think this is an important aspect.

This means I usually have to write two scripts, one secure validation service for the server and another nifty, user-friendly client-side script to improve the user-experience. But it doesn't end here. Two scripts mean two places for error messages and two places for validation rules. Not to mention the problem with inconsistent ways of addressing the users when their input aren't valid.

Many great ideas come from laziness I thought and started to think about the problem...

The idea

What if you could apply validation rules to the input fields on the server and client at the same time? Since I didn't wanted to dive into the DOM for some custom tag values I got to the conclusion that the name attribute was my best option. The name attribute is accessible on the client side and is submitted along with the data to the server.

My first draft on how to assign rules was based on underscores as seperators but after suggestions from Anders Fjeldstad I converted to using a colon to separate the name from the rules and a pipe to separate multiple rules from each other. This way the resemblance to bitwise-or's became obvious and the readability increased.

<input type="text" name="myfield:required" />

Multiple rules are as mentioned combined with pipes:

<input type="text" name="myfield:optional|numeric" />

The drawback with this technique is the complex names of the input fields. The solution to this is to strip everything in the name to the right of the colon before we access the data on the server side. In PHP the input field myfield:optional|numeric would be accessed with e.g. $_POST['myfield'].

Now we have a way of validating the same rules twice. Since no custom attributes are added to the markup it still validates according to XHTML 1.0.

If this should be of any use the error messages also need to travel along with the rules. The solution I've come up with simply fetch the error message from a hidden input field.

<input type="hidden" name="myfield:error"
value="This is the error message for myfield." />

Each field that is validated requires a hidden error message field.

An implementation

This implementation is written in javascript and PHP. My objective has been to make the javascript transparent, i.e. no error message should be thrown. The script has been tested and is known to work in the following browsers:

In Opera 7 there is a problem with applying CSS rules dynamically on labels.

To set up and run the script on the client, simply include vform1.0.js in your header.

<script type="text/javascript" src="vform1.0.js"></script>

If you use multiple onload commands, I suggest you use some kind of event adder that preserve previous function calls, e.g. Simon Willison's addLoadEvent.

To run the script in PHP include vform.class.php and then validate. The vform object is initiated in the included file.

include('vform.class.php');

if (!$vform->isValid()) {
   $errorMessage = $vform->getErrorList(true);
} else {
   // Input is valid and the rules have
   // been stripped from the fieldname.
}

Default validation rules

The following validation rules are predefined and can be used in combination in your markup.

required
The user is required to write something. White space characters are excluded.
optional
Combined with other rules, this allows fields to be either empty or validated according to some rules.
alpha
Only allow letters and no digits.
numeric
Only allow digits and no white space characters.
email
The format of the text must be an e-mail address.
url
The format of the text must be an URL. If the address doesn't start with http:// it is added.
xml
With this option set < and > are not converted to entities. This means XML is accepted as input and that the tags are preserved.
equal(field)
Check for equality between the specified field. Field is the name of the field with all the rules stripped.
min(n)
Only applies to array objects, e.g. Radio or Checkbox, and specifies the least number of alternatives that have to be checked.
max(n)
Only applies to array object, e.g. Radio or Checkbox, and specifies the greatest number of alternatives that have to be checked.
callback(function)
To specify your own validation function, in javascript, PHP or both, use the callback command. See below for examples on how to write a callback function.
error
This specifies an error message for the given fieldname. This command can not be used in combination with other rules.

Feedback

Feedback is provided by the following CSS classes. The name of these classes can be changed in the javascript file. Notice that these class names are appended to the current classes of the element to allow for multiple CSS rules.

focusClassName
Applied when an element is in focus.
validClassName
Applied when the element is validated.
invalidClassName
Applied when the element is invalid. The error message is also added as an title to the element.

Error messages

There is two ways of displaying errors. If an element exists with id="errorMessage" (this value can be changed in the javascript file) this field will be used to display the errors by the javascript. If no such element exists the errors will be displayed in an alert window. Notice that the errors don't echo automagically to this errorMessage-element in PHP. Look at the example how to manage this relatively easy.

Callback functions

To make the validation flexible the callback-rule allows for custom validation. In javascript a callback function takes the field as input and returns the value of the field on success or false on failure. An example of a callback function is the validation of a Swedish phone number:

function phone(elm) {
   var re = new RegExp('(d{2,4})(?:s|-){1,3}(d{5,10})');
   var match = re.exec(elm.value);
   if (match != null)
      return match[1] + '- ' + match[2];
   return false;
}

In PHP the callback function takes the value of the field as input and returns true or false depending on the outcome of the validation. The value is passed by reference thus allowing for alteration without returning the result. The callback function for Swedish phone-number in PHP looks like this:

function phone($value) {
   return preg_match('/(d){2,4}- (d){5,10}/', $value);
}

vform PHP interface

The following functions can be considered public in the vform class.

bool isValid( void )
Return true if all fields (POST or GET) which are given rules are valid. This also strips all rules from the fieldnames making them easy to access without knowing the rules.
string getErrorList( [bool useLabels
[, string prefix [, string suffix]]] )
Return an unordered HTML list with the error messages for all invalid fields. useLabels indicate whether or not labels should be used, default value is false. This is only useful if the field-id's are defined such that they can be generated from the names of the fields possibly preceded by some prefix or succeeded by some suffix.
array getErrors( void )
Return an array containing the possible error messages.

Source

This script is part of the commentary validation on this blog, but I've also set up an demonstration page. The source of the demonstration is also available.

Reader comments

  1. Hya !
    This how-to is very good, can you send me the sources on my email.I donno how to use it :(

    16th January 2005, 14:34 CET. 
  2. I really like the idea of this class. Clients can manipulate the name part of a POST, which means they would be able to stop the server side validation.

    One idea would be to use the php dom model functions to parse the original form and extract the validation requirements from that.

    I tried giving it a go, but the DOM model functions I needed seam to be only avaliable in php 5.

    If you have any further developments on this I would be really intersted.

    19th February 2005, 08:04 CET. 
  3. Hi There

    Looks like a very nice attempt at generic validation. Especially the DOM model looks nice, there seems to be a problem though (tiny one).
    It happens whenever a user enters a correct field (label and field turn green), and then changes it back to an incorrect field (the field turns red, the label does not!). Any idea how this could be fixed?

    Moreover, I would advise that instead of using the short form as PHP tag, use the full version - this because the short form relies on short_open_tag directive in php.ini being enabled. (which is not always the case).

    Anyway, I hope you could give me a fix on the problem I mentioned, thanks!

    17th March 2005, 12:42 CET. 
  4. I have updated the javascript file now so that labels turn red again. The problem was caused by the function resetElement in which the label wasn't properly reset.

    The problem with users passing POSTs from other sites where the rules aren't applied is a little messier problem. Since the whole idea with this kind of is to avoid PHP DOM I have tried to think of alternative solutions. I haven't come up with anything yet, but my thoughts are set on some kind of hashing of the rules which could be applied after development is completed.

    Oh, and the short tags. I agree, don't use them if you're developing for a unknown host. There is no short tags used in vForm, except for in the demo. The reason for this? Well, I'm lazy, aren't we all?

    17th March 2005, 13:28 CET. 
  5. Nice fix.

    About the problem with your form rules being bypassed by form spoofing, hashing is a solution yes. I am actually developing something like that for PHP right now. I create a MD5 fingerprint of the rules, then when it is posted I check the fingerprint values to make sure that the data submitted is valid.
    I'll see if I can get something online when it's done.

    17th March 2005, 14:58 CET. 
  6. I was looking for something exactly like what you have designed. I tried using it after disabling javascript. But your verifyrules function always returns "corrupted or bypassed message". I even tested them on my test site and same result! Can you please point me to the mistake?

    16th December 2005, 18:53 CET. 
  7. Thanks for the innovative idea. Though, it looks very cute idea, it is very complex to understand and implenent some dynamic fields. Instead, this idea has increased my strenth about thinking for only one validation class for server and client. Let us go generic. I respect this. I will try to release my validation based on this idea, if possible, in my website.

    23rd April 2008, 07:28 CET. 

Pages linking to this entry

Pingback is enabled on all archived entries. Read more about pingback in the Pingback 1.0 Specification.

About this post

Created 13th November 2004 18:44 CET. Filed under Javascript and DOM, PHP.

7 Comments
1 Pingback