Process PHP Forms Like A Champ : Part II

Process PHP Forms like a Champ -- Part II : Cleaning Up Hello Form

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

This article addresses some of the missing items from the simple form introduced in Part I. The reader is assumed to have read Part I and a basic knowledge of PHP.

Globals Are Evil

I did a bad thing when I created the form in Part I; I used a global variable to share information between the ShowForm() and ValidateForm() functions. If you're an experienced programmer, then I've probably lost some credibility with you. If you're new to programming, then you're probably wondering what's so bad about global variables; after all most of the code you've written so far is full of them, right? You'll just have to take this on faith, but global variables are worse than brain-eating zombies and the flesh-eating virus combined. If you can't take it on faith, then use your favorite search engine to perform some research on the topic; I'm sure it's out there.

In order to eliminate the global variable $errors, I'm going to perform a little code shuffling:

Globals Are Evil

  • Globally
    1. Drop the global $errors definition
    2. Create a new function named DoForm() that wraps up the global form logic
      • $errors becomes an array local to this function, thus eliminating the global
    3. ShowForm() and ProcessForm() switch places in the if-statement
    4. ShowForm() now takes $errors as an argument
  • ShowForm()
    1. ShowForm now takes a single parameter named $errors
    2. Drop the global $errors definition in ShowForm()
    3. Add the call to unset() to remove the error indicating an empty post
  • ValidateForm()
    1. Change the global $errors declaration to be a local variable declaration
    2. If an empty post is found, return an array with an empty_post index
    3. Change ValidateForm() to return $errors

Try the form or view the full source.

Preventing Duplicate Submissions

If you aren't aware what is meant by the phrase “duplicate submissions,” then you need to perform a little experiment using the existing form. Go ahead and submit the form and then refresh the web page; your browser should warn you about sending post data.

Browser warnings for sending post data:

Firefox

Internet Explorer

If a user happens to refresh a page that was the result of a form submission, the browser will resubmit the form. These messages are disruptive and can be confusing to the average user. In addition, whatever your form does as the ProcessForm() step will be repeated for each submission; this means a user could place duplicate and unwanted orders at an e-commerce site. You certainly do not want that to happen.

To prevent this from happening, you simply redirect the user with a call to header(). Be warned that all post data will be lost when you perform the redirect; in order to access the post data on the landing page, you will need to preserve it in persistent storage, such as a session.

Please note the following method relies on your ProcessForm() method being able to finish executing before the user has a chance to refresh their browser. If you are running a busy e-commerce site you will need to expand upon this solution to eliminate accidental submission of duplicate orders.

For most intents and purposes, it is sufficient to redirect as the final step of the ProcessForm() function. This behavior can be added to our existing form with a couple minor changes:

Redirect

  • ProcessForm() now does the following:
    1. Stores the contents of $_POST in a session
    2. Redirects to the current page with a call to header()
    3. Calls exit(), which is necessary after redirecting
  • DoForm() now does the following:
    1. Checks for the existence of post data in the session
      1. Post data exists in the session
        1. Retrieve the post data from the session
        2. Say “Hello” to the user
        3. Unsets the post data from the session
      2. Post data does not exist in the session
        1. The function performs our form control logic as it did previously

Also, since the script is now using sessions, you must remember to call session_start() at the top of the script.

Go ahead and try the form again. This time when the form reaches the processing page, refresh the browser; instead of the warning about post data you will see the empty form.

Better Error Messages

If you haven't done so already, try submitting our form with an empty name field. You will see two error messages:

  • Name is a required field.
  • Name can be alphabetic characters only.

While sometimes this behavior is preferable, in most cases it makes sense to display a single error per field. This change can be accomplished with a very minor code change to ValidateForm():

Bettor Error Messages

Technically, the only requirement to accomplish this was to assign both error messages to the same index in the $errors array. However, since the conditions are trivial I combined the if-statements into a single code block as well.

Test the form again and confirm that this works.

GetPostValue()

In order to retrieve post values without receiving 'index undefined' errors, I use the following code in ShowForm();

$name = isset($_POST['name']) ? $_POST['name'] : '';

This is fine for small forms with only a few fields. However as you create many forms or large forms with many fields, you will find that you type this code quite frequently. Should you later decide to grab values from $_POST in a different manner, you will have lots of code to update! I therefore recommend wrapping up this functionality in a small function:

function GetPostValue( $index ) {
  return isset($_POST[$index]) ? $_POST[$index] : '';
}

You can then call this function in ShowForm() and ValidateForm() using:

$name = GetPostValue( 'name' );
Variable Variables

Variable variables are best explained with a small code example:

<?php
  $name = 'Steve';                     // Creates $name
  ${'foo'} = 'bar';                    // Creates $foo
  ${'as' . 'df'} = 42;                 // Creates $asdf
  ${$foo} = 'Hello, World!';           // Creates $bar
?>

Essentially you can create variables whose name depends on an expression evaluated at run time. This can come in handy while creating a form because we often create variables named after the form fields. Combined with the GetPostValue() function above, you could easily create defaults for all of your form fields:

<?php
  $fields = array( 'name', 'addr', 'city', 'state', 'zip' );
  foreach($fields as $field){
    ${$field} = GetPostValue($field);
  }
  // Creates $name, $addr, $city, $state, $zip and each
  // is populated by $_POST['name'], $_POST['addr'],
  // $_POST['city'], $_POST['state'], and $_POST['zip']
  // (or an empty string if $_POST is empty)
?>

Variable variables are convenient but should be used sparingly and only when it makes sense. Your IDE will not be able to detect variable variables and if you're not careful you may accidentally overwrite existing variables.

Magic Quotes

Magic Quotes is one of those things that sounds great in theory but is undesirable in practice. Magic Quotes will automatically escape incoming data, which is to say:

That's Steve's!

will be escaped to:

That\'s Steve\'s!

I will not go into any great detail as to the purpose behind Magic Quotes, but I will say the following:

  • If mysterious backslashes are appearing in your form data, Magic Quotes is the likely culprit
  • You should program with Magic Quotes turned off by configuring php.ini
    • Most shared hosting services have Magic Quotes turned on and you can not disable them. For these hosts you can write a function that undoes the effects of Magic Quotes (best viewed with CSS disabled):
  • Magic Quotes will be deprecated and removed from PHP 6
  • For more information: http://www.google.com/search?q=php+magic+quotes
Final Thoughts

Between this article and Part I, most readers should have a fundamental understanding of how to develop web forms. I've purposefully ignored the finer details of form handling, such as the different types of inputs or advanced validation techniques; I will cover those topics in the near future. However, you should feel confident that you could research those topics and use what you discover in the groundwork I've provided thus far.

Comments

Hi

Just want to thank you for very informative post, regards Ed

Nice snippets for form processing

Nice snippets for form processing.
Saved me a heap of time!
Works like a charm!

Post new comment

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