Easier form validation with PHP
Let’s talk about form validation. Here’s what I would class as the ideal validation system for a form in a web application:
- The form is displayed; you fill it in.
- You submit the form to the server.
- If you missed something out or provided invalid input, the form is redisplayed pre-filled with the valid data you already entered.
- The redisplayed form tells you what you got wrong. It also flags the fields that were incorrect.
- Loop until you fill the form in correctly.
Writing this once in PHP is trivial, but takes quite a bit of very dull code. Writing this for more than one form quickly becomes a tedious nightmare of duplicating and slightly editing code, which is why so few forms bother.
I’ve been trying to figure out an elegant way of automating as much of this code as possible on and off for more than two years. I’ve tried systems that generate the whole form based on a bunch of criteria (too inflexible), systems that describe the validation rules (fine but they don’t help with the redisplay logic) and I’ve looked at solutions available in other language (such as ASP.NET), all to no avail. Over the past couple of days I’ve been working on the problem again, and for the first time I think I’m actually getting somewhere.
My latest attempt (sparked by this article on Evolt) involves embedding validation and redisplay rules in the markup of the form itself. The form is written in XHTML, but with a number of additional tags and elements. Any form field elements can have a number of additional attributes which specify the validation rules of the form. For example:
<input type="text" name="name"
compulsory="yes" validate="alpha" callback="uniqueName"
size="20" />
The yellow highlighting in the above line marks the additional elements. In this case, they mean that the field is compulsory, must contain only letters, and should be checked for final validity by calling the user defined uniqueName() function.
There are a few other validation attributes, but the above gives a good idea of how the system works. The second problem is how to display errors. Part of the solution here is my custom <error> element, which can be used to associate an error message with an error in a particular field. In my tests I’ve been using this to display an exclamation mark next to invalid fields:
<input type="text" name="name"
compulsory="yes" validate="alpha" callback="uniqueName"
size="20" />
<error for="name"> !</error>
This associates the text inside the <error> element (in this case the single exclamation mark) with the “name” field.
So far, all I’ve done is add a bunch of invalid markup to an otherwise valid chunk of XHTML. The key is how this markup is processed. FormML (for want of a better name) is never passed to the browser; instead it is processed by my PHP FormProcessor class before being displayed. This class strips out all of the FormML tags, and also applies logic to the rest of the form based on the information from the tags. Because the whole thing is XHTML, this can be relatively easily achieved using PHP’s built in XML parser. The XML is modified on the fly to create the XHTML that is sent to the browser. In the above example, the contents of the <error> element would only be displayed if the form was being redisplayed and the user had not filled in their name correctly. In addition, the class populates the value attribute of each input element with the previously entered data and adds an “invalid” class to the element to allow it to be styled appropriately.
The next problem is to display a list of descriptive error messages describing the problem. This took slightly longer to work out: I needed a way of setting an error message for each potential problem (remember there are several ways a field can be invalid: it could have been left unfilled, or it could have failed a regular expression check, or it could have failed a callback function), and I also needed some way of indicating how these errors should be displayed. My eventual solution was to introduce a new <errormsg> element for specifying error messages, and a simple templating system (based on a few more custom tags) for indicating where these errors should be displayed. I’m not entirely happy with the way this works at the moment, but here’s what I’m using:
<errorlist>
<ul>
<erroritem> <li><message /></li></erroritem>
</ul>
</errorlist>
<errormsg field="name" test="compulsory">You did not enter your name</errormsg>
<errormsg field="name" test="alpha">Your name must consist <em>only</em> of letters</errormsg>
The <errormsg> elements can be placed anywhere in the markup, and will be removed before display. The <errorlist> fragment defines the template for the list of error messages, with the <erroritem> element enclosing the “template” for each error and the <message> element showing where the actual error message (as defined elsewhere) should be displayed. Note that the <errormsg> elements specify the field and the test that they relate to. It is not necessary to provide custom error messages for every possible field/test combination; the system generates moderately intelligent error messages in the event that one has not been provided.
That’s a lot of custom markup to define the behaviour of a form. The good news is that the actual PHP used to display, validate and redisplay the form is incredibly simple. Here it is:
$formML = '... formML XML code goes here ...';
$processor = new FormProcessor($formML);
if ($processor->validate()) {
// The form has been submitted and is valid - process the data
echo 'Form data is OK!';
} else {
// This will display the form for the first time OR redisplay it
// with relevant error messages
$processor->display();
}
And that’s all there is to it.
The code is still under very heavy development. At the moment it’s messy, has several minor bugs (and possibly some major ones I haven’t yet uncovered), isn’t fully tested and is almost certainly not ready for deployment. Never-the-less, you can play with a demo form that uses it or grab the code here:
- FormProcesser.class.php—the class(es)
- test.php—the demo form, as seen here.
Incidentally, I haven’t mentioned javascript in the above in an attempt to keep things simple. I know client side validation is a great addition to stuff like this (provided it’s backed up by solid server side logic) and one of my longer term aims is to dynamically add the necessary javascript to the form during the processing phase, thus skipping the need to write any boring validation code by hand.
Smiler - 18th June 2003 01:16 - #
I've been avoiding IRC completely for about a year because I kept on losing whole evening just chatting. I'm thinking about starting the habit again soon under controlled circumstances (limiting myself to a few channels) though.
I actually wrote something like this that handled automatic generation of the XHTML ages ago, but in practise it wasn't flexible enough to be useful. I ended up dropping it completely because it wouldn't let me tweak the form layotu easily enough.
Simon Willison - 18th June 2003 01:18 - #
Scott Johnson - 18th June 2003 01:44 - #
Anne van Kesteren - 18th June 2003 06:59 - #
Harry Fuecks - 18th June 2003 10:22 - #
GaryF - 18th June 2003 15:25 - #
Simon Willison - 18th June 2003 16:02 - #
Manuzhai - 18th June 2003 18:06 - #
This post has come in the middle of my thinking about what is the best templating methodology.. I've been looking at xml style, attribute addition (as yours is) new markup (smarty style) etc.
I've written a template pre-parser that you might be interested in (takes html or xhtml and applies various transformations and extracts sections etc seems to cover nearly all possibilities I could think of.
Anyways, I have no real conclusions. I want a templating language that is easy to learn and yet provides a smooth learning curve to power user. I like tal style templates. I also like albatross. I think a template language should offer the sorts of functionality you demonstrate here. I also am incredibly frustrated with smarty. Flexy seems good but ruins my HTML.
My path to creating a new template language has reached this postulating ideals point (it seems a classically, and admirably, lengthy point in python developent in particular). I want to get this right so am soliciting as much feedback as possible. any thoughts?
Tim Parkin - 18th June 2003 21:19 - #
Bob - 19th June 2003 04:01 - #
PeterV - 19th June 2003 14:39 - #
Simon Willison - 19th June 2003 16:26 - #
Jeremy C. Wright - 19th June 2003 23:11 - #
I did write a very long post on this subject and about logic/presentation, but as addcomment.php just gave me no reply I've lost the whole b****y thing. So I will just say: have a look at PHPTal (use google - I'm sick of typing it) and integration between the two would be good.
Peter - 20th June 2003 09:51 - #
mitchell perilstein - 23rd June 2003 18:58 - #
Ben Nolan - 24th June 2003 03:15 - #
dibby - 24th June 2003 15:44 - #
Simon Willison - 24th June 2003 17:59 - #
Ted Stresen-Reuter - 27th June 2003 16:21 - #
Richard Lynch - 27th June 2003 19:40 - #
Jean-Marc König - 2nd July 2003 17:14 - #
Dark_Greg - 3rd July 2003 07:41 - #
Simon Willison - 3rd July 2003 08:20 - #
Dark_Greg - 3rd July 2003 08:59 - #
Simon Willison - 3rd July 2003 09:05 - #
<form action="http://www.notmyserver.com/signup.php" method="post"> <input type="text" name="name" id="name" compulsory="NO" validate="alpha" callback="uniqueName" size="20" /> <input type="text" name="email" id="email" compulsory="YES" validate="ALPHA" size="20" /> <input type="submit"> </form>If such a form where accepted, you'd accept input where the name field is not mandatory, and the email field accepts any alpha-num input.lhb - 5th July 2003 10:16 - #
Simon Willison - 5th July 2003 11:50 - #
lhb - 5th July 2003 12:14 - #
Dark_Greg - 9th July 2003 03:52 - #
Simon Willison - 9th July 2003 10:14 - #
ok heres an idea...can functions from classes be called like this?:
callback="user::uniqueName"
yeah? that should work shouldnt it? (again untested, i'll be giving this class a SOLID workout over the weekend...and can post detailed experiances if you like)Dark_Greg - 9th July 2003 12:10 - #
zaz - 31st July 2003 10:22 - #
Peter Bowyer - 4th August 2003 20:48 - #
I really like this! I've played around with this script a bit and used it to create this form: http://www.infoalli.com/requestbid.php. I made a few modifications of my own in order to handle radio buttons and check boxes. I also modified the constructor to be able to accept a file name or a string containing FML. This saves a bit of typing, so you don't have to write the code to read the file into a string for every form you write.
I'm also evaluating the possibility of using it in some software I'm developing. I'll keep you updated on any other modifications I make and how I use it.
Thanks!Wayne Jensen - 7th August 2003 07:21 - #
ikke - 9th August 2003 15:33 - #
Guy - 28th August 2003 08:02 - #
Forbes - 16th September 2003 02:10 - #
Alex - 8th October 2003 23:59 - #
Simon Willison - 9th October 2003 00:26 - #
paulh - 10th October 2003 13:46 - #
daniel - 12th October 2003 02:26 - #
paulh - 12th October 2003 03:04 - #
paulh - 12th October 2003 03:06 - #
Chris - 2nd November 2003 22:39 - #
I know this topic is long out of date (only found this site today), but I just wanted to note that (X)HTML forms already have business logic in them. Forms aren't the usual clear separation of content and style — they also include attributes like
maxlength. It's my experience that forms will always have blurred lines between business and template logic, unless you let go of the reigns.Justin French - 8th December 2003 03:13 - #
h - 7th January 2004 21:26 - #
444545545 - 8th January 2004 13:56 - #
wiredX - 30th January 2004 12:07 - #
Ran across this post a few days ago while searching for something related. Specifically, I was looking for a way to get custom tags in PHP, similar to what you have when coding JSP. Custom tags allow you to define new tags that when encountered in mark up are transformed into something else. You do something similar here with your FormML, but with FormML the tags are "hard wired". With custom tags the developer can create new tags by writing a "tag handler", and thus can extend the available tags easily.
While searching for a custom tag solution I ran across the Pear XML_Transformer class, which would do precisely what I want, but like most things in Pear, it's basically not documented and appears to not have a stable enough code base for me to feel safe using the source as documentation. Not to mention it's a little to over designed (i.e. there's a lot of functionality you'll rarely use that you're burdened with even when you don't want it). I also found this, which gave a nice starting point for what I want.
So, why is this a relevant comment? I've got code mostly implemented that allows you handle forms similar to what you're doing, though I place the validation in the PHP file, not the "template". Custom tags in the template take care of populating the form, so <html:input name="FieldName" type="text" /> automatically populates with the content of $_POST["FieldName"]. Error messages are displayed in a fashion similar to what you've done, but more inline with how things are done in Apache Struts. The end result is similar to yours, but with a cleaner separation of presentation and business logic and a more extendable template markup language. The end result I hope will be a PHP library that supplies most of the functionality available in Struts (including Tiles). If you're interested I'll keep you informed of my progress.
wekempf - 9th February 2004 17:52 - #
egege - 10th February 2004 15:20 - #
carolina - 10th February 2004 22:36 - #
Laura Lara - 17th February 2004 00:50 - #
Azizus Salehin - 29th February 2004 06:03 - #
Sergio B. - 23rd March 2004 00:25 - #
bcv - 4th April 2004 16:15 - #
6666 - 12th April 2004 13:56 - #
fuj - 14th April 2004 13:28 - #
Sometimes you use $my_array[myname] instead of $my_array['myname'].The second question is as follows: The regular expressions you specified for matching alpha and for numeric strings seem to be the same. With my beginning knowledge of regular expressions, it seems that the expression for numeric would be something different. The PHP function is_numeric could also be used ...Jay Sheth - 20th April 2004 17:45 - #
df - 6th May 2004 03:53 - #
Intosia - 14th May 2004 08:08 - #
tywr - 30th May 2004 06:52 - #
werewr - 21st June 2004 23:33 - #
fdshdfgh - 2nd July 2004 17:46 - #
zfdzs - 15th July 2004 09:23 - #
aSAsasS - 27th July 2004 08:13 - #
red - 30th July 2004 00:51 - #
tony - 1st October 2004 01:45 - #
helo - 7th October 2004 04:56 - #
rtrh - 10th October 2004 23:59 - #
asdf - 18th November 2004 10:02 - #
Dear Sir,
Thanks so much for this nice article. I was wondering if some one has allready done work on Form Validation and automation in PHP and i found exactly what i was searching. Your article is very informative. May be some one can take up from here and make it more useful and release it as a GPL Code for automation of HTML Forms.
Thanks for sharing.
Chantu
http://www.chantu.com
PS: humm ... i wonder why you dont allow capital letters in Tags :DChantu - 19th November 2004 20:59 - #
Jamie - 10th December 2004 15:35 - #
matt - 15th December 2004 17:57 - #
fasdf - 29th December 2004 14:40 - #
tewe - 9th January 2005 05:49 - #
vx - 14th January 2005 20:44 - #
asdfasdf - 20th January 2005 16:34 - #
'Users'=>array( 'Editable' => '1', 'AllowNew' => '1', 'AllowEdit' => '1', 'AllowDelete' => '1', 'EditTable' => array('RoleType'=>'Admin','Location'=>'Local'), //must be admin over /admin folder 'ListTable' => array('RoleType'=>'Admin','Location'=>'Local'), //alternative is 'Anywhere' 'Type'=>'Users', 'Related' => array(array( 'Caption' =>'Roles', 'RelatedTable' =>'roles', 'RelatedField' =>'roleid', 'RelatedDisplayField' =>'roledesc', 'LinkerTable' =>'rolemembers', 'LinkerRelatedFields' =>array('roleid'), 'LinkerThisFields' =>array('username') ) ), 'Fields'=>array( 'username' =>array('Type'=>'Text', 'Size'=>'40', 'Create'=>'VARCHAR(40)', 'ListPos'=>'1', 'SearchBy'=>'=', 'Validation'=>'NotBlank','PriKey'=>'1', 'Caption'=>'User name', 'ToUpperCase'=>'1'), 'firstname' =>array('Type'=>'Text', 'Size'=>'30', 'Create'=>'VARCHAR(30)', 'ListPos'=>'2', 'SearchBy'=>'=', 'Validation'=>'NotBlank', 'Caption'=>'First name'), 'lastname' =>array('Type'=>'Text', 'Size'=>'60', 'Create'=>'VARCHAR(60)', 'ListPos'=>'3', 'SearchBy'=>'=', 'Validation'=>'NotBlank', 'Caption'=>'Surname'), 'active' =>array('Type'=>'Boolean', 'WidgetType'=>'Checkbox', 'Create'=>'BOOLEAN', 'ListPos'=>'4', 'Caption'=>'Is the account active?'), 'accountcreated' =>array('Type'=>'DateTime_Created', 'DisplayAs'=>'readonly', 'Size'=>'120', 'Create'=>'TIMESTAMP', 'Caption'=>'Time account created'), 'accountcreatedby' =>array('Type'=>'Text_CreatedBy', 'DisplayAs'=>'readonly', 'Size'=>'40', 'Create'=>'VARCHAR(40)', 'Caption'=>'Account created by'), 'accountmodified' =>array('Type'=>'DateTime_Updated', 'DisplayAs'=>'readonly', 'Size'=>'120', 'Create'=>'TIMESTAMP', 'Caption'=>'Time account last modified'), 'accountmodifiedby' =>array('Type'=>'Text_UpdatedBy', 'DisplayAs'=>'readonly', 'Size'=>'40', 'Create'=>'VARCHAR(40)', 'Caption'=>'Account modified by'), 'email' =>array('Type'=>'Text', 'Size'=>'80','Caption'=>'Email Address', 'Create'=>'VARCHAR(80)', 'ListPos'=>'5', 'SearchBy'=>'LIKE'), 'password' =>array('Type'=>'Text', 'WidgetType'=>'Password','Size'=>'15','Caption'=>' Password', 'Create'=>'VARCHAR(15)'), //'usertype' =>array('Type'=>'Integer', 'WidgetType'=>'Select', 'Options'=>$aUserTypesText, 'Create'=>'INT2', 'ListPos'=>'9', 'SearchBy'=>'=', "Caption"=>"Usertype"), ) ),Although this is not comprehensive of what is currently being done, it gives you a good idea. We'll probably announce it on freshmeat when we're ready - we'll have to name it as well. It will also be announced at zedcore.com It certainly does make form creation easy, as well as database integration, validation, table creation, maybe one day table editing can be done after a change in the definition.... The array also includes items to guide the database management tool in our CMS.richard - 22nd February 2005 19:52 - #
fd - 3rd March 2005 02:58 - #
bvn - 3rd March 2005 15:30 - #
you state "The form is written in XHTML, but with a number of additional tags and elements"
correct me if i'm wrong, but strict XHTML validation will fail this markup because of those extraneous attributes/tags.
jeff - 7th March 2005 19:56 - #
jeff - 7th March 2005 19:57 - #
Your email validation is too restrictive – the following could be a perfectly valid email address (as per RFC 2822):
" spaces! @s! \"escaped quotes!\" "@łódź.plShot - 7th March 2005 20:33 - #
sdfsd - 14th April 2005 22:32 - #
Justin - 21st April 2005 01:38 - #
wer - 26th April 2005 22:14 - #
gfdsgfd - 17th May 2005 02:17 - #
ngdn gdgs - 18th May 2005 19:02 - #
Glen - 20th May 2005 11:52 - #
fdsa - 4th June 2005 10:17 - #
hgfh - 7th June 2005 06:05 - #
Gestaltware - 7th June 2005 22:10 - #
3232 - 25th June 2005 05:40 - #
hhh - 25th June 2005 07:34 - #
fffffffff - 28th June 2005 05:38 - #
fdfd - 7th July 2005 04:23 - #
asdasd - 30th August 2005 06:00 - #
rtr - 6th September 2005 10:03 - #
gilemon - 13th September 2005 13:36 - #
gilemon - 13th September 2005 13:38 - #
geza - 20th September 2005 23:39 - #
s - 25th September 2005 16:39 - #
asdf - 27th September 2005 04:05 - #
gfgjcfgj - 4th October 2005 23:19 - #
Charles Peek - 12th October 2005 22:27 - #
f - 15th October 2005 05:54 - #
fghgfh - 28th October 2005 09:12 - #
Wolverine - 16th December 2005 12:44 - #
sdf - 26th December 2005 20:20 - #
Joshua - 28th December 2005 23:40 - #
ffdfdf - 15th January 2006 14:29 - #
cvb - 26th January 2006 13:56 - #
cvb - 26th January 2006 13:56 - #
Vasil - 1st February 2006 05:50 - #
zxc - 7th February 2006 05:18 - #
praveen - 7th February 2006 09:57 - #
mlancvaster - 14th February 2006 14:07 - #
jghgjkhgu8i - 16th February 2006 08:46 - #
sdaf - 28th February 2006 09:57 - #
I added some code to deal with radiobuttons and checkboxes. In the tag_open function of the class FormProcessor I changed the code that deals with the value of the input field in the $_POST array. I tried to post the code aswell but I cannot form the XHTML well
Hans van Domselaar - 28th February 2006 13:11 - #
68 - 1st March 2006 06:00 - #
hgj - 1st March 2006 06:01 - #
hgj - 1st March 2006 06:01 - #
67 - 1st March 2006 06:01 - #
fg - 3rd March 2006 08:50 - #
wfwe - 20th March 2006 13:12 - #
qwe - 30th March 2006 10:15 - #
mg - 1st April 2006 21:04 - #
vbmvb - 4th April 2006 12:29 - #
this is outstanding work.
i am just starting php and can only hope that one day i will be able to understand how the hell this works...
until that day.........................!
paul - 7th April 2006 21:04 - #
streaky - 13th April 2006 02:06 - #
324234 - 22nd April 2006 06:20 - #
Fikus - 28th April 2006 23:59 - #
john - 1st May 2006 21:36 - #
mangal - 10th May 2006 16:07 - #
samon - 12th May 2006 07:42 - #
vikas - 17th May 2006 07:31 - #
iuiu - 18th May 2006 08:39 - #
tre - 21st July 2006 11:55 - #
ss - 21st August 2006 04:12 - #
Max Developer - 3rd September 2006 21:37 - #
d - 19th September 2006 15:58 - #
Nils - 20th September 2006 23:51 - #
Jeff - 22nd September 2006 20:30 - #
fhgfhg - 3rd October 2006 20:16 - #
asdvasv - 3rd October 2006 20:19 - #
jeff - 6th October 2006 04:08 - #
praveen sharma - 11th October 2006 08:56 - #
adsad - 26th October 2006 17:21 - #
8 - 26th October 2006 18:12 - #
Nelson - 26th October 2006 22:20 - #