Advertisement
PHP

Simplify Form Handling in a Big Way

by

Save time, reduce maintenance pains, simplify your code, and do it all while feeling like a freakin' genius! In this tutorial, learn how to use variable variables, lookup arrays, and a bit of clever programming to simplify form handling in a big way.


Variable Variables, Methods, and Properties

Before we can get too deep into using a lookup array, it's important to first understand the concept behind variable variables.

What Are Variable Variables?

Variable variable is a term, which describes the use of a variable to declare another variable.

In the simplest form, a variable variable might look like:

$foo = 'A value!'; // Declare an initial value
$bar = 'foo'; // Wait, what's happening?
echo $$bar; // Holy crap! That output 'A value!'

Why Should you Care?

When you look at a proof of concept like the previous example, using variable variables probably looks pretty silly and overcomplicated. However, there really are solid, practical reasons to use them in some cases.

Lookup arrays can easily simplify your code

Practical Examples

The responsible use of variable variables can drastically reduce the amount of code that needs to be repeated by, say, converting an associative array to an object with sanitized values.

Example without variable variables

$comment = new stdClass(); // Create an object

$comment->name = sanitize_value($array['name']);
$comment->email = sanitize_values($array['email']);
$comment->url = sanitize_values($array['url']);
$comment->comment_text = sanitize_values($array['comment_text']);

Example with variable variables

$comment = new stdClass(); // Create a new object

foreach( $array as $key=>$val )
{
    $comment->$key = sanitize_values($val);
}

See how much simpler that was? And you can imagine what the example without variable variables would look like if the array had something like 50 or 100 values.

NOTE: I'm aware that you could also use array_map() and explicitly cast the array as an object to accomplish the same thing. That's not the point. We're illustrating a concept, here. Play along.


The Problem with Form Processing

Make form processing a breeze.

Now that you know how to use variable variables, we can move on to the meat and potatoes of this article, which is the idea that incorporating a lookup array instead of multiple controller files or a switch statement can save you a lot of extra maintenance, repeated code, and headache in general.

To illustrate our concept, we're going to use the idea of form processing. This is an essential aspect of web programming, and can also be one of the most tedious areas of any project.

However, after reevaluating your coding habits, you can potentially make form processing a breeze.

Frequently, either an individual file is created for each form to be processed, or a switch statement is used. In this section, we'll go over how both solutions might be implemented, and then we'll examine a solution using variable variables, and how it can improve your projects.

A Working Example with Multiple Form Processing Files

An often used method for handling form submissions is to create a whole new file to handle each form's data separately.

Take, for example, these three forms that update a user account with a new name, new email, or both:

<form action="assets/inc/ex1-form1.php"
      method="post"
      id="ex1-form1">
    <div>
        <h4>Form 1</h4>
        <label>Name
            <input type="text" name="name" class="input-text" />
        </label>
        <input type="submit" class="input-submit" value="Submit" />
        <input type="hidden" name="token" value="secret token goes here" />
    </div>
</form>

<form action="assets/inc/ex1-form2.php"
      method="post"
      id="ex1-form2">
    <div>
        <h4>Form 2</h4>
        <label>Email
            <input type="text" name="email" class="input-text" />
        </label>
        <input type="submit" class="input-submit" value="Submit" />
        <input type="hidden" name="token" value="secret token goes here" />
    </div>
</form>

<form action="assets/inc/ex1-form3.php"
      method="post"
      id="ex1-form3">
    <div>
        <h4>Form 3</h4>
        <label>Name
            <input type="text" name="name" class="input-text" />
        </label>
        <label>Email
            <input type="text" name="email" class="input-text" />
        </label>
        <input type="submit" class="input-submit" value="Submit" />
        <input type="hidden" name="token" value="secret token goes here" />
    </div>
</form>

Each of these forms points to a different processing file. So what does each of these files look like?

Processing form 1 (assets/inc/ex1-form1.php)

<?php

// Turn on error reporting so we can see if anything is going wrong
error_reporting(E_ALL);
ini_set('display_errors', 1);

// Make sure our faux-token was supplied
if( isset($_POST['token']) && $_POST['token']==='secret token goes here' )
{
    // Require the necessary class
    require_once 'class.copterlabs_account.inc.php';

    // Create a new instance of the class
    $account_obj = new CopterLabs_Account();

    // Handle the form submission
    $output = $account_obj->save_name();

    // For this example, just output some data about the form submission
    echo "<pre>Processing File: ", $_SERVER['PHP_SELF'],
            "\n\n<strong>Method Output:</strong>\n", $output, "</pre>\n",
            '<p><a href="../../">Go back</a></p>';
}
else
{
    die( 'Invalid form submission' );
}

Processing form 2 (assets/inc/ex1-form2.php)

<?php

// Turn on error reporting so we can see if anything is going wrong
error_reporting(E_ALL);
ini_set('display_errors', 1);

// Make sure our faux-token was supplied
if( isset($_POST['token']) && $_POST['token']==='secret token goes here' )
{
    // Require the necessary class
    require_once 'class.copterlabs_account.inc.php';

    // Create a new instance of the class
    $account_obj = new CopterLabs_Account();

    // Handle the form submission
    $output = $account_obj->save_email();

    // For this example, just output some data about the form submission
    echo "<pre>Processing File: ", $_SERVER['PHP_SELF'],
            "\n\n<strong>Method Output:</strong>\n", $output, "</pre>\n",
            '<p><a href="../../">Go back</a></p>';
}
else
{
    die( 'Invalid form submission' );
}

Processing form 3 (assets/inc/ex1-form3.php)

<?php

// Turn on error reporting so we can see if anything is going wrong
error_reporting(E_ALL);
ini_set('display_errors', 1);

// Make sure our faux-token was supplied
if( isset($_POST['token']) && $_POST['token']==='secret token goes here' )
{
    // Require the necessary class
    require_once 'class.copterlabs_account.inc.php';

    // Create a new instance of the class
    $account_obj = new CopterLabs_Account();

    // Handle the form submission
    $output = $account_obj->save_both();

    // For this example, just output some data about the form submission
    echo "<pre>Processing File: ", $_SERVER['PHP_SELF'],
            "\n\n<strong>Method Output:</strong>\n", $output, "</pre>\n",
            '<p><a href="../../">Go back</a></p>';
}
else
{
    die( 'Invalid form submission' );
}

As you can plainly see, the example files above are duplicating a ton of code. Expand this to 15 forms on a site, and you'll quickly find that maintenance could become a nightmare.

The Account Class

As you can see, the processing files are creating an instance of the class CopterLabs_Account. This will be a very simple class that outputs information about a form submission.

Here's the code for the class (assets/inc/class.coperlabs_account.inc.php):

<?php

/**
 * A simple class to test form submissions
 *
 * PHP version 5
 *
 * LICENSE: Dual licensed under the MIT or GPL licenses.
 *
 * @author    Jason Lengstorf <jason.lengstorf@copterlabs.com>
 * @copyright 2011 Copter Labs
 * @license   http://www.opensource.org/licenses/mit-license.html
 * @license   http://www.gnu.org/licenses/gpl-3.0.txt
 */
class CopterLabs_Account
{

    public $name = NULL,
           $email = NULL;

    public function save_name()
    {
        $this->name = htmlentities($_POST['name'], ENT_QUOTES);

        return "Method: " . __METHOD__ . "\nName: " . $this->name . "\n";
    }

    public function save_email()
    {
        $this->email = htmlentities($_POST['email'], ENT_QUOTES);

        return "Method: " . __METHOD__ . "\nEmail: " . $this->email . "\n";
    }

    public function save_both()
    {
        $this->name = htmlentities($_POST['name'], ENT_QUOTES);
        $this->email = htmlentities($_POST['email'], ENT_QUOTES);

        return "Method: " . __METHOD__ . "\nName: " . $this->name . "\nEmail: "
                . $this->email . "\n";
    }

}

You can try out this code at Example 1 on the demo page.

A Working Example with a Single Processing File and a Switch Statement

Another popular solution for form processing is to consolidate all the processing scripts into one file and determine what to do with the data using a switch statement.

The switch approach commonly employs a trick in which a hidden input is added to the form containing an action to be taken upon submission. This
action is then used to determine what to do with the form.

Here are the same three forms, from above, with actions added, all pointing to a single processing file:

<form action="assets/inc/ex2-switch.php"
      method="post"
      id="ex2-form1">
    <div>
        <h4>Form 1</h4>
        <label>Name
            <input type="text" name="name" class="input-text" />
        </label>
        <input type="submit" class="input-submit" value="Submit" />
        <input type="hidden" name="action" value="update-name" />
        <input type="hidden" name="token" value="secret token goes here" />
    </div>
</form>

<form action="assets/inc/ex2-switch.php"
      method="post"
      id="ex2-form2">
    <div>
        <h4>Form 2</h4>
        <label>Email
            <input type="text" name="email" class="input-text" />
        </label>
        <input type="submit" class="input-submit" value="Submit" />
        <input type="hidden" name="action" value="update-email" />
        <input type="hidden" name="token" value="secret token goes here" />
    </div>
</form>

<form action="assets/inc/ex2-switch.php"
      method="post"
      id="ex2-form3">
    <div>
        <h4>Form 3</h4>
        <label>Name
            <input type="text" name="name" class="input-text" />
        </label>
        <label>Email
            <input type="text" name="email" class="input-text" />
        </label>
        <input type="submit" class="input-submit" value="Submit" />
        <input type="hidden" name="action" value="update-both" />
        <input type="hidden" name="token" value="secret token goes here" />
    </div>
</form>

And the new processing file looks like: (assets/inc/ex2-switch.php)

<?php

// Turn on error reporting so we can see if anything is going wrong
error_reporting(E_ALL);
ini_set('display_errors', 1);

// Make sure our faux-token was supplied
if( isset($_POST['token']) && $_POST['token']==='secret token goes here' )
{
    // Require the necessary class
    require_once 'class.copterlabs_account.inc.php';

    // Create a new instance of the class
    $account_obj = new CopterLabs_Account();

    // Sanitize the action
    $action = htmlentities($_POST['action'], ENT_QUOTES);

    // Use the new 'action' hidden input to determine what action to call
    switch( $action )
    {
        // Form 1 handling
        case 'update-name':
            $output = $account_obj->save_name();
            break;

        // Form 2 handling
        case 'update-email':
            $output = $account_obj->save_email();
            break;

        // Form 3 handling
        case 'update-both':
            $output = $account_obj->save_both();
            break;

        // If no valid action is found, something isn't right
        default:
            die( 'Unsupported action.' );
    }

    // For this example, just output some data about the form submission
    echo "<pre>Processing File: ", $_SERVER['PHP_SELF'],
            "\nAction: ", htmlentities($_POST['action'], ENT_QUOTES),
            "\n\n<strong>Method Output:</strong>\n", $output, "</pre>\n",
            '<p><a href="../../#ex2">Go back to example 2</a></p>';
}
else
{
    die( 'Invalid form submission' );
}

You can see this in action by visiting Example 2 on the demo page. This is a marked improvement over using multiple forms, but you can see that we're still duplicating some code.

On top of that, it's a personal preference of mine to avoid switch statements whenever I can. This is due to the fact that switch uses loose comparisons ('a string' will trigger case 0 because a string evaluates to 0 if you convert it to an integer) and is extremely easy to turn into spaghetti code.


Fixing the Problem: Lookup Arrays and Variable Variables

As we've seen so far, both of the above solutions have their drawbacks, and require duplicate code. Imagine if there were a dozen or more forms on the site — not pretty.

To address this issue, we can use a concept called a lookup array, which maps the actions passed from the form to a method called on the object.

Yes, you could set the action as the method name, but that allows a bogus form submission to call any public method. Making the array a key-value pair is a small step to add a little more control without much extra work.

A Working Example with a Single Processing File and a Lookup Array

Using our knowledge of variable variables from the beginning of this tutorial, let's modify our demo to use a lookup array.

Modify the three forms to point to a new controller file:

<form action="assets/inc/ex3-lookup-array.php"
      method="post"
      id="ex3-form1">
    <div>
        <h4>Form 1</h4>
        <label>Name
            <input type="text" name="name" class="input-text" />
        </label>
        <input type="submit" class="input-submit" value="Submit" />
        <input type="hidden" name="action" value="update-name" />
        <input type="hidden" name="token" value="secret token goes here" />
    </div>
</form>

<form action="assets/inc/ex3-lookup-array.php"
      method="post"
      id="ex3-form2">
    <div>
        <h4>Form 2</h4>
        <label>Email
            <input type="text" name="email" class="input-text" />
        </label>
        <input type="submit" class="input-submit" value="Submit" />
        <input type="hidden" name="action" value="update-email" />
        <input type="hidden" name="token" value="secret token goes here" />
    </div>
</form>

<form action="assets/inc/ex3-lookup-array.php"
      method="post"
      id="ex3-form3">
    <div>
        <h4>Form 3</h4>
        <label>Name
            <input type="text" name="name" class="input-text" />
        </label>
        <label>Email
            <input type="text" name="email" class="input-text" />
        </label>
        <input type="submit" class="input-submit" value="Submit" />
        <input type="hidden" name="action" value="update-both" />
        <input type="hidden" name="token" value="secret token goes here" />
    </div>
</form>

Next, put together the processing file that will handle form submissions (assets/inc/ex3-lookup-array.php):

<?php

// Turn on error reporting so we can see if anything is going wrong
error_reporting(E_ALL);
ini_set('display_errors', 1);

// Make sure our faux-token was supplied
if( isset($_POST['token']) && $_POST['token']==='secret token goes here' )
{
    // Require the necessary class
    require_once 'class.copterlabs_account.inc.php';

    // Create a new instance of the class
    $account_obj = new CopterLabs_Account();

    // Sanitize the action
    $action = htmlentities($_POST['action'], ENT_QUOTES);

    // Set up a lookup array to match actions to method names
    $lookup_array = array(
        'update-name' => 'save_name',
        'update-email' => 'save_email',
        'update-both' => 'save_both'
    );

    // Make sure the array key exists
    if( array_key_exists($action, $lookup_array) )
    {
        // Using variable variables, call the proper method and store the output
        $output = $account_obj->$lookup_array[$action]();
    }
    else
    {
        die( 'Unsupported action.' );
    }

    // For this example, just output some data about the form submission
    echo "<pre>Processing File: ", $_SERVER['PHP_SELF'],
            "\nAction: ", htmlentities($_POST['action'], ENT_QUOTES),
            "\n\n<strong>Method Output:</strong>\n", $output, "</pre>\n",
            '<p><a href="../../#ex3">Go back to example 3</a></p>';
}
else
{
    die( 'Invalid form submission' );
}

Check this out on the demo page by trying out the forms on Example 3.

Since we set the action as the key in the array, we use array_key_exists() to ensure that the action is valid. Then, we use the value that corresponds to the action as the method name. Notice that we added the parentheses after the value to make sure it's executed as a method.

The addition of the lookup array keeps the code concise, simple, and clear (once you get the hang of variable variables).


Summary

Used responsibly, lookup arrays can make your scripts far easier to update and maintain when you combine them with variable variables.

How do you think you can integrate lookup arrays and variable variables into your projects to make maintenance easier? Let me know in the comments!

Related Posts
  • Code
    Theme Development
    Custom Controls in the Theme CustomizerTheme customizer custom control 400
    In the last article, we explored the advanced controls available in the Theme Customizer, and how to implement them. We’re going to look at how to create our own custom control, allowing you to choose which Category of Posts are displayed on the home page. To get started, download version 0.6.0 of our Theme Customizer Example.Read More…
  • Code
    PHP
    Validation and Exception Handling: From the UI to the BackendProcedural to oop php retina preview
    Sooner or later in your programming career you will be faced with the dilemma of validation and exception handling. This was the case with me and my team also. A couple or so years ago we reached a point when we had to take architectural actions to accommodate all the exceptional cases our quite large software project needed to handle. Below is a list of practices we came to value and apply when it comes to validation and exception handling.Read More…
  • Code
    PHP
    Acceptance Testing With CodeceptionIntro to codeception retina preview
    Typically new features for web applications are tested by visiting the appropriate page in a browser, maybe filling out some form data, submitting the form, and then developers or testers hope to see their desired result. This is the natural way most web developers test their apps. We can continue with this natural testing process and improve upon it to ensure our apps are as stable as possible by using Codeception. Read More…
  • Code
    Plugins
    Using HighCharts in WP-AdminHighcharts 400
    Charts are a great way to present data. They make data more digestible by making it visually appealing. In WordPress, there is no built-in method for getting posts and pages data in a graphical form. Although, there are certain plugins available which integrate Google Analytics with WordPress, but they are overkill if you want to get only a portion of that data. Also, nothing should keep you from learning new techniques and to dive straight into the subject is the best way to learn.Read More…
  • Code
    PHP
    Authentication With Laravel 4Laravel 4 auth retina preview
    Authentication is required for virtually any type of web application. In this tutorial, I'd like to show you how you can go about creating a small authentication application using Laravel 4. We'll start from the very beginning by creating our Laravel app using composer, creating the database, loading in the Twitter Bootstrap, creating a main layout, registering users, logging in and out, and protecting routes using filters. We've got a lot of code to cover, so let's get started!Read More…
  • Web Design
    UX
    Implementing the Float Label Form PatternForm float input retina
    Using Matt Smith’s mobile form interaction design as a guide, we will create a stunning form interaction for the web that’s both beautiful and accessible using HTML, CSS and JavaScript.Read More…