picture of Yuri Makasjuk
One of the most common tasks that I came across developing interactive websites was processing users' input. In this article I would like to share the experience I had developing an object-oriented solution for it. The best effect that I can imagine this paper to have is to not just show “another solution for checking a submitted form”, but rather to let you taste the power of object-oriented technology. Together with you, I will look back at the way I thought that brought me to a solution that was implemented and successfully used. Reading this article requires some knowledge of object-oriented programming fundamentals. As a result, we will have a working example developed.

Introduction

First of all, we already know how to examine $HTTP_POST_VARS, we know how to name form inputs in such a way that they will be presented as a nice PHP array. We've read Richard Creech's article on making a form's controls dynamic. Why do we need anything else at this stage?
Let me explain. You see, I have a Delphi background and with Delphi you get very much addicted to re-using instead of re-inventing. So, if you find yourself coding something that gives you this dejavu - “I've been doing this before” - you want to invent a class that can be reused throughout your projects.

Analysis

Let's take a look at the task of processing user forms and try to find the most common steps. They will describe generic functionality of a form. First of all, a form must be presented to user. This includes rendering graphical representation of form objects (which is carried out by browsers quite efficiently). Then we must display the initial values; this should be governed by application logic. Then the user's input must be received and analyzed. We receive a set of values from user. At the abstract level the decision that is made on this stage would usually be: can this form be considered completed or should we go back and ask user to correct something.
Let's first look at the situation when we need something corrected. In this case an application must display the form again and give some explanations about what went wrong (display some error messages). Besides, a well-mannered form should preserve values entered by user, at least the correct ones. Now suppose, we decide that the values are good enough to allow us to continue. Then these values (or some of them) usually get stored somehow (in a database, in session variables). Finally, another decision is made: where do we go from here? It is often based on values entered and, of course, follows some paths defined by programmer.
Now, what should we try to achieve by the object analysis? The best would be to program generic behavior in parent class(es) once and forever. Then from that moment on we will be able to only concentrate on context-specific steps for each new form that we develop: check values, store values, decide where to go from the form.
The key element will be a generic form class that I called FormProcessor. Let's first have a general idea about how it works. I like putting forms into separate files. There's nothing that makes it necessary, it just makes things easier, for example, to edit them in visual editors. Also, I usually have forms that 'submit to themselves', in other words I usually group a form with code that takes its input. One idea is to create instances of FormProcessor's sub-classes in the beginning of such files. Then, this instance will 'tune in' the environment and will be able to have complete control over displaying the form and working with it further on. By 'tuning in', I mean deciding whether this particular form was displayed and submitted before and we should now check the input. If it has never been submitted before, it should be displayed for the first time. Object-oriented technology will work for us in both situations. Generic framework within FormProcessor will run one of the virtual member functions -- either for checking or for displaying the form. Notice: all we will have to do in each particular form's class is override these virtual functions. In the same way, we will override the function responsible for storing form's values. Again, generic class will decide when to call it.

Executing environment

Before we go deeper into details, there's one important thing to talk about. That is, how do we organize forms into a branching sequence (I hope, nobody minds me using word 'wizard' here)? Here's what I suggest. You may think of it as of some kind of a 'runner' that manipulates instances of classes derived from FormProcessor. This environment knows which form (script in a separate file) should be loaded first. It then includes the script containing a form (FormProcessor's descendant). This form decides whether it is still displaying itself or just finishing processing of data submitted to it. After storing correct data it reports to the runner: “hey, I'm finished now -- look, this is who should be next”. Based on this information, the runner can load the next form, according to the decision made in the current one. The next form, when included, will sense that it is just loaded for the first time. It will notify the environment (runner) about that and the chain will stall at this point. Of course, waiting for the next form to be submitted. Before something else distracts us, let's see what I meant here in the code:

<?php

  
/**
  * A 'runner' - environment for managing form wizards
  *
  * @author Yuri Makassiouk <racer@bfpg.ru>
  */
  //include('constants.php3');
  //this is the directory where forms (wizard's screens) reside:
  
$wizardRoot '';
  if (!isset(
$wizardPage))
    
//initialize sequence. (This value should normally not be hard-coded)
    
$wizardPage 'reg_form01.php';
  
clearstatcache();
  
  while (
$wizardPage) {
    
$page = ($wizardRoot $wizardPage);
    
//echo ' ' . $wizardPage . ' ';  // - uncomment this to monitor what's going on
    
$wizardPage ''// this will stall the wizard
    
if (file_exists($page)) {
      
// the included file may either set the global variable $wizardPage
      // and that will continue the loop instantly
      // or it may have the pair 'wizardPage=...' get submitted 
      // with a form the file contains.
      
include($page); 
    }
    else {
      echo 
"<br><b>Form wizard:</b> cannot find '$page', aborting wizard";
      
// this break isn't really necessary - no included file - nobody to 
      // set $wizardPage being != ''. But just in case, you know =)
      
break;
    }
  }

?>
Now, if you put the code above in a separate file, you may then include that file in your nicely formatted page. This page will be displaying your wizard's screens.

Implementing the FormProcessor class

But this was still quite far from our original subject, we were talking about object-oriented forms here, remember? Let's have a look at FormProcessor and see what's under its hood. I had to strip the code of PHPDoc-style comments, because I think it requires step-by-step explanations, while reading through the comments in arbitrary order might confuse. Version of this source with PHPDoc comments is available for download from here.

<?php

  
/**
  * An abstract class implementing generic functionality for processing user's input
  *
  * This class encapsulates generic functions for working 
  * with data coming from user forms. Descendants must only override certain 
  * functions that perform context-specific tasks, like custom checking of 
  * data, storing correct data, etc.
  *
  * @author Yuri Makassiouk <racer@bfpg.ru>, 
  *  Mission & Media <firma@missionandmedia.com>
  */
  
class FormProcessor {
    var 
$Name;
    var 
$FirstTime;
    var 
$Values;
    var 
$Errors = array();
    var 
$ErrorMessageFormat '<br>%s';
    var 
$wizardPage;
    var 
$fieldPrefix 'mm_form_data_';

    function 
FormProcessor($Name$wPage='') {
      
$this->Name $Name;
      
$this->wizardPage $wPage;
      
      
$this->Values $GLOBALS[$this->fieldPrefix.$this->Name];
      
$this->FirstTime = (count($this->Values) == 0);

      if (!
$this->FirstTime)
        
$this->CustomCheck();
        
      if (
$this->IsCompleted()) {
        
$this->StoreData();
        
$GLOBALS['wizardPage'] = $this->NextWizardPage();
      } 
      else
        
$this->DisplayForm();
    }
    
    function 
IsCompleted() {
      return (!
$this->FirstTime && count($this->Errors)<=0);
    }
    
    function 
CustomCheck() {}
    
//abstract
    
    
function DisplayForm() {}
    
//abstract
    
    
function NextWizardPage() {}
    
//abstract
    
    
function StoreData() {}
    
//abstract
    
    
function Additional() {
      if (
$this->wizardPage) :
    
?>
    <input type="Hidden" name="wizardPage" value="<?php echo $this->wizardPage?>">
    <?php endif;
    }
    
    function 
Set($Name$Value) {
      
$this->$Name $Value;
    }
    
    function 
ErrorReport($Name) {
      if (isset(
$this->Errors[$Name]))
        
printf($this->ErrorMessageFormat$this->Errors[$Name]);
    }
    
    function 
GetInitialValue($Name) {
      if (isset(
$this->Values[$Name]))
        return 
$this->Values[$Name];
      else
        return 
false;
    }
    
    function 
InitialValue($Name) {
      echo 
$this->GetInitialValue($Name);
    }
  }

?>
One of the key principles in FormProcessor's functionality is that it knows how HTML form's inputs are named. Later on in this article, I will show how we automate naming of fields, now just take it as it is: FormProcessor's descendants know name of the array that form's values are submitted into. Inside the member functions of the class, this name is evaluated as...
$this->fieldPrefix . $this->Name
...where $this->Name is form's name that we pass as constructor's parameter. $this->fieldPrefix is defined as data member of the class, but you can think of it as of a constant value that remains the same throughout lifetime of instances of the class. Its only purpose is to transparently add 'uniqueness' to the field names, so that they do not accidentally get mixed with some other program's objects.
The first thing generic constructor does is it 'smells the air around'. It assigns global variable (whether it is set or not) with this magic name to $this->Values. Then, if $this->Values is not empty, i.e. form's fields were submitted before, FormProcessor decides that it is the not the first time this form is on the arena. In this case, it calls the abstract $this->CustomCheck() member function. Overriding this function in sub-classes allows these classes' authors to implement whatever custom conditions checking they fancy. Of course, being a member function of the class gives CustomCheck() access to $this->Values array, which contains all values that form is working with as an associative array. If overridden CustomCheck() finds error(s) that should not allow the form to be considered as completed, it should write error messages in $this->Errors data member. It is also treated as an associative array with the same association as $this->Values. In other words, each submitted value has a potential error message linked with it which, if not empty, indicates a fault in this value.
Then, and this is my favorite thing about abstract generic classes, the rest of constructor reads like English: if the form is completed, let's somehow store the data it has. Otherwise, let's display it (again or for the first time - IsCompleted() returns 'false' if this is the form's first time). Of course, StoreData() and DisplayForm() are calls to abstract member functions that should be overridden in sub-classes. I hope you have noticed that in case form is completed, besides storing data constructor also assigns a new value to the global variable $wizardPage. This action tells the 'runner' - executing environment - what form it should load next ('while' loop in runner will make another iteration).
Can you guess where the next $wizardPage's value comes from? Of course, from an abstract function that should get overridden in a sub-class.
Meaning of the rest of the FormProcessor's machinery should become clear from the following discussion. And now, before we attempt to build something useful with our class, I have to explain how HTML form's inputs know the names they should be assigned.

Inputs - brief introduction

In fact, auto-assigning inputs' names really was the initial idea that pushed me towards developing of another set of classes. The classes that would serve as object wrappers for HTML form's input controls. In this article I would really like us to concentrate on the FormProcessor class and its descendants. Developing of a properly analyzed hierarchy of classes for form controls deserves a separate discussion. So, I suggest that we only talk about form controls classes as much as we need for completing this study.

<?php

  
/**
  * A small hierarchy of classes that 
  * serve as object wrappers for HTML form's inputs
  */

  /**
  * An abstract class representing generic form control
  *
  * @author Yuri Makassiouk <racer@bfpg.ru>, 
  *  Mission & Media <firma@missionandmedia.com>
  */
  
class FormControl {
    var 
$Name;
    var 
$form;
    var 
$Value;
    var 
$Attributes;
    var 
$FormName;
    var 
$InputName;
    
    function 
FormControl($Aform$AName$AValue=''$AAttributes='') {
      
$this->Name $AName;
      
$this->form $Aform;
      
$this->Value 
        (
$this->form->FirstTime)?$AValue:($this->form->Values[$this->Name]);
      
$this->Attributes $AAttributes;
      
$this->FormName $Aform->Name;
      
$this->InputName sprintf("%s%s[%s]"
        
$this->form->fieldPrefix$this->FormName$this->Name);

      
$this->Display();
    }
    
    function 
InputName() {
      echo 
$this->InputName;
    }
    
    function 
Display() {
      echo 
$this->Render();
    }
    
    function 
Render() {}
    
//abstract
  
}
  
  
/**
  * Class representing a text control
  * 
  */
  
class TextInput extends FormControl {

    function 
Render() {
      return 
"<input type=\"Text\" name=\"".
        
$this->InputName."\" value=\"$this->Value\" $this->Attributes>";
    }
  }

  
/**
  * Class representing a set of radio buttons
  * 
  */
  
class RadioButtons extends FormControl {
    var 
$options;
    var 
$ItemFormat '%RBTN&nbsp;%LABEL';
    var 
$separator '<br>';
    
    function 
RadioButtons($Aform$AName
        
$OptionList$AValue=''$AAttributes=''
        
$AItemFormat='%RBTN&nbsp;%LABEL'$Aseparator '<br>') {
      
$this->options $OptionList;
      
$this->ItemFormat $AItemFormat;
      
$this->separator $Aseparator;
      
$this->FormControl($Aform$AName$AValue$AAttributes);
    }
    

    function 
Render() {
      
$i=0;
      
$out '';
      while (list(
$key$val)=each($this->options)) {
        
$item $this->ItemFormat;
        
$item str_replace('%RBTN'
            
sprintf("<input type=\"Radio\" name=\"%s\" 
            value=\"${key}\" $this->Attributes%s>"

            
$this->InputName$key==$this->Value?' checked':''), $item);
        
$item str_replace('%LABEL'$val$item);
        if (++
$i!=count($this->options))
          
$item .= $this->separator;
        
$out .= $item;
      }
      return 
$out;
    }
  }

?>
As you can see, constructor of the base class (FormControl) does all the job of linking the control to form object, reference to which is passed as one of parameters. Then, the name of HTML input is evaluated. It should be such that the value will be submitted as part of an array which the form (descendant of FormProcessor) will be looking for. Name of input is then stored into a local data member. Control also receives the default value as one of constructor's parameters. This value will be used if the form that contains the control is displayed for the first time. Control is accessing a data member of form class to check this. Otherwise, the default value will be taken from the form's $Values array.
Then the constructor calls the Display() member function, which echoes output of the Render() function. This function is abstract and should be overridden in sub-classes so that they produce correct HTML code to represent themselves in pages. See definitions of the two other classes: TextInput and RadioButtons for illustration of this.

Example of a form

Now we are equipped for the final touch: building a small example. I will demonstrate creation of the first page of a wizard that should collect some information. Let's say, about a subscription to some internet resource. As the first step, the user must enter the username he is going to use, correct email address and select type of the registration. Based on the registration type user chooses, the wizard should proceed to a corresponding screen. Before the wizard can proceed, it validates values that the user enters. I will not build the forms for the second step. There's not much interesting to do without introducing additional input classes; and I would still like the article's subject to stay in the center of discussion.

<?php

  define
('MinNameLength'5);
  
  class 
RegForm01 extends FormProcessor {
    
    function 
CustomCheck() {
      if (
strlen($this->Values['Name']) < MinNameLength)
        
$this->Errors['Name'] = 
            
'Username should contain at least ' MinNameLength ' symbols';
        
      if (
$this->Values['Email']) {
        if (!
eregi('^[-!#$%&\'*+\\./0-9=?A-Z^_\`a-z{|}~]+
            @[-!#$%&\'*+\\/0-9=?A-Z^_\`a-z{|}~]+\.[-!#$%&'
*+
            
'\\./0-9=?A-Z^_\`a-z{|}~]+$'$this->Values['Email']))
          
$this->Errors['Email'] = 'Value entered does not 
            appear to be a valid e-mail address'
;
      } else {
        
$this->Errors['Email'] = 
            
'E-mail address is required to complete subscription';
      }
    }
    
    function 
NextWizardPage() {
      
//decision about what wizard's screen to load next is made here,
      //based on the submitted values:
      
return ($this->Values['PaymentMethod'] == 
        
'P')?'reg_form_free.php':'reg_form_payed.php';
    }
    
    function 
StoreData() {
      
// we will not discuss storing persistent variables in this article.
      // here would normally appear code that updates a database or
      // stores session variables
      
STORE_VARIABLE($this->Values['Name']);
    }
    
    function 
DisplayForm() {
    
// I'm not sure you meet a form like this on a real web-site =), but I hope
    // it'll do for the example

?>
<form action="<?php echo $PHP_SELF?>" method="POST">
    Username: <?php new TextInput($this, 'Name', '', 'size="30" maxlength="100"')?>
    <?php $this->ErrorReport('Name')?>
Email: <?php new TextInput($this, 'Email', '', 'size="30" maxlength="100"')?> <?php $this->ErrorReport('Email')?> <br><br> Subscription type:<br> <?php new RadioButtons($this, 'SubscrType', array('P'=>'Preview', 'M'=>'Membership'), 'P')?> <?php $this->ErrorReport('SubscrType')?> <br> <br> <?php $this->Additional()?> <input type="submit" name="Submit" value="Submit"> </form>

<?php

    
}
  }
  
  
$form = new RegForm01('RegForm01''reg_form01.php');
?>

This code can also be downloaded from here.

Conclusion
Object-oriented programming is an efficient and powerful technique. It allows one to create reusable, easily adjustable components that make programming process fast and, honestly, very much fun, too. I hope, my little experience that I presented here will inspire you to build your own libraries of classes that would be a basement for complex and sophisticated web applications.
-- Yuri