GreenBeast’s PHP Contact Form

Posted October 4th, 2005 by Mike Cherim

Update: Complete Downloads now available on Official page

There’s been some interest in my PHP contact forms lately as a few people have asked about them. Most notably my latest one, the one on the Contact Page on my portfolio Green-Beast.com has been brought up. I told a few people I’d post it here since there’s interest. Plus, considering how many people have helped me, well, I enjoy the opportunity to give something back.

My PHP contact form is basically comprised of three parts. First there is the form and the engine which runs it. This is located in a single PHP file. This is the file that makes the form work and puts it on the page. The other parts associated are form styling and special focus-related functions unsupported by Internet Explorer (IE). These part consist of a single JavaScript file and a number of CSS entries. I will detail the script/form first (in little blocks for clarity and understanding). Please note this is a slightly modified version from what’s actually on the referenced site.


The Script and Form Body

File Name: contact_form.php

First thing is to identify the script type and how it’ll work. I show here it is PHP and it is meant to work or function IF values are POSTed. I show the values I need/want to collect by the submission of the form. You can add or remove values as you see fit, just follow the example. To guarantee some minimum values and to make form abuse by spammers more difficult, four fields are required: 1) Name [text input], 2) Email [text input], 3) Reason [select options pulldown], and 4) Topicbody [textarea].

<?php
if ($_POST)
{
$name = $_POST['name']; // Required
$email = $_POST['email']; // Required
$phone = $_POST['phone'];
$url = $_POST['url'];
$reason = $_POST['reason']; // Required
$topicbody = $_POST['topicbody']; // Required
$topicbody = stripslashes($topicbody); // Removes PHP-inserted "escape" slashes
$ltd = date("l, F j, Y - g:i a") ; // Date and Time
$ip = getenv("REMOTE_ADDR"); // Submitter's IP address
$hr = getenv("HTTP_REFERER"); // Submitted from URL (will be form URL)

Now I will make the required fields actually required. Basically it first identifies the required values then seeks their input content on submit, and IF the fields are empty, it spits out an error instead of sending the form. Any field OR another will cause the error. You can add or remove required fields as you see fit, just follow the example.

if (!isset($name,$email,$reason,$topicbody) || empty($name) || empty($reason) || empty($topicbody)){
 print "<h2>Error!</h2>
<h3>Sorry. An error has occurred!</h3>
<p>The following &#8220;Required&#8221; fields were not filled in. Using your &#8220;Back&#8221; button, please go back and fill in all required fields.</p>n";
echo '<dl><dt>Field(s) Missed:</dt>'."\n";

Note the <h2>, <h3>, <p>, and definition list in the error - this will need CSS styling. (Note: you may want to add a class in your CSS for the <h2>, such as h2.error { color:red; }, in which case you’d write it like this: <h2 class=\"error\">. Note the escape-slashes in front of the quotes. This is needed as the string is parsed by quotes and if the quotes were not escaped would halt the script prematurely.) This next part returns the missed field name(s) to avoid confusion. This way not only is an error returned, but it’s a smart error specifying what exactly went wrong. So the definition list called “Field(s) Missed” is populated by definitions. This is why I used this XHTML, it fit best.

if(empty($name)){ echo '<dd>&#8220;Enter your full name&#8221;</dd>'."\n"; }
if(empty($email)){ echo '<dd>&#8220;Enter your email address&#8221;</dd>'."\n"; }
if(empty($reason)){ echo '<dd>&#8220;Select a contact reason&#8221;</dd>'."\n"; }
if(empty($topicbody)){ echo '<dd>&#8220;Enter your message&#8221;</dd>'."\n"; }
 echo '</dl>'."\n";
} else {

Next some email field scripting is repeated and continued because it can trigger an error in two ways - it looks not only for field population, but also looks for email address parts “@” and “.” specifically. This is not full email validation, but it’s enough to keep the honest people honest. First we look for input field content.

if(!isset($email) || empty($email)){
echo '<h2>Error!</h2>
<h3>Sorry. An error has occurred!</h3>
<p>The following &#8220;Required&#8221; fields were not filled in. Using your &#8220;Back&#8221; button, please go back and fill in all required fields.</p><dl><dt>Field(s) Missed:</dt><dd >&#8220;Enter your email address&#8221;</dd></dl>';

Here’s the part where the email “parts” (formatting) are confirmed. The value in the email field must contain “@” and “.” else it returns the error saying the address doesn’t seem to be valid. This can correct a common and innocent error such as putting a comma “,” instead of a period “.” as one example. In this error message I insert a link to the privacy policy assuming that one may have falsified their email address to protect themselves from spammers.

} else if(strpos($email,".") === FALSE || strpos($email,"@") === FALSE) {
 echo '<h2>Error!</h2>
<h3>Sorry. An error has occurred!</h3>
<p>The email address you have submitted seems to be invalid. Using your &#8220;Back&#8221; button, please go back and check the address you entered. Worried about Spam? Please review my <a href="/siteinfo/#privacy" title="Privacy Policy">Privacy Policy</a> and worry no more.</p>';
} else {

If the fields were filled in and the email address formatted properly, the script continues. It sort of reads like this in plain-speak: Hi, I’m a script, please give me some values when you post by way of my form. IF you don’t give me these values the way I want them, by not getting one OR the other right, I will give you a friendly message telling you you screwed up, ELSE, I’m going to starting using your inputted values to work in a couple of different ways. Okay. See what’s happening now? The values are inputted and compiled to do a couple of things. First a new composite value called “$message” is formatted. The \n is a break. I use \n\n for carriage returns, but I think \r can be used. This is how the one at Green-Beast.com reads.

$message = "Hello Mike,\n\nYou are being contacted via Green-Beast.com by $name\n\n$name has provided the following information so you may contact them:\n\nEmail: $email\nPhone: $phone\nWebsite: $url\n\n$name has contacted you to $reason.\n$name has supplied the following comments:\n$topicbody\n\n\nOther Collected Information:\nIP Address: $ip\nTime Stamp: $ltd\nReferrer: $hr";

The output looks like this:


Hello Mike,

You are being contacted via Green-Beast.com by John Doe

John Doe has provided the following information so you may contact them:

Email: jdoe@doenet.com
Phone: 980-576-3412
Website: http://doenet.com

John Doe has contacted you to make a general comment.
John Doe has supplied the following comments:
Nice contact form!

Other Collected Information:
IP Address: 00.00.00.00
Time Stamp: Friday, October 3rd, 2005 - 3:19 pm
Referrer: http://green-beast.com/contact/


Now let’s continue with the script… The next part forms and sends the actual email, again using the values combined now with the new composite value. (Note: I use no sender info in my form, but I put identifying info in the subject line. Only I see this anyway. Sender is the server name.)

mail("myemail@mydomain.com", "Green-Beast.com contact: $name", $message);

That was simple, huh? This next part also takes the submitted values and returns them to page at the same exact time the email is being sent by the server. This part forms a nice visual confirmation actually showing the submitter all of the submitted information. Note the escape slashes. Miss just one and the script won’t work. Also note the all caps I used on the form reset. You don’t want a user to accidentally try hitting Refresh to clear the form results as this would prompt a re-post. If they didn’t know better and chose “Ok” to re-post, the info you’d get redundant email.

echo "<h2>Success!</h2>
<h3>Contact Made!</h3>
<p>Thank you for making contact with Mike Cherim, <em>$name</em>. If appropriate, Mike will get back to you shortly. You have successfully submitted the following information:</p>
<ul>
<li>$name</li>
<li><a href=\"mailto:$email\" title=\"$name&#8217;s $email\">$email</a></li>
<li>$phone</li>
<li><a href=\"$url\" title=\"$name&#8217;s $url\">$url</a></li>
</ul>
<p>You&#8217;re making contact to <em>$reason</em>.</p>
<p>And you added the following message:</p>
<blockquote cite=\"$name\">
<p>$topicbody</p>
<p><cite>$name</cite></p>
</blockquote>
<p><a href=\"$hr\" title=\"Clear Results &amp; Reset Form\">CLEAR RESULTS &amp; RESET FORM</a></p>
<dl>
<dt>Form submitted by:</dt>
<dd>$name</dd>
<dd>$ip</dd>
<dd>$ltd</dd>
</dl>\n";
}
}
} else {
?>

That’s it. Else nothing except closing and re-opening the script, and then closing it again at the end of the form. This integrates the form into the PHP script, which removes it if there is an error or upon successful submission. Now I will detail the actual form. On this form I had “titles” or tool tips on the input select options. I removed them here for clarity. I purposely made this input select required (thus the value of “selected” is null: value=""). The reason for this is to inconvenience spammers who use the form anyway, despite its required fields. Cutting-and-pasting garbage is fast and easy for the determined spammer wanting to use your server resources to B.C.C. his or her victims, but having a pull down makes it more like work encouraging the would-be spammer to go out and find and easier form to cozy up with for the night.

First a little intro to the form with a jump link to some contact options like a telephone number, etc.

<form method="post" action="<?= $_SERVER["PHP_SELF"]; ?>">
<h2>Contact Mike Cherim</h2>
<p>The form on this page is an excellent way to communicate with me. The form is mine, the choice to use it is all yours. If you wish to use another method of contact, I do offer some <a href="#options" title="Alternative methods of contact">Contact Options</a> on this very page.</p>

Now the first set of fields, Name and Email. I add “Required” to the legend to inform the visitor.

<fieldset>
<legend>&#8220;Required&#8221; contact info:</legend>
<div><label for="name">Enter your full name<br /><input type="text" name="name" id="name" size="35" maxlength="50" value="" /></label></div>
<div><label for="email">Enter your email address<br /><input type="text" name="email" id="email" size="35" maxlength="50" value="" /></label></div>
</fieldset>

Now the second set of fields, Phone and URL. I add “Optional” to the legend to inform the visitor.

<fieldset>
<legend>&#8220;Optional&#8221; contact info:</legend>
<div><label for="phone">Enter your phone number<br /><input type="text" name="phone" id="phone" size="35" maxlength="50" value="" /></label></div>
<div><label for="url">Enter your website address<br /><input type="text" name="url" id="url" size="35" maxlength="50" value="http://" /></label></div>
</fieldset>

Now the select pulldown, Reason. Again I add “Required” to the legend to inform the visitor.

<fieldset>
<legend>&#8220;Required&#8221; contact reason:</legend>
<div><label for="reason">Select a contact reason<br />
<select name="reason" id="reason">
<option value="" selected="selected">Please make a selection</option>
<option value="ask about having a website built">I want to have a website built</option>
<option value="ask about having an existing design customized">I&#8217;d like an existing design customized</option>
<option value="request some website maintenance">I need website maintenance</option>
<option value="request design of an existing application">I need an existing application designed</option>
<option value="ask about scripting services">I need to have a script made</option>
<option value="have some graphics made">I need imagery or graphics work</option>
<option value="request another service">I need another service (Explain below)</option>
<option value="just say hello">I just want to say a quick hello</option>
<option value="ask a question">I have a question to ask</option>
<option value="make a general comment">I have a comment to make</option>
<option value="report a problem on this site">I need to report a problem on this site</option>
<option value="do other…">Other... (Explain below)</option>
</select></label></div>
</fieldset>

Now the text area, Topicbody. I add “Required” to the legend to inform the visitor.

<fieldset>
<legend id="next">&#8220;Required&#8221; comments area:</legend>
<div><label for="topicbody">Enter your message<br /><textarea rows="6" cols="32" name="topicbody" id="topicbody"></textarea></label></div>
</fieldset>

Now the buttons, Submit Form and Reset Form. Note the only form element classes used in this whole thing are in these buttons to make them distinct from the other inputs for better static, hover, and focal control. Other fields may be classed too. An example would be “Postal or Zip Code” as you might want that input field shorter that the others and thus you’d style it separately.

<fieldset>
<legend>Time to send it:</legend>
<p>Okay, great, one more step: Click the &#8220;Submit Form&#8221; button. If appropriate to your message, you will be contacted shortly.</p>
<div><label for="send"><input class="button" type="submit" value="Submit Form" name="send" id="send" /></label>
<label for="change"><input class="button" name="change" type="reset" id="change" value="Reset Form" /></label></div>
</fieldset>
</form>

That’s it. The form ends. Now we add closure to the script and gather up the form XHTML in a tidy little package that the server will use as needed.

<?php
}
?>

Style The Form

File Name: n/a - Add to your CSS

What is posted above is a complete contact form that’ll work if PHP is supported on the server, regardless of style and whether or not CSS is supported. However, to make the form look good a to add focus to it to make it more usable and accessible, I offer the following CSS entries. Do note that only one class is used so you are adding style to any input field using this style sheet. Add classes if you need them, or use input field IDs if the changes you need to make are very limited. Here’s the CSS:

/* Note: I use a star "*" selector to zero padding and margin so those will need to be defined */
fieldset {
   margin-top : 20px;
   margin-left : 40px;
   border : none;
}
legend {
   font-weight : bold;
   color : #99cc66;
}
label {
   font-weight : normal;
   font-size : 90%;
   padding-top : 20px;
   padding-left : 4px;
   margin-left : 8px;
}
/* The font needs to be defined as it does not inherit from the body. ***Note that the color contrast needs to be colored enough to be visible if the IE user has JavaScript disabled (#666; on #ccc; in this case). */
input, select, textarea {
   font-family : 'Trebuchet MS', Tahoma, Arial, san-serif;
   width : 90%;
   color : #666;
   font-size : 100%;
   background : #ccc;
   border : 1px solid #999;
   margin : 2px;
   margin-left : 12px;
   padding : 4px;
}
/* Note that due to a fixed page width the input widths are defined in pixels or percent (of a fixed space) whereas the height is in EMs (or percent if the height is not defined) so the fields will get bigger with the text if enlarged */
input {
   width : 82%;
   height : 1.6em;
   border : 1px solid #999;
   padding : 2px;
}
select, option {
   height : 2em;
   padding-left : 4px;
   padding : 2px;
}
textarea {
   font-size : 100%;
   margin-top : 8px;
   width : 90%;
   height : 20em;
   padding : 4px;
}
input:focus, textarea:focus, select:focus, input.focus, textarea.focus, select.focus, input:hover, textarea:hover, select:hover, input.hover, textarea.hover, select.hover {
   color : #000;
   height : 1.6em;
   background : #fafafa;
   border : 1px solid #333;
   cursor : text;
   margin-left : 12px;
}
textarea:focus, textarea.focus, textarea:hover, textarea.hover {
   width : 90%;
   height : 20em;
   cursor : text;
}
select:hover, select:focus, select.focus {
   height : 2em;
   cursor : pointer;
   margin-left : 12px;
}
/* Here's where the .button class comes into play */
input.button {
   width : 25%;
   height : 2em;
   color : #333;
   font-weight : bold;
   border : 1px solid #999;
   margin-bottom : 10px;
   background : #ccc;
}
.button:hover, .button:focus {
   cursor : pointer; /*This is the little link hand, not an arrow. Do not use cursor:hand */
   background : #99cc66;
   color : #fafafa;
   border : 1px solid #333;
}
/* This is necessary for the JavaScript used for IE (keep reading) */
input.button.hover, input.button.focus {
   cursor : pointer;
   background : #99cc66;
   color : #fafafa;
   border : 1px solid #333;
}

That’s it. That’s a lot of style information, but I don’t see a way around it. There are a lot of what seem to be duplicates, but note that some are JavaScript classes. Example: :hover versus .hover.

Using the form above and about 2/3 of the CSS information above, you’ll have a perfectly functional, somewhat smart, and beautifully-styled form.

***But guess what? In IE, the form’s focus won’t work. JavaScript is needed to make the form behave in IE as it will in all other top browsers. In the CSS above I make the unfocused input field state rather subdued. Medium Gray #666 or silver #ccc in this case. You can use whatever colors you like to match your site, but be careful to retain enough contrast in case focus doesn’t work, which will be the case if the IE user has JavaScript disabled. In this case I avoided using #999; (light gray) or silver.


Add Scripting for IE

File Name: focus_js.js

Okay, now here’s the JavaScript needed. The first event is to start the function when the page loads.

window.onload = function()

Now we take a state and tell it to swap out for a class of the same name. Hover state for hover class and a script function, in this case, and it is applied only to XHTML elements as identified by their TagName… “input” in the first one. It swaps the CSS :hover state for a script state, name “onmouseover” and tells it to employ the .hover class. This script also removes the state “onmouseout” once the pointer moves off of the element by replacing .hover class with a null value.

{
  var inputs = document.getElementsByTagName("input");
  for(var i = 0; i < inputs.length; i++){
   inputs[i].onmouseover = function(){
    this.className += " hover";
   }
   inputs[i].onmouseout = function(){
    this.className = this.className.replace(" hover", "");
   }
  }

Now for “select” :hover

{
  var select = document.getElementsByTagName("select");
  for(var s = 0; s < select.length; s++){
   select[s].onmouseover = function(){
    this.className += " hover";
   }
   select[s].onmouseout = function(){
    this.className = this.className.replace(" hover", "");
   }
  }

Now for “textarea” :hover

{
  var textarea = document.getElementsByTagName("textarea");
  for(var t = 0; t < textarea.length; t++){
   textarea[t].onmouseover = function(){
    this.className += " hover";
   }
   textarea[t].onmouseout = function(){
    this.className = this.className.replace(" hover", "");
   }
  }

Now we do it all again, this time for the focus state. Focus is the only thing used. When styling links for focus one must add a:active to make them work in IE. But since forms aren’t supported anyway, this information need not be present. Do, note, however, the change in scripting. No longer am I using “onmouseover” and “onmouseout,” but now I’m using “onfocus” and “onblur.”

{
  var inputs = document.getElementsByTagName("input");
  for(var i = 0; i < inputs.length; i++){
   inputs[i].onfocus = function(){
    this.className += " focus";
   }
   inputs[i].onblur = function(){
    this.className = this.className.replace(" focus", "");
   }
  }
{
  var select = document.getElementsByTagName("select");
  for(var s = 0; s < select.length; s++){
   select[s].onfocus = function(){
    this.className += " focus";
   }
   select[s].onblur = function(){
    this.className = this.className.replace(" focus", "");
   }
  }
{
  var textarea = document.getElementsByTagName("textarea");
  for(var t = 0; t < textarea.length; t++){
   textarea[t].onfocus = function(){
    this.className += " focus";
   }
   textarea[t].onblur = function(){
    this.className = this.className.replace(" focus", "");
   }
  }

Now the 6 script code blocks are closed to make the whole thing one.

}
}
}
}
}
}

Whew, we’re almost done. There are two steps left. One we have to include the script link in the document head. I am assuming you’re all set on the style info as you are adding the CSS I gave to your existing style sheet. Since this script is only needed for Internet Explorer, I parse it with a conditional comment so the other browsers simply ignore it. As follows:

<!--[if IE]><script type="text/JavaScript" src="focus_js.js"></script><![endif]-->

One step left. We need to include the form into the web page you want it to appear. This is done easily:

<?php include("contact_form.php"); ?>

That’s it. The whole shebang. The works. Please post any comments or questions you may have.


6 Responses to: “GreenBeast’s PHP Contact Form”

  1. Martin Neczypor responds:
    Posted: October 4th, 2005 at 1:55 pm

    That’s wonderful, man. I really love forms that just make sense, and this one definately fits the bill. Great work, and thanks for sharing! I will definately start to make use of this on my new design ;o).

  2. Fabian responds:
    Posted: October 7th, 2005 at 1:24 am

    I just redone my form to stop stupid spammers…
    I was just reading your about page I just relised that Im almost 3 times younger then you :P
    Love your new redesign

  3. Benjamin Moore responds:
    Posted: November 1st, 2005 at 10:27 am

    Very interesting! I liked it!

  4. Jeffrey Campbell responds:
    Posted: November 1st, 2005 at 11:09 am

    You’re doing a great work here. I enjoyed visiting here very much. Thanks!

Sorry. Comments are closed.




Note: This is the end of the usable page. The image(s) below are preloaded for performance only.