The beginning stages of a web-based form are very simple: you read the HTML (HyperText Markup Language) guide, learn about select menus, options, form labels, radio inputs and all the myriad ways you can inveigle information from your visitors. You also learn about the action and method attributes. Great. So all of this HTML you’ve learned is wonderful – but won’t actually do anything. You need to provide some kind of back-end rules to actually make any use of this information.

Deciding how to use the Data

First things first, you have to decide what you want to do with the data. I’m only going to deal with the most common probable tasks, here – no image editing or complex database updates, just simple data management. Are you going to write your data to a database? Do you want to use it to create a file which will hold a record of the transaction? Do you want it to send an email?

For the sake of each example, I’m going to imagine we’re creating a survey. The form needs to submit information which will be inserted into a MySQL database which, if you choose, can be analyzed in various ways using other scripts later. This is just about getting the data into the database. We’ll start, therefore, by describing the form you’ll be using to collect information.

Let’s imagine a survey collecting four pieces of information:

  1. A reason for contacting you

    The reason for contact will have four options, but only one will be allowable as a choice. This will use radio button inputs.

  2. A list of services the contacting person might be interested in.

    In this case, the person will be able to select several options, so we’ll be using checkboxes.

  3. Survey taker’s state of residence.

    This will be a drop down. Only one can be chosen, but there are far too many choices for a radio input to make any sense.

  4. Comments from the survey taker.

    Could be anything – so this will have to be a text field.

The code for your form will look something like this:

<form method="post" action="">
<p>
<fieldset>
<legend>Why are you contacting us?</legend>
<div>
<input type="radio" name="q1_why" id="q1a">
<label for="q1a">Technical Support</label><br />
<input type="radio" name="q1_why" id="q1b">
<label for="q1b">Applying for Work</label><br />
<input type="radio" name="q1_why" id="q1c">
<label for="q1c">Wanted to Hire You</label><br />
<input type="radio" name="q1_why" id="q1d">
<label for="q1d">Other</label><br />
</div>
</fieldset>
<fieldset>
<legend>What service are you interested in? (Check all that apply.)</legend>
<div>
<input type="checkbox" name="q2_service[]" id="q2a" value="Heating" />
<label for="q2a">Heating</label><br />
<input type="checkbox" name="q2_service[]" id="q2b" value="Cooling" />
<label for="q2b">Cooling</label><br />
<input type="checkbox" name="q2_service[]" id="q2c" value="Plumbing" />
<label for="q2c">Plumbing</label><br />
<input type="checkbox" name="q2_service[]" id="q2d" value="Wiring" />
<label for="q2d">Wiring</label><br />
<input type="checkbox" name="q2_service[]" id="q2e" value="Carpentry" />
<label for="q2e">Carpentry</label>
</div>
</fieldset>
<fieldset>
<legend>Where are you from?</legend>
<div>
<label for="q3">Pick your state:</label>
<select id="q3" name="q3_state">
<option value="none">Choose One</option>

<option value="MN">Minnesota</option>
<option value="MT">Montana</option>
<option value="NY">New York</option>
</select>
</div>
</fieldset>
<fieldset>
<legend>Comments</legend>
<div>
<label for="q4">Please provide additional comments about our services.</label><br />
<textarea id="q4" name="q4_comments" rows="4" cols="40"></textarea>
</div>
</fieldset>
<fieldset>
<div>
<label for="submit">Submit the form</label>
<input type="submit" id="submit" name="submit" value="Send your Input" />
</div>
</fieldset>
</form>

This form will be the centerpiece of our example form processing script.

Creating your Form and your Script

Regardless of your chosen method of using data, you’ll need to have a script which collects the information from the form. This can be the same for each processing choice — first gather all data, then build the final element which either sends your email, inputs data to your database, or adds information to your data file.

What happens if you leave the action attribute blank in a form?

The form will submit to itself – that is, the page which holds the form will be used to process the form information. In PHP (Hypertext PreProcessing), this simply requires you to add your form processing code to that page. This is a workable way of handling form data for a simple isolated form, but leaving the action attribute empty is not valid under the HTML 4.01 or XHTML (eXtensible HyperText Markup Language - HTML reformulated as XML (eXtensible Markup Language)) 1.0 doctypes. To have a page process itself, it’s preferable to use the PHP global variable $_SERVER['PHP_SELF'], which will supply the current file name.

You don’t, however, want to use this global variable “in the raw.” To make it a bit safer, you’ll want to do something more like htmlspecialchars( $_SERVER['PHP_SELF'] , ENT_QUOTES ), which will strip out some potentially harmful attacks. It’s not infallible; but it’s better than using PHP_SELF by itself!

There are two general types of form submission methods – POST and GET. Both are useful, but in this case I’ll only be dealing with POST data. If you want to learn more about the differences between the GET and POST methods, you might want to read an article describing the difference at length. You’ll notice that the code above has had the method attribute set to POST. The other important attribute for forms, action has been left blank for now.

In the course of this article, we’re going to build a script to process this example form – our script will be called process.php. For simplicity, we’ll contain the entire script in a single page. The form itself and all the processing commands will be contained in one page.

The First Pass: Retrieving the Variables

Opening the script:


We’ve established the outermost boundaries of our data processing. This snippet initiates PHP, establishes a simple if logical query which checks whether the form has been submitted. The data following the double forward slash? Just a comment. Anything on the same line as a double slash is a comment – PHP ignores it.

One of the most important things to consider when retrieving data from a publicly available web form is whether you can trust the data. And the most important thing to realize when you’re considering this is that you can never trust public input. Never. Part of having a form online is the acknowledgement that whether through malice, incompetence, or accident, there will always be the possibility that your form will be used to send data other than what you intended. You should take precautions, therefore, to check every piece of information that comes your way.

Text fields are the easiest fields to retrieve, but can be tricky to examine. For more information on PHP security, I highly recommend reading Dave Child’s “Writing Secure PHP” or ordering a book specifically on the subject, such as Pro PHP Security by Chris Snyder and Michael Southwell. I’ll touch lightly on the subject; but don’t count on this script for any serious security.

So what needs to go inside that commented space? We’ll need to collect the information which was placed in the form, check and make sure it’s the type of information we were looking for, and then we’ll assign each piece of data to a variable which we can use to format our final result.

$q1_why = $_POST['q1_why'];
$q2_service = $_POST['q2_service'];
$q3_state = $_POST['q3_state'];
$q4_comments = $_POST['q4_comments'];

Pretty straightforward – we’re taking the raw information from the form submission and assigning them to variables. Every piece of data sent from a form using the POST method will be stuck into PHP’s $_POST array variable, retrievable using the name attribute of the form input. You’ll notice one thing here which doesn’t seem to quite match – the name attribute for question 2 was actually “q2_service[].” What do the square brackets mean? They mean that this input, when submitted, will be placed in a variable of type array as well. This is what allows you to retrieve multiple answers from this question – everything checked off is now associated with the $q3_service array variable.

$error = FALSE;
if ($q3_state == "none") {
    $error = TRUE;
}
if (isset($q4_comments)) {
    $q4_comments = trim($q4_comments);
    $q4_comments = strip_tags($q4_comments);
}
if ( isset($q1_why) && isset($q2_service) && isset($q3_state) && isset($q4_comments) && $error == FALSE ) {
  $process = TRUE;
} else {
  $process = FALSE;
}

Slightly more complicated. I’ve elected to skip examining the data for questions 1 and 2, because they are defined within the form. There’s relatively little chance that they’re being faked. Nonetheless, in a production script you may well want to inspect them. I am going to check questions 3 and 4, however. Question 3 could be a problem because the default option is “None.” In this script, we’re going to insist that our visitors be from one of these three states. In a real situation, you may want to allow for some other possibility – but not today. We’re checking to see whether our visitor has failed to pick their state. If so, we’ll set the variable $error equal to the boolean value “TRUE,” which we’ll use later to trigger an error condition.

With question 4, we don’t really care what the content is – but we don’t want any extra spaces and we REALLY don’t want anybody submitting HTML formatted text. So we’re using the trim() function to strip white space and the strip_tags() function to remove HTML code.

Question 2 may have multiple parts. If the user can submit multiple choices, there’s always a possibility that these multiple parts will need to be dealt with – we can’t just send the array around directly. We’ll need to turn this array of answers into specific parts.

while ( (list($key,$val) = each($q2_service)) ) {
  $q2_services .= "[" . $val . "]";
}

This code loops through the array of services selected and creates a string variable with each element contained in square brackets. This doesn’t serve any programmatic purpose except to make it easy to distinguish one element from another when viewing your final results.

Finally, we string everything together – make sure every variable has been set and that $error is FALSE, indicating that no error has occurred. If everything’s OK, we’ll set the value of $process to TRUE and we’ll move on to actually do something with this information.

Writing Form Data to a MySQL Database

The first task you’ll need to accomplish is to create your database. This won’t necessarily be available on every hosting solution. If you’re using one of the major hosting services, such as GoDaddy, you can always refer to their extensive documentation on databases. One of your first steps should always be going to your host’s help pages – any luck, and they’ll provide documentation like this which may answer a number of your questions on setting up a database. Regardless of your hosting service, however, if you use MySQL you will have to deal with the language which defines and interacts with the database – SQL (Structured Query Language (a database standard)), or Structured Query Language.

It’s well beyond the scope of this article to try and articulate the complete nature of MySQL or Structured Query Language. To get started learning SQL, you may want to give a look to the articles available from the W3Schools’ SQL Tutorial.

However, the minimum you’ll need will be a single database table with five fields. I’m actually going to go to six, because I like to attach a timestamp to every form submission – this information can really make your data a lot more useful.

CREATE TABLE survey (
  sid INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
q1_why VARCHAR(127) NOT NULL, 
  q2_service TEXT NOT NULL, 
  q3_state VARCHAR(2) NOT NULL, 
  q4_comments TEXT NOT NULL, 
  time_stamp TIMESTAMP NOT NULL) 
  TYPE = MYISAM;

This SQL code will create your basic MySQL table with six fields. It’ll contain an automatically incremented unique ID, create space for your fields, and keep track of when the submission occurred. For more explanations of databases, try the MySQL home page or consider reading a complete guide to MySQL such as MySQL™: The Complete Reference, by Vikram Vaswani.

Creating the database is somewhat outside the range of what I’m trying to cover, however. Hopefully the references provided will help you out – but for now, we’re going to have to deal with getting information into the database. This, of course, will require a completely different, though related, SQL query.

define ('DB_USER', 'yourusername');
define ('DB_PASSWORD', 'yourpassword');
define ('DB_HOST', 'localhost');
define ('DB_NAME', 'your_database');
$dbc = @mysql_connect (DB_HOST, DB_USER, DB_PASSWORD) or die('Failure: ' . mysql_error() );
mysql_select_db(DB_NAME) or die ('Could not select database: ' . mysql_error() );

Before you can start adding information to a database, you need to attach your script to the database. In order for it to do that, you need to supply that script with your username, your password, and the name of your database. Now, obviously, this is a security issue. You should never make this information easily accessible – if possible, place this information in a separate file with a difficult to guess filename, somewhere below your website’s root directory. You can then include your database connection:

include ("../mydb_connect1597.php");

In this case, the connection file has been included one directory below the active script. Having connected to your database, you can move on and insert your data.

$query = "INSERT INTO survey VALUES ('','$q1_why','$q2_services','$q3_state','$q4_comments','now()')";
$q = mysql_query($query);

That’s all the PHP code required to insert the information from your form into a database. Now, you probably want to check that the data was correctly inserted, as well.

if (!$q) {
  exit("<p>MySQL Insertion failure.</p>");
} else {
  mysql_close();
  //for testing only
  //echo "<p>MySQL Insertion Successful</p>";
}

This code will simply check whether the mysql_query was performed successfully, and notify you of the results. It also closes the database connection, since you no longer need it in this instance of the script.

That’s something like the bare minimum needed to process a form and insert the data into a MySQL database – there are scads of issues not dealt with, including many database security issues and error handling. But that’s a question for a later article.

Placing your data in a file

As long as you know the correct data format, you can create just about any type of file using PHP. The easiest, of course, are .txt and .csv files. Comma separated values files tend to be a bit more useful for this kind of thing, since you can then open the file in a program such as Microsoft Excel and be able to sort the information. Although it’s possible to create the file from the beginning with PHP, it’s much easier to simply create the file in advance. You can create a .csv format file in any text editor – simply open a blank document and save it as “survey.csv” – the quotes will be required if you’re using Microsoft’s Notepad.

Servers are protective of files. Naturally, they don’t want to leave every file just sitting there waiting for anybody to edit it. In order to prevent this, files have permissions settings. Each file has permissions set for three users: the owner, the group, and the WORLD – which is to say, everybody. Each user is granted specific permissions for three actions: read, write, and execute. Most of the time, you want everybody to be able to read your documents, but only want yourself to be able to write them. For scripts and files which will be recording data, however, you need to be a little more trusting. For more on file permissions, read “ Understanding file permissions on Unix: a brief tutorial.”

Permissions are represented most commonly in 9 character strings. Each set of three characters represents one user, and each character within that user represents one behavior. For example, the permissions set rwxrw-r-x gives the owner of the file permission to read it, write it, and execute it. The group can read and write, and the world can read and execute the file.

You need to make certain that your .csv file has permissions set which will allow your script to read it and write it. The script probably runs with group permissions, but in some servers it may require world-wide permissions – if PHP is running under a different group than the owner of the file.

The code:

$csv_file = 'survey.csv';
if (is_writable($csv_file)) {
  if (!$csv_handle = fopen($csv_file,'a')) {
    // this line is for troubleshooting
    // echo "<p>Cannot open file $csv_file</p>";
    exit;
  }
}

You’ve defined a file pointer which can find the comma separated values file you created. You’re checking it to see whether it’s readable and, while you’re testing, you’re providing an error message to tell you what’s going wrong.

About Error Messages

You don’t want to provide technical error messages in a production setting – when you’re live, you need to focus on telling your users what they need to know and nothing more. Giving away technical information is an invitation to hackers and means nothing to the average user.

At this point, you’ve also opened the file for editing using the function fopen. This function accepts two arguments: the first has to be a file pointer. The second is a code which represents the manner in which you’re opening the file – you need to tell the script whether you want to overwrite the existing content of the file, whether you want to add information at the beginning of the file or the end, or whether you don’t intend to write to the file at all.

The PHP online manual has a complete list of this codes at the fopen function page. For now, however, just know that a means “Open for writing only; place the file pointer at the end of the file. If the file does not exist, attempt to create it.” We’re not interested in reading the information and we don’t want to overwrite anything that’s already there, so it’s to the end of the file we go.

$csv_item = "\"$q1_why\",\"$q2_services\",\"$q3_state\",\"$q4_comments\"\n";
if (is_writable($csv_file)) {
  if (fwrite($csv_handle, $csv_item) === FALSE) {
    //for testing
    //echo "Cannot write to file";
    exit; 
  } 
}
fclose($csv_handle);

This script creates a text string containing the four variables, checks to see whether the file is writable, and writes the string into the file. It then closes the file by releasing the file handler you’ve created and exits the script. The information has been saved to your file for later reading.

Sending your Information by Email

In some ways, this is the easiest method. It will simply stick the information into an email message and send it to you. What it doesn’t do is keep a running list of the uses of the form – if you wanted to use this for a survey, for example, you’d be manually entering the information from each survey submission into your chosen method of data tracking.

There are four basic elements required to send mail with PHP. The entire process is handled by a function called, predictably enough, mail(). This function needs four arguments:

  1. The destination address, or recipient.
  2. The message subject.
  3. The message content, or body.
  4. Mail header information.

Technically speaking, the mail headers are optional information. However, I think they’re important and valuable so we’re going to go ahead and pretend that they’re required.

//first, define your four mail function fields
$recipient = "you@yourdomain.com";
$subject = "Online Survey from Your Domain";
$content = "Online Survey:\n
Why are you contacting us? $q1_why\n
What services are you interested in? $q2_services\n
Where are you from? $q3_state\n
Your Comments:\n
$q4_comments\n";
$header = "From: YourSurvey \n"."Reply-To: survey@domain.com\n";

This is pretty straightforward – each field is a basic text string. To make it easier to read and understand, I’m writing the questions asked into the message before the variable with the answers. I’m also making use of \n – this code creates a new line in the message, and will make the results much easier to process. (It has the same effect above in the CSV files section.)

You can name the sending address whatever you’d like, but I’d recommend using a name which will make it easy to associate the messages sent with this particular script.

Having assigned these variables, all that’s left is to send the message:

mail($recipient, $subject, $content, $header);
exit;

And you’re done. You’ve processed your data.

Conclusion

In conclusion, this is a very sparse way of addressing the complicated issue of letting people use your site interactively. It hardly touches on any of the major issues of usability or security, such as protecting your scripts from use by spammers; preventing hackers from accessing your databases; returning user-friendly error messages; or any higher level data processing. But it’s intended to be basic – this is just an introduction. Once you’ve gotten your feet wet, you should really look into a solid textbook which will cover some of the more difficult issues.

Recommended Reading

I know that I’ve mentioned that Pro PHP Security book already — but I can’t emphasize the importance of security enough. By putting a form on your site and inviting any kind of input from your visitors, you’re creating a doorway into your website. Most of the time you’ll be fine, even if you haven’t taken the precautions you should — but it’s not worth the risk if you have the opportunity to protect yourself.