Reply to comment

Process PHP Forms Like A Champ : Part I

Process PHP Forms like a Champ -- Part I : The Hello Form

Series
  1. Part I -- The Hello Form
  2. Part II -- Cleaning Up Hello Form
Quick Links
Synopsis

I constantly see the same questions, problems, and scenarios arise in programming forums when it comes to creating and processing web forms. My goal with this series of articles is to give all beginning programmers the foundation necessary to solve 99% of problems they will encounter while processing forms.

The reader is assumed to have a basic knowledge of PHP.

User-Server Overview

We’ll start by looking at how the user and web server interact with each other.

User-Server Overview

The person represents the user, or client. Anything that happens on the client, or user, side of the interaction is beyond your control.

The computer represents our web server; since this is where PHP lives this is where we work our magic.

In step #2 of the diagram, the web server must build the form so that it can be sent back to the client. An appropriately named PHP function for this step would be ShowForm().

In step #4 the web server has received the client’s form submittal and must validate it. Form validation is a crucial step that can not be overlooked. If you skip form validation, then at best your program will not work and at worst you will open your web server up to malicious attackers. There are only two possible outcomes from validation; either the form will be valid or it will be invalid. An appropriately named PHP function that validates the form would be named ValidateForm().

If the form is invalid the server must go back to step #2 and rebuild the form so that the user can try again; in rebuilding the form it is considered best practice to repopulate the form fields with the data the user originally entered as well as telling them where they went wrong.

If the form is valid, then we can process it. Processing the form could be any combination of actions but the most common ones are interacting with a database, sending automated e-mails, or both. As the final step of processing it is good practice to output some sort of message to your user so they know that something happened. In the diagram, form processing is step #4b and an appropriately named PHP function might be ProcessForm().

Form Logic

Now let’s take a moment and think about the logic we must enact on the server in order to make the diagram a reality. I stated that we essentially have three functions: ShowForm(), ValidateForm(), and ProcessForm().

Let’s assume that ShowForm() and ProcessForm() return strings that will be sent back to the client. In the case of ShowForm() this string will be the HTML that composes the form. In the case of ProcessForm() this string will be some sort of success message.

Let’s also assume that ValidateForm() returns true if the form is valid and false if it is invalid.

With the above assumptions, this might be our PHP code:

$submitted = empty($_POST); // check if submitted
if( $submitted ){
  if( ValidateForm() ){
    $form = ProcessForm(); // form was valid, so process
  }else{
    $form = ShowForm(); // form was invalid, so show
  }
}else{
  $form = ShowForm(); // form was never submitted, so show
}
echo $form; // send as output

Basically we check if the form has been submitted and if it hasn’t we call ShowForm(). If the form has been submitted we attempt to validate it. Remember that ValidateForm() returns true if the form is valid and false if it is not, so we can use it directly in our if statement. Based on the validity of the form we either process it or show it.

The code can be shortened slightly by deciding that the first step should be form validation. You might be wondering, "How can I validate a form if I’ve never shown it to the user?!" If you are, then my response is, "If a form has never been submitted, is it valid or invalid?"

if( ValidateForm() ){
  $form = ProcessForm(); // form was valid, so process
}else{
  $form = ShowForm(); // form was invalid, so show
}

Don't worry if that doesn't make perfect sense yet, it should soon.

The Hello Form

We're going to create an extremely simple form. It will have a single text field the user can type their name in. It will also have a button labeled "Say Hello" that will greet our user when clicked. In terms of validation we are going to require the user to enter something in the text field and that they only enter alphabetic characters, i.e. no digits or punctuation.

ValidateForm()

This seems backwards but I'm going to start with the validate function.

Validate Flow Chart

We start by checking if the form is submitted. If it's not then we make an early return statement, returning false.

If we do not make an early return, then the form has been submitted. We start checking each one of our validation conditions. Here again are the conditions for the simple form we're creating:

  1. User must enter their name
  2. The name must be alphabetic

Note that in the diagram if a condition fails we do not return early. Instead we record the error and keep on validating. This will enable us to tell the user all of the validation mistakes that occurred and will result in much happier users, especially with large forms.

Finally, ValidateForm() must return true or false. By checking if we recorded any errors we will know which value to return.

Taking everything I've said so far and turning it into code yields:

  /**
   * Validates the form and returns true or false.
   * @return bool true if valid, false if invalid
   */
  function ValidateForm(){
    global $errors;                    // A dirty hack to pass form errors
                                       // from one function to another.

    // If not yet submitted we return early, returning false.
    // Form data is available in an array named $_POST; if empty($_POST)
    // returns true then the form has not been submitted.
    if(empty($_POST)){
      return false;                    // Not submitted, return early
    }

    // If we have not returned, then the form was submitted and we can continue
    // with validation.

    // Get the name the user typed from the $_POST array.
    $name = $_POST['name'];

    // User must enter a name
    if( strlen( trim( $name ) ) == 0 ){
      // No name entered
      $errors['name_required'] = 'Name is a required field.';
    }

    // User must enter only alphabetic characters
    if( !ctype_alpha( $name ) ){
      // Name is not alphabetic only
      $errors['name_not_alpha'] = 'Name can be alphabetic characters only.';
    }

    // Now we return true or false depending on whether or not we recorded any
    // errors.  We will use PHP's count() function to count the number of
    // error messages in $errors; if count() is greater than zero then we
    // encountered errors and return false, otherwise true;
    if( count( $errors ) > 0 ){
      return false;
    }else{
      return true;
    }
  }
ShowForm()

What follows is the logic of the ShowForm() function.

Show Flow Chart

The first step is to determine if we had errors during validation. If we had errors, then we build an error message to display to the user.

Next, whether or not we had errors, we create default values for each of our form inputs. If the form has never been submitted these values will be empty. If the form has been submitted but we’re showing it again, that means there were errors; in order to make the user’s experience as painless as possible we should repopulate the form with the data they entered previously.

Next we create the HTML (or XHTML) markup for the form.

Finally we return the concatenation of all the text strings that have been built.

  /**
   * Builds the markup for the form and returns it.
   * @return string form markup
   */
  function ShowForm(){
    global $errors;                    // A dirty hack so that we can detect
                                       // errors we found during validation.

    // First we check for errors.  If count($errors) is greater than zero, then
    // we had errors from a previous submission.
    $error_string = '';                // Define here to avoid undefined
                                       // variable errors.
    if( count($errors) > 0 ){
      // Had errors, so we build an error string.  I like to use an unordered
      // list.
      $error_string = implode('</li><li>', $errors);
      $error_string = "
        <div class='form_errors'>
          The following errors were encountered while processing your request:
          <ul><li>{$error_string}</li></ul>
        </div>
      ";
    }

    // Now we build our defaults, we only have one field so this is easy.
    // The basic format is:
    // $field_name = isset($_POST['field_name']) 
    //             ? $_POST['field_name'] : '';
    // Which means if the field_name exists in post, use it.  Otherwise use a
    // blank value.  Doing it this way will prevent PHP from reporting "index
    // undefined" errors.
    $name = isset($_POST['name']) ? $_POST['name'] : '';

    // Now we build our form markup.  Notice that I embed $error_string and
    // $name in the appropriate places.  Do not be alarmed if you do not fully
    // understand the form markup, that will be explained in a future article.
    $form = "
      <form method='post' action=''>
        {$error_string}
        <label>Name:</label>
        <input type='text' name='name' value='{$name}' size='16' />
        <input type='submit' name='submit' value='Say Hello!' />
      </form>
    ";
    return $form;                      // And return the result...
  }
ProcessForm()

This is where we perform our processing magic. In reality we would be interacting with a database, sending e-mails, or some other significant action. But because this is just a simple example, I'm just going to create a message to display to our user.

  /**
   * Process the form.  This would be where we update the database, send
   * e-mails, or whatever else it is our form is intended to do.
   */
  function ProcessForm(){
    return "Hello, {$_POST['name']}!";
  }
Putting it all Together

For completeness, here is the entire script along with a live example.

Hello Form

<?php
  // $errors will be an array of validation errors.
  // This variable is global because it needs to be used in both ValidateForm()
  // and ShowForm().  I do not recommend the use of global variables, but using
  // one here simplifies the tutorial.
  $errors = Array();

  // This small chunk of code controls the flow of the entire form script.
  if( ValidateForm() ){
    echo ProcessForm();
  }else{
    echo ShowForm();
  }

  /**
   * Builds the markup for the form and returns it.
   * @return string form markup
   */
  function ShowForm(){
    global $errors;                    // A dirty hack so that we can detect
                                       // errors we found during validation.

    // First we check for errors.  If count($errors) is greater than zero, then
    // we had errors from a previous submission.
    $error_string = '';                // Define here to avoid undefined
                                       // variable errors.
    if( count($errors) > 0 ){
      // Had errors, so we build an error string.  I like to use an unordered
      // list.
      $error_string = implode('</li><li>', $errors);
      $error_string = "
        <div class='form_errors'>
          The following errors were encountered while processing your request:
          <ul><li>{$error_string}</li></ul>
        </div>
      ";
    }

    // Now we build our defaults, we only have one field so this is easy.
    // The basic format is:
    // $field_name = isset($_POST['field_name'])
    //             ? $_POST['field_name'] : '';
    // Which means if the field_name exists in post, use it.  Otherwise use a
    // blank value.  Doing it this way will prevent PHP from reporting "index
    // undefined" errors.
    $name = isset($_POST['name']) ? $_POST['name'] : '';

    // Now we build our form markup.  Notice that I embed $error_string and
    // $name in the appropriate places.  Do not be alarmed if you do not fully
    // understand the form markup, that will be explained in a future article.
    $form = "
      <form method='post' action=''>
        {$error_string}
        <label>Name:</label>
        <input type='text' name='name' value='{$name}' size='16' />
        <input type='submit' name='submit' value='Say Hello!' />
      </form>
    ";
    return $form;                      // And return the result...
  }

  /**
   * Validates the form and returns true or false.
   * @return bool true if valid, false if invalid
   */
  function ValidateForm(){
    global $errors;                    // A dirty hack to pass form errors
                                       // from one function to another.

    // If not yet submitted we return early, returning false.
    // Form data is available in an array named $_POST; if empty($_POST)
    // returns true then the form has not been submitted.
    if(empty($_POST)){
      return false;                    // Not submitted, return early
    }

    // If we have not returned, then the form was submitted and we can continue
    // with validation.

    // Get the name the user typed from the $_POST array.
    $name = $_POST['name'];

    // User must enter a name
    if( strlen( trim( $name ) ) == 0 ){
      // No name entered
      $errors['name_required'] = 'Name is a required field.';
    }

    // User must enter only alphabetic characters
    if( !ctype_alpha( $name ) ){
      // Name is not alphabetic only
      $errors['name_not_alpha'] = 'Name can be alphabetic characters only.';
    }

    // Now we return true or false depending on whether or not we recorded any
    // errors.  We will use PHP's count() function to count the number of
    // error messages in $errors; if count() is greater than zero then we
    // encountered errors and return false, otherwise true;
    if( count( $errors ) > 0 ){
      return false;
    }else{
      return true;
    }
  }

  /**
   * Process the form.  This would be where we update the database, send
   * e-mails, or whatever else it is our form is intended to do.
   */
  function ProcessForm(){
    return "Hello, {$_POST['name']}!";
  }

?>
Final Thoughts

This article is a crash course in form processing, which means that many explanations and details were left out. I hope the reader has a basic understanding of the overall method I used for the simple form in this article because that same process can be applied to almost every single form you will ever create. In the next article I plan to demonstrate how a few minor code changes can clean up the current form and I also plan to introduce a couple of helper functions. In later articles I am planning to give more detailed explanations behind form markup, validation, and other more advanced concepts and techniques.

Reply

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options