Posted by: Donald Howard | April 12, 2012

Handling User Input Errors

A

Handling User Input Errors Session

A

The standard way to collect a User’s input is to set up one script in which the User enters information into an HTML form and then submits the form. The form entries, generally in the form of $_POST variables, are then validated by a second script. All within the same Controller (index.php) file. This is a good approach which I strongly recommend. You’ll see this design used throughout my sessions.

Validation is one of the few areas where I think we should program both a Client and a Server response. The User benefits are just to great to ignore even when the price is a significant amount of time. So this session will look at jQuery to address the Client Side and PHP for the Server.

First a quick overview of my objectives in input validation:

  1. Make it easy for the User to correct typos,
  2. Identify clearly required fields,
  3. Present all errors not just first error,
  4. Make it clear in which field the error occurred,
  5. Provide a clear and concise description of the error.

Sounds reasonable, yes?

Important: You can add Client Side validation but you can NEVER rely solely on it. You always have to have Server Side validation. Why? Because the user can turn-off Client Side script with a click of the mouse and you generally don’t want to make javascript a requirement to access your site. Though I have seen that done. The Server Side validation should be designed as if there were NO Client Side validation at all!

So I’m going to begin building the Server SIde logic as if there was no Client Side. Lets start with an examination of the Controller file that will centrally control the scripts. This is my typical two script process for gathering first gathering user input and then validating / saving or handling errors. In this example a new authorized user is being added to the database.

First script gather the user’s input:

            if (isset($_POST['userNew']) || (isset($_SESSION['action'])
                         && $_SESSION['action']==’userNew’)) {
                   // Reset session variable ……………………..
                   unset($_SESSION['action']);
                  include_once $_ENV['siteRoot'].
                               ‘/resources/componentPak/StateAbbr.php';
                  $states = StateAbbr::get_states();
                   // Present new Auth User form ……………..
                  include ‘/html/newAuthUser.html.php';
                  exit();
         }
 

The condition statement that wraps this script allows two means of access. The first isset($_POST['userNew']) is generated when the user clicks on a submit button in the Controller’s main menu (default form). The HTML would look something like this:

               <button
                           type=”submit”
                           name=”userNew
                           <?php echo $level==3 ? ‘ ‘ : ‘DISABLED'; ?>
               >
                          New User
              </button>
 

As you can see the name assigned to the button element is ‘userNew’. The same as the post variable we are checking above. When the user clicks this button the form is submitted and the Controller file is entered from the top, processing down until it satisfies a condition or falls to the bottom and executes the default script.

The second condition is (isset($_SESSION['action']) && $_SESSION['action']==’userNew’). This session variable ‘action’ is used by all my validation scripts. Whenever a user’s input fails validation this session variable is set. Each form of validation has a different value it sets this session variable to. Think of it as a flag. Validating a new authorized user we set the action flag to ‘userNew’ whereas when validating an edit authorized user we set the action flag to ‘userEdit’.

Essentially this action flag will bring the user back to the same form they originally entered data into. As a matter of fact they won’t even think they have left.  An error(s) will just appear below the form for them to address.  With the same information populating the form that they just submitted (with the exception of passwords).  In this way it is relatively easy for them to see their error and make the necessary changes.

We will talk more about the setting of the session action variable when we discuss the validation script.

The next thing that we do in the User input script is to unset the session variable.  Remember, a session variable will remain for the length of the session (session scope).  If we don’t unset it, we may find ourselves looping back to this script.

The next two statements make available a predefined array (think PHP version of enum) of states to be used in the HTML in the form of a dropdown list.

Finally we present the user with the HTML input form. My new authorized user form looks like this:

When they complete the form and click ‘Add User’ the form will be submitted (with all their posted information) and re-enter the Controller file at the top and process down until they satisfy a condition or fall all the way to the bottom and execute the default script. In this example they will satisfy the following condition and begin the validation process:

 
     if (isset($_POST['userAdd'])) {
           try {
                $cErrs = new InputErrors();
                // Create New User Post Array …………………
               $nArray = userArray();
               // Create new authorized user object ………….
               $newUser = $objFac->assembleAppUser($nArray);
               $_SESSION['newUser'] = $newUser;
               // Validate components ……………………………
               $newUser->validate($comp, $cErrs))
                          throw new InputException();
               // Add new authorized user ……………………..
               $daoPDO->addAppUserTran($newUser);
               // Success display for user ……………………..
               $msg = ‘YOUR REQUEST TO ADD A NEW
                         AUTHORIZED USER WAS SUCCESSFUL!';
               include_once $_ENV['siteRoot'].
                         ‘/resources/messagePak/opSuccess.html.php';
               // Clean up the data objects ……………………
               unset($_SESSION['newUser']);
               exit();
          } catch(InputException $i) {
               MessageMgr::processInputErrs(
                         $cErrs, ‘userNew’);
          }
     }
 

This blog session is about handling user input errors so we are going to focus on the validation and the exception handling (highlighted in blue).

My object requirement for most projects follows the following design:

Composite —> Component —> AbstractComponent

That is, a Component Class (essentially an HTML document element) extends the AbstractComponent Class and a Composite Class combines several components. Generally I have a Composite Class defined for my major database tables. Here are some Class definition examples:

THE ABSTRACT COMPONENT

     abstract class AbstractComponent {
          private $value, $nullOkay, $varName, $validator;
          protected function __constructor(Validator $vType, $val, $okay, $vName) {…}
          public function get_value(){…}
          public function set_value($val) {…}
          public function get_nullOkay(){…}
          public function set_nullOkay($okay){…}
          public function get_varName(){…}
          public function performValidation(InputErrors $cErrs) {…}
     }
 

A COMPONENT EXAMPLE

     class PhoneObj extends AbstractComponent {
          public function __construct(Validator $vType, $v, $o, $vn){…}
          public function get_formatted(){…}
          public function __toString() {…}
          private function format10DigitPhone($phoneDigits) {…}
     }
 

A COMPOSITE EXAMPLE

     class Users implements Validator {
          private $id, $firstName, $lastName, $title, $company;
          public function __construct() {…}
          public function get_id(){…}
          public function get_firstName(){…}
          public function get_lastName(){…}
          public function get_title(){…}
          public function get_company(){…}
          public function validate(AbstractComponent $o, InputErrors $cErrors) {…}
          public function __toString(){…}
     }
 

To mitigate some of this complexity I use a CompositeFactory Class to assemble the Composites and Super Composite I use in this session example.

As you can see in the Controller file I call validation on the Composite Class (AppUser) object $newUser by calling its validate() method. AppUser is something I call a Super Composite. Essentially this means a Composite made up of other Composite Classes. In this example AppUser represents a composite of User, Contact, and Access Composite Classes.

The AppUser->validation($comp, $cErrs) looks like this:

     function validate(AbstractComponent $o, InputErrors $cErrors) {
          $valSuccess=true;
          $valUser = $this->user->validate($o,$cErrors);
          $valCon = $this->contact->validate($o,$cErrors);
          $valAcc = $this->access->validate($o,$cErrors);
          if($valUser==false || $valCon==false
               || $valAcc==false) $valSuccess = false;
          //
          return $valSuccess;
     }
 

The validation process cascades down from the super composite (AppUser) to the composites (User, Contact, Access) to their component’s Validator like this:

The user->validate($o,$cErrors)

     public function validate(AbstractComponent $o, InputErrors $cErrors) {
          $valSuccess=true;
          $valID = $this->id->performValidation($cErrors);
          $valFName = $this->firstName->performValidation($cErrors);
          $valLName = $this->lastName->performValidation($cErrors);
          $valTitle = $this->title->performValidation($cErrors);
          $valCompany = $this->company->performValidation($cErrors);
          if($valID==false || $valFName==false
                || $valLName==false || $valTitle==false
                || $valCompany==false) $valSuccess = false;
          //
          return $valSuccess;
     }
 

AbstractComponent->performValidation($cErrs)

     public function performValidation(InputErrors $cErrs) {
          if(isset($this->validator)){
               return $this->validator->validate($this, $cErrs);
          } else {
               throw new Exception(‘Validator not set for this class/object – ‘
               .get_class().’ / ‘.$this->get_varName());
          }
     }
 

A Class that implements the Validator Interface

$this->validator->validate() method example

     public function validate(AbstractComponent $o, InputErrors $cErrors) {
          $val = htmlspecialchars($o->get_value());
          $okay = $o->get_nullOkay();
          $goodFormat=true;
          $int_options = array(“options”=>array(“min_range”=>0));
          // Empty string will evaluate to null …………………….
          if($val!=null) {
               $val = trim($val);
               $o->set_value($val);
               // Null will fail this test ………………………………..
               if($this->allowNeg) {
                    if(!filter_var($val, FILTER_VALIDATE_INT)){
                         $goodFormat=false;
                         $cErrors->add(get_class($o),
                         ValStringInts::INCLUDES_NONDIGITS);
                    }
               } else {
                    if(!filter_var($val, FILTER_VALIDATE_INT, $int_options)){
                         $goodFormat=false;
                         $cErrors->add(get_class($o),
                         ValStringInts::INCLUDES_NONDIGITS);
                    }
               }
               // Check empty string ………………………………..
               if(strlen($val)<1) {
                    if(!$okay) {
                         $goodFormat=false;
                         $cErrors->add(get_class($o),
                         ValStringInts::NO_COMP_VALUE);
                    } else {
                         // Rather value be null than empty string!
                         $o->set_value(null);
                    }
               }
               // Check minimum digits …………………………..
               if($this->minDigits!=null & strlen($val)<$this->minDigits){
                    $goodFormat=false;
                    $cErrors->add(get_class($o),
                              ValStringInts::LESSTHAN_MIN_LENGTH);
 
               }
               // Check maximum digits ………………………….
               if($this->maxDigits!=null && strlen($val)>$this->maxDigits) {
                    $goodFormat=false;
                    $cErrors->add(get_class($o),
                              ValStringInts::GREATERTHAN_MAX_LENGTH);
                }
          // If is null ………………………………………………..
          } else {
               if(!$okay){
                    $goodFormat=false;
                    $cErrors->add(get_class($o),
                              ValStringInts::NO_COMP_VALUE);
               }
          }
          return $goodFormat;
     }
 

As you can see the validation logic is found in the Class that implements the Validator Interface and the way we have structured a Validator Type to a Component Class is an example of a Strategy Pattern.

That is a very long explanation to simply say that when we call ‘$newUser->validate($comp, $cErrs)’ we are validating all the elements that make up the AppUser Class object ($newUser). Notice in the Validator validate() method above that we do not stop the script if a validation fails, rather we save the error to an array ($cErrors). All the validation() methods return a boolean result that boils up to the Controller file script that originally made the call.

     If(!$newUser->validate($comp, $cErrs)) throw new InputException();

As you can see if a validation error occurred we throw a new InputException() which is then caught by the scripts try / catch block. It is in the catch block that our error handling process takes place.

To handle errors we use another object that manages our messages to the User. This Class is MessageMgr which has a number of static methods to assist the process. In this case we are calling the MessageMgr’s processInputErrs($cErrs, ‘userNew’) method. Note that two arguments are passed with the call to this method. The first is the InputErrors Class ($cErrs) which is essentially an array and second the string ‘userNew’ which is the action flag we discussed earlier.

The processInputErrs() method I’m using looks like this:

     static function processInputErrs(InputErrors $errs, $flag){
          $_SESSION['errs']=$errs->get_errArray();
          $_SESSION['action'] = $flag;
          header(‘Location: .’);
          exit();
     }
 

The function sets up a session variable ‘errs’ to pass an array of errors back to the HTML form that the User originally used to enter their data. We then set the session variable ‘action’ value to the string argument ($flag). We use the header(‘Location: .’); to simulate a form submit without data. Lastly we exit the current script and enter the Controller file at the top, restarting the process. Because we set the action flag we will end up back in the First script where we originally gathered the user’s input.

Within the HTML document is another MessageMgr static method that will display errors should they occur.

     <?php
          MessageMgr::displayInputErrs();
     ?>
 

The method displayInputErr() looks like this:

     static function displayInputErrs() {
          if(isset($_SESSION['errs'])) {
               $errArray = $_SESSION['errs'];
               // Print errors in html form div …………..
               print “<div style=\”color: red; margin-right: 30px margin-left: 30px;\”>”;
               print “<hr>”;
               print “<h1>” . ‘Input Error:’ . “</h1>”;
               for($i=0; $i<count($errArray); $i++) {
                     print ‘<b>’.$errArray[$i].'</b><br /><br />';
               }
               print “<hr>”;
               print “</div>”;
               // Unset session input errors …………….
               unset($_SESSION['errs']);
          }
     }
 

Notice that if there are no $_SESSION['errs'] nothing will be inserted into the HTML! Also note that if there are session errors the session variable is ‘unset’ after the errors are displayed.

So if we make some obvious User entry errors the result should look something like this:

The validation process is very important to any website that collects user input. It may appear that the approach I am using is overly complicated and that may be true. It took some time to create and test the logic initially but now each time I set up a new project I import the componentPak, validationPak, and messagePak to the project and I’m ready to go. The way validation is designed I can add new Classes to implement the Validator Interface to expand the capability without having to modify anything. I encourage you to come up with your own approach. Try not to focus on how much time it takes to set up. I’d rather you concern yourself with how easy it will be to implement time after time.

Now its time to implement the Client Side validation process using jQuery. To do this yourself you will need at minimum jquery.js and jqueryValidate.js. I am also using Josh Bush’s maskedinput.js which I really like and use a lot for things like phone numbers. Finally I will create a javascript file (authUserInput.js) for the authorized user input page which I will store with the other jQuery script files (‘/’.$_ENV['siteRoot'].’/resources/jqueryPak/authUserInput.js’).

I have placed the jQuery libraries in the Administration USER Controller which handles the adding, updating and deleting of authorized users. Here is an example of how that is done:

     $validate=’/’.$_ENV['siteRoot'].
               ‘/resources/jQueryPak/jqueryValidate.js';
     echo “<Script
          language=JavaScript
          src=’$validate’
          type=’text/javascript’>
     </Script>”;

This makes the library available to every script run from this Controller. If you only need it for a single web page it is probably more efficient to load it in as part of the page.

Lets jump right in. Our objective is to create a Client Side that looks the same as the Server Side with regard to error handling. That means we want to show the errors immediately under the form and above the command buttons. And we want the errors to be shown in red.  Here is the jQuery Validation method I wrote to do this:

     $(document).ready(function(){
          $(“#frmAuthUser”).validate({
               onfocusout: false,
               onkeyup: false,
               onclick: false,
               errorLabelContainer: “#jqErrCon”,
               wrapper: “p”,
               rules: {
                    “fName”: {required: true,minlength: 2},
                    “lName”: {required: true,minlength: 2},
                    “email”: {required: true,email: true},
                    “phone”: {required: true},
                    “zip”: {minlength: 5},
                    “zipext”: {minlength: 4},
                    “password”: {required: true,minlength: 6},
                    “confirm”: {required: true,minlength: 6,equalTo: “#password”}
               },
               messages: {
                    “fName”: {required: “FIRST NAME: This component
                              requires a value but none was provided!”,
                                         minlength: “FIRST NAME: String length is
                              less than minimum!”
                    },
                    “lName”: {required: “LAST NAME: This component
                              requires a value but none was provided!”,
                                         minlength: “LAST NAME: String length is
                              less than minimum!”
                    },
                    “email”: {required: “EMAIL ADDRESS: This component
                              requires a value but none was provided!”,
                                       email: “EMAIL ADDRESS: This component is
                              not a properly formatted email address!”
                    },
                    “phone”: {required: “PHONE NUMBER: This component
                              requires a value but none was provided!”
                    },
                    “zip”: {minlength: “ZIP CODE: String length below minimum!”,
                                  maxlength: “ZIP CODE: String length exceeds the maximum!”
                    },
                    “zipext”: {minlength: “ZIP EXTENSION: String length below
                              minimum!”,
                                        maxlength: “ZIP EXTENSION: String length exceeds
                              the maximum!”
                    },
                    “password”: {required: “PASSWORD: This component requires
                              a value but none was provided!”,
                                                minlength: “PASSWORD: String length below
                              minimum!”
                    },
                    “confirm”: {required: “CONFIRMATION: This component requires
                              a value but none was provided!”,
                                            minlength: “CONFIRMATION: String length below
                              minimum!”,
                                             equalTo: “CONFIRMATION: Password components
                              did not match!”
                    }
               },
               invalidHandler: function(form, validator) {
                    $(“#jqErrCon”).show();
                    $(“#jqErrHR”).show();
               }
          });
     });
 

In case my explanation of jQuery Validation plug-in leaves you with questions the complete instructions can be found here – http://docs.jquery.com/Plugins/Validation/.

Note that the validate is performed within the standard ‘$(document).ready(function(){});’. That is after the document is loaded. The ‘$(“#frmAuthUser”).validate({ … })’ statement begins the definition of the validation process. Here we will place the logic for how we want the validation to occur. The jQuery symbol ($) is followed by the selector, in this case the form represented by the id = ‘frmAuthUser’ followed by the validate method. The form ‘frmAuthUser’ should contain all of the elements in the document that we are validating. If one or more elements you wish to validate is not found within the form tags it will be ignored.

There are four validation timings that can be applied to an element on a web page; 1) when the element loses focus, 2) when the user types something in an element, 3) when the user clicks an element, and 4) when the user clicks a submit button. As you can see I have turned off all except the ‘onsubmit’ option. In this way the errors will be shown after the user submits the form, the same way it is done on the Server Side approach.

Next I am defining a container where my errors will appear. The default is to place them after the page element and I like that but that is not how we do it Server Side. The container is identified by an element id (errorLabelContainer: “#jqErrCon”) and then we define how we will wrap each error. In this example I will use

paragraph tags. This will give me a line break between each error just like I have on the Server Side. To make this work you also have to have something like this in your web page to define the error container:

     <div id=”jqErrCon” style=”display:none;”>
          <hr>
          <p><h1 style=”color: red;”>Input Error:</h1></p>
     </div>
          <hr id=”jqErrHR” style=”display: none;”>
 

And some CSS to define the appearance of the error message class:

     .error {float: none; color: red; padding-left: .5em; font-style:italic;
               vertical-align: top; font-weight: normal; border: 1px dashed #d50000;}
 

Next I define the ‘rules’, these are the page elements I want to have validated and what parameters to evaluate. For example the first rule is for fName (which is the element name) and it states that user input is required and that minimum length is 2 characters. I don’t bother to validate the maximum input because I use the standard HTML attribute ‘maxLength’ to set that limit. Note should an element fail they will be listed in the order in which they appear in the ‘rules’ definition.

I follow the ‘rules’ for each element with a ‘message’ for each element. I want the messages to be the same as those used in the Server Side process. This is really something that should be put in a property file to make it easy to manage (no consistency issues); maybe in another session for right now I’m simply adding the messages to the jQuery script. As you can see the syntax is the ‘name’ of the element followed by the attribute being evaluated with its corresponding message. A default message will be used if you do not provide a message for an attribute that is evaluated and fails.

The last is the invalid handler function ‘invalidHandler: function(form, validator) { … }). This is a callback for custom code when an invalid form is submitted. Called with an event object as the first argument, and the validator as the second. In this example we toggle the visibility of the active HTML page’s presentation div tag (tag shown above). This is the tag that we have associated with the ‘errorLabelContainer’ we defined at the beginning of the validate method.

That’s it, we now have a Client Side approach that is comparable with our Server Side. Should javascript be enabled and an error in entry occur the submit is canceled and errors are displayed. Making the same mistakes we made while testing the Server Side validation earlier, we get:

Similar look and feel but not exactly the same. Keep in mind that we CANNOT replace Server Side validation with Client Side. To bad really is takes far less code using jQuery to do the same things. Lets note the visible differences: 1) The input boxes in the form are highlighted in red with a dotted line (CSS) 2) the errors are italicized (CSS) 3) the password and confirmation are still entered and 4) any content in the invalid text box is red (including new entries made by user).

Note that with jQuery you can compare two entry elements using the equalTo() method. Very nice for password confirmation!

There are a number of different ways to accomplish the Client Side with jQuery. Here is a simpler way to go that accomplishes the same thing, though the results don’t look anything like the Server Side. But does it matter to you that they look similar? After noodling over it I have decided that you should take advantage of every logical and visible advantage you can squeeze from a tool for your user’s benefit. That has led me to the following simplified script:

     $(document).ready(function(){
          $(“#frmAuthUser”).validate({
               onfocusout: false,
               onkeyup: false,
               onclick: false,
               wrapper: ‘li’,
               submitHandler: function(form) {
                    form.submit();
               }
          });
     })
 

We substitute classes and attributes added to the HTML page for the ‘rules’ we placed earlier in the jQuery script. Like this:

     <input
          type=”text”
          id=”fName”
          name=”fName”
          maxLength=”50″
          minlength=”2″
          class=”required”
          onfocus=”this.select();”
          value=” <?php isset($_SESSION['newUser'])
               ? $_SESSION['newUser']->get_user()->
               get_lastName()->get_value() : ”; ?>
     />

Note that we have entered the ‘minlength’ evaluation parameter to our HTML element as an attribute and the ‘required’ evaluation parameter as a Class. According to the manual if an evaluation parameter includes an argument (like minlength’s argument 2) it should be entered as an element attribute. Note also that in the NetBeans IDE, if you have the HTML Validator enabled, this will show as an error. Don’t worry about it, you can safely ignore NetBeans helpful warning in this case.

And now the result should look something like this:

As you can see the code is even simpler, though we have to add some classes and attributes to the HTML as ‘rules’. I got rid of the container in favor of having the errors show up below the input element that failed validation. I also chose not to create custom messages and went with the defaults instead. Which do you favor?

Okay we are almost done. We still have to handle the ‘Cancel’ button on the Client Side approach. How do we process the Cancel request without re-validating the form?

The first approach I tried was a jQuery solution. I added the following event process to override the validation on a submit from the ‘Cancel’ document element:

     $(“#cancel”).click(function () {
          $(‘#frmAuthUser’).validate().cancelSubmit = true;
          document.location.href = “”;
     });
 

This does effectively cancel the default submit process and navigates back to the Controller file as we desire. However, without the post variable that we use in our Controller file to call scripts this approach does not fire my Cancel script. In the cancel script I like to cleanup any Session objects that can be garbage collected by the system. My cancel script looks like this:

          if(isset($_POST['cancel'])){
               unset($_SESSION['newUser']);
               unset($_SESSION['editUser']);
               unset($_SESSION['deleteUser']);
               unset($_SESSION['action']);
          }
 

Notice that we are NOT clearing the Session objects associative array using the ‘session_unset()’ function. We don’t, because we want to keep the $_SESSION['userAuth'] variable so that our user never has to sign in twice to the same session.

So my script doesn’t do everything I want it to do so what is the solution. Well while I was noodling over a jQuery solution a friend pointed out to me that I could just place the ‘Cancel’ button within its own form tags. Sometimes the simple solutions are so hard to see. It worked like a charm.

There is much more that can be added to both the Server and Client Side with regard to validation but I think we’ve touched on all the essential pieces.

Enjoy.

Don

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: