In Part 1, we ran over some basics of PHP programming, and created a log file directory. Let's now build a page to gather the inquiree's contact data and get an idea what s/he wants to know about. Here's what we want the form to look like:
The opening of our form page is pretty straightforward HTML, just a Document Type Declaration, the header stuff, title and some CSS to define our look. You can adjust background and text colors easily. The "maincontent" specification sets up about 10% margins on either side of a centered form:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Contact Us</title>
<style type="text/css"><!--
body,p,td,li {
font-family: arial,helvetica,sans-serif;
font-size: .85em;
color: #000;
background: #eee;
}
h1 {
font: 1.5em/1.2 arial,helvetica,sans-serif;
font-weight: 700;
color: #fc0;
}
#maincontent {
width: 80%;
margin-left: auto;
margin-right: auto;
}
-->
</style>
Validate the form data - When the form is submitted, we want to check a few entries, such as that the email is a valid U.S. or Euro-style construct, the visitor has supplied a name, and so on. What's neat about this script is that it shows you all your errors at once in the alert:
<script type="text/javascript"><!--
function verify(form)
{
var msg = "";
var err = 0;
var j = new RegExp();
var k = new RegExp();
if (form["name"].value == '')
{
msg += "You forgot to enter your name.\n";
err++;
}
Notice how we check to see if the visitor's name was omitted, and then concantenate some warning text to the "msg" string variable which will be "alerted" later. (The JavaScript operator "+=" means "append to what's there already". In PHP it is written ".=") The "\n" is a newline character.
Continuing...
if (!form["need"].value)
{
msg += "Please select something to contact you about.\n";
err++;
}
In Javascript, if a variable is null (""), testing it returns a "not" state. So "!" means "not". Translating to English, "if the value of form input element 'need' is not something" then give a warning.
Continuing again...
if (!form["biz"].value)
{
msg += "Please select your business type.\n";
err++;
}
if (form["email"].value != '')
{
// allow Euro-style and subdomain addresses
j.compile(/^[A-Za-z0-9._-]+@[A-Za-z0-9._-]+\.[A-Za-z0-9]{2,3}$/);
if (!j.test(form["email"].value))
{
msg += "Your email address is invalid.\n";
err++;
}
}
// must have an email if notify was "email"
if ((form["notify"].value == "email") && (form["email"].value == ''))
{
msg += "Please enter an email address.\n";
err++;
}
// must be a valid U.S. phone number, if present
if (form["phone"].value != '')
{
k.compile(/^[0-9]{3}\-[0-9]{3}\-[0-9]{4}$/);
if (!k.test(form["phone"].value))
{
msg += "Phone # must be aaa-ppp-nnnn.\n";
err++;
}
}
if ((form["notify"].value == "phone") && (form["phone"].value == ''))
{
msg += "Please enter a phone number.\n";
err++;
}
if (err > 0)
{
msg = "_______________________________\n" + msg;
msg = "Please correct the following form errors:\n\n" + msg;
alert(msg);
return false;
}
return true;
}
//-->
</script>
</head>
Notice that if the requestor signified "Notify by email", his email is tested, and if "...by phone", the phone number becomes a required entry. Both of the option list entries are required, too. If all the checks result "true", the form is submitted according to the "action=" attribute in the form tag (more on that later).
When the visitor hits the "Submit" button, we will have the form action re-load this page; this next code will detect that we've submitted, and send the form data to MySQL.
Lastly, at the end of the script, we check to see if there were any warnings added to the "msg" variable. If so, we show the browser "alert" and return false -- preventing the form action from being executed until the visitor gets everything right. The form action would have been to re-load this page and passing the form variables in the PHP "$_POST" variables array. If there were no warnings, we return true, allowing the form action to be executed.
Capturing form variables - To pass a form variable to a PHP page, you name the form element that contains the data you want to pass. For example, in this case our form has a button named "submit". When the form action attribute reloads the page, the $_POST['submit'] variable contains the button's value; in this case, the value is "Submit", which is also the title of the button. Other $_POST variables contain the visitors supplied information. "$_POST['comments']", for instance, contains what s/he entered in the form input textarea named "comments".
We will check for the presence of $_POST['submit'], and also check that the visitor's email is using a registered mail server before proceeding with form processing. If the email is invalid, the user just gets the form back.
<?php
if(isset($_POST['email']))
list($userName, $mailDomain) = split("@", $_POST['email']);
if($userName == "anonymous")
exit;
if ($_POST['submit'] != "" and checkdnsrr($mailDomain, "MX"))
{
extract($_POST);
(Note: Some servers do not allow users to do DNS checking; if your contact form fails to work, try removing the "and checkdnsrr($mailDomain, "MX") clause from the line above.)
Now we construct the body of an email message out of the form data, prefixed by a date/time stamp. First we do some "spamming" detection; if many submitted items have the same data, it is likely a spam attempt. Next, we "sanitize" each submitted item by getting rid of extra returns and newlines, another method of either spamming or injecting addresses to bad sites. We remove any embedded HTML and then string all the items into a mail message format:
if($_POST['name'] == $_POST['email'] or $_POST['email'] == $_POST['comments']
or preg_match("/http:/", $_POST['comments']))
die("<h1>WARNING!</h1>\n<p>Spam attempt detected [1]</p>");
// function to prevent injection hacking
function sanitize($content)
{
return str_replace(array('\r', '\n'), "", $content);
}
// process form - start with a local date and time
$body = "\n" . date("Y.m.d H:i") . "\n";
// gather form data
$form_fields = array_keys($_POST);
// build up the body message from form entries
for ($i=0; $i<sizeof($form_fields); $i++)
{
$thisField = $form_fields[$i];
if ($thisField != "submit")
{
$thisValue = $_POST[$thisField];
if($thisValue != "" and $thisValue == $lastValue)
die("<h1>WARNING!</h1>\n<p>Spam attempt detected [2]</p>");
$lastValue = $thisValue;
if (is_array($thisValue))
{
for ($j=0; $j<sizeof($thisValue); $j++)
{
sanitize($thisValue[$j]);
$thisValue[$j] = ereg_replace("<.+>", "", $thisValue[$j]); // strip HTML
$body .= ucfirst($thisField) .": ". $thisValue[$j];
}
} else {
sanitize($thisValue);
$thisValue = ereg_replace("<.+>", "", $thisValue); // strip HTML
$body .= ucfirst($thisField) .": ". $thisValue ;
}
$body .= "\n";
}
}
Mail the data -
In the code block above, we do some filtering to prevent a large number of types of contact form-raiding robots from using your form to email spam. The first is the "anonymous" email account, a fairly common one used by novice spammers. The rest primarily use a method known as "injection" to tack on a list of emails separated by returns and new lines. If we get rid of all the returns and newlines, the entire email address becomes invalid.
We also use PHP's "ereg_replace()" function to replace all code between and including the < and > symbols; in other words, we strip out HTML tags from the comments.
Notice we print the names of the form variables ("$thisField") to make the email more readable. We also ignore the "submit" button, since it is not really relevent information.
All that remains is to mail the message. If mailing is successful, thank the submitter, if not, let them know there was an error:
// send ourself the message
$to = "You <you@your.isp>";
$subject = "Contact Form Submitted";
if (mail($to, $subject, $body))
{
echo "<h1>Thank you!</h1>\n
<p>Your contact submission has reached our server.<br />
You will hear from us shortly.</p>\n";
} else {
echo "<h1>Oops!</h1>\n
<p>We're sorry, there was an error mailing your information.<br />
Please use other means to contact us.</p>\n";
}
And finally, save the form entries in a file so if the mail gets lost, we still have a record of the submission. You can make a habit of checking the log file every day:
// append to a log file too, in case mail fails
if (!$LOG = fopen("form_logs/contactlog.txt","a"))
{ die($php_errormsg); }
if (-1 == fwrite($LOG, $body))
{ die($php_errormsg); }
fclose($LOG) or die($php_errormsg);
} else {
?>
Notice the "} else {"? The first time the page is accessed, the form was NOT submitted (no $_POST['submit'] exists) so we want to display the form for visitor input.
Now for the form. Our form will be submitted using the "POST" method to hide the data from the browser user. This form takes the submitters name as one string; if you wish, you can easily break it up into two parts, firstname - lastname. The form gives examples of choices using selection/option tags as well as a final textarea field for the inquiree to free-form special needs.
This code construction shows an appropriate use for the HTML <table> tag to neatly present the easy-to-read form. Putting it inside a <div> container permits us to position and style it with CSS:
<!-- form table: each row has 2 cells- label,element -->
<div id="maincontent">
<form name="form_one" method="post"
action="<?php echo $_SERVER['PHP_SELF']; ?>"
onsubmit="return verify(this);">
Notice the "action" attribute of the form tag; we use a chunk of PHP to reload this page ("$_SERVER['PHP_SELF']" is a global PHP variable containing the URL of this page). The "action" form attribute is not executed, however, until after the JavaScript "verify()" function returns a "true" status (all the form entries passed our checks).
<table border="0" cellpadding="2" cellspacing="0">
<tr>
<td colspan="2" align="left">
Respond by:
<input type="radio"
name="notify"
value="e-mail"
onclick="notify.value='email'"
checked="checked" />E-mail
<input type="radio"
name="notify"
value="phone"
onclick="notify.value='phone'" />Phone
<br />
<br />
<p class="req">(Required entries)</p>
</td>
</tr>
<tr>
<td align="right" width="10">Honorific:</td>
<td>
<input type="radio" name="honorific" value="Mr" onclick="honorific.value='Mr.'" />Mr.
<input type="radio" name="honorific" value="Ms" onclick="honorific.value='Ms.'" />Ms.
<input type="radio" name="honorific" value="Mrs" onclick="honorific.value='Mrs'" />Mrs.
<input type="radio" name="honorific" value="Miss" onclick="honorific.value='Miss'" />Miss
<input type="radio" name="honorific" value="Dr" onclick="honorific.value='Dr.'" />Dr.
</td>
</tr>
<tr>
<td align="right">
<span class="req">Name:</span></td>
<td align="left"><input type="text" name="name" size="30" /></td>
</tr>
<tr>
<td align="right">Title:</td>
<td align="left"><input type="text" name="title" size="30" /></td>
</tr>
<tr>
<td align="right">Organization:</td>
<td align="left"><input type="text" name="org" size="30" /></td>
</tr>
<tr>
<td align="right"><span class="req">E-mail:</span></td>
<td align="left"><input type="text" name="email" size="30" /></td>
</tr>
<tr>
<td align="right">Phone:</td>
<td align="left"><input type="text" name="phone" size="12" /></td>
</tr>
<tr>
<td align="right">Fax:</td>
<td align="left"><input type="text" name="fax" size="12" /></td>
</tr>
<tr>
<td align="right"><span class="req">My business is: </span></td>
<td align="left">
<select name="biz">
<option selected="selected" value="">Business type?</option>
<option value="real">Real Estate</option>
<option value="law">Law</option>
<option value="medical">Medical</option>
<option value="dental">Dental</option>
<option value="veterinary">Veterinary</option>
<option value="accounting">Accounting</option>
<option value="academic">Academic</option>
<option value="retail">Retail</option>
<option value="personal">Personal</option>
<option value="consultory">Consulting</option>
<option value="other">Other</option>
</select>
</td>
</tr>
<tr>
<td align="right"><span class="req">Contact me<br />about: </span></td>
<td align="left">
<select name="need">
<option selected="selected" value="">Choose...</option>
<option value="moweb">Make-over Web site</option>
<option value="moecom">Make-over E-com site</option>
<option value="newweb">New Web site</option>
<option value="newecom">New E-com Web site</option>
<option value="webtrain">Web design training</option>
<option value="manuals">Designing my manuals</option>
<option value="other">Other</option>
</select>
</td>
</tr>
<tr>
<td align="right">Comments or <br />special needs: </td>
<td>
<textarea name="comments" rows="5" cols="30" maxlength="512"></textarea>
</td>
</tr>
<tr>
<td> </td>
<td align="left"><br />
<font size="2">
<input name="submit" type="submit" value="Submit" />
<input type="reset" value="Clear" />
</font>
</td>
</tr>
</table>
</form>
</div>
<!-- end of form table -->
<?php
} // close the "else"
?>
</body>
</html>
Top-to-bottom: We first give the inquiree a choice of how they want us to respond, either by email or phone. Next we tell her/im which fields are the minimum required by showing their labels in boldface. Following that, we allow them to select their "honorific" (Mr., Ms., etc) and input their name (required), job title and organization. Next there are blanks for the email (required), phone and fax numbers. Following these are two SELECT/OPTION sets as examples of collecting categories of interest on the part of the inquiree. These might be useful for directing inquiries to specific departments or people in your organization. Lastly, we allow the inquiree to input a free-form list or paragraph describing specific needs they might have.
The textarea input "name='comments'" is set to allow 512 characters. I estimate that at being about 8 to 10 lines of text. If you wish to allow more, increase the value in the "maxlength" attribute of that entry. The last bit of PHP closes the "else" clause of the "if" statement, by providing a right-brace. It is followed by the HTML that finishes the page.
Implementing the script is simple: Copy and paste all the code blocks into a plaintext file and upload it to your website as "contact.php". It goes at the base level, the same place as your other web pages -- not in the /form_logs/ directory we created earlier.
To save you having to cut and paste, here is the complete contact page. (Download and save as "contact.php".) (zipped version)
Copyright 2005 Richard Nilsson. Verbatim copying and redistribution of this entire article are permitted without royalty in any medium provided this notice is preserved.