Skip to content.

Scott Arciszewski

Software, Privacy, Security, Innovation

Building a Form Mail Script in PHP

August 1, 2014 11:32 PM • Development, PHP, Migrated, Tutorial

This was originally posted on a website I was developing over a year ago called Keenotes.


Building a form mail script in PHP is rather straightforward. First, I'll show a plaintext example, then I'll add captcha verification, and finally move onto accepting rich text (HTML) emails.

Plaintext Form Mailer

This is the most basic of the basic. If you asked someone with a basic grasp of the PHP programming language, they might throw something together that looks like this:

<?php
/* PHP Form Mailer - Plaintext version */
/* I do not recommend using this one in production. Spammers will use it! */
/* Written by Scott Arciszewski on 2013-03-22 */
define("CONTACT_EMAIL", "[email protected]"); // Replace me
if(isset($_POST['replyTo']) && isset($_POST['subject']) && isset($_POST['body']) ) {
mail(CONTACT_EMAIL, $_POST['subject'], $_POST['$body'], "From: {$_POST['replyTo']}\r\nReply-to: {$_POST['replyTo']}");
// Do other stuff (thank the user, or do a header("Location: /thanks.php"); redirect;
  exit;
} else {
// Put your layout header here
?>
<form method="post">
From: <input type="text" name="replyTo" required="required" /><br />
Subject: <input type="text" name="subject" required="required" /><br />
<textarea name="body" required="required"></textarea><br />
<button type="submit"><b>Send</b> Email</button>
</form>
<?
// Put your layout footer here
}
?>

This is a pretty simple "Send us an email" script. All it does is take 3 form values. take note of the

method (post) and the use of $_POST. If you don't specify the method, you won't get any values for $_POST and when you test it you will see .../emailus.php?replyTo=test%40example.org&etc... in your address bar.

However, if you just copy and paste that code to your website, the only thing that you will probably get is spam. It's not very user-friendly, it doesn't stop spammers, and it doesn't prompt visitors for specific information. We can do better.

Plaintext Variant: Ask for specific information from the user

Let's tackle the specific information bit first. We're going to need to change the HTML significantly, and the PHP substantially to ask for more information:

<?
/* PHP Form Mailer - Plaintext version - Multiple form element variant */
/* I do not recommend using this one in production. Spammers will use it! */
/* Written by Scott Arciszewski on 2013-03-22 */
  define("CONTACT_EMAIL", "[email protected]"); // Replace me
  if(isset($_POST['replyTo']) && isset($_POST['subject']) && isset($_POST['body']) ) {
$body = "Age:\t\t{$_POST['age']}\n".
"Sex:\t\t{$_POST['sex']}\n".
"Location:\t{$_POST['location']}\n".
str_repeat('#', 80)."\n\n{$_POST['body']}\n".
"Sent from: {$_SERVER['REMOTE_ADDR']} at {$_SERVER['REQUEST_TIME']}\n";
mail(CONTACT_EMAIL, $subject, $body, "From: {$_POST['replyTo']}\r\nReply-to: {$_POST['replyTo']}");
// Do other stuff (thank the user, or do a header("Location: /thanks.php"); redirect;
exit;
  } else {
// Put your layout header here
?>
<form method="post">
  From: <input type="text" name="replyTo" required="required" /><br />
  Subject: <input type="text" name="subject" required="required" /><br />
  <!-- These are all custom fields! -->
Your age: <input type="text" name="age" required="required" /><br />
Your sex: <input type="text" name="sex" required="required" /><br />
Your location: <input type="text" name="location" required="required" /><br />
  <!-- Now we return to business as usual! -->
  Your Message:<br />
  <textarea name="body" required="required"></textarea><br />
  <button type="submit"><b>Send</b> Email</button>
</form>
  <?
  // Put your layout footer here
}
?>

This script displays more form options and compiles it into a structure instead of just feeding raw post data into the mail() function. This isn't a complete solution, but we're definitely getting somewhere.

Stopping (or at least reducing) Spam with reCAPTCHA

Captchas are a double-edged sword: They stop most spammers, but they annoy, frustrate, and chase away some of the impatient short-sighted brats we affectionately know as "end users" and in some cases "customers". But you can't please everybody, and these are usually the sorts of people who are predisposed towards shallow hostility, grudges, gossip, and blissful ignorance anyway, so the end result is probably the same.

The first thing you want to do is go to https://www.google.com/recaptcha/whyrecaptcha and sign up for a key pair for the recaptcha API and download their PHP library. At this point, you should definitely read the Using reCAPTCHA with PHP page to learn how to set it up. I'm not going to explain it here because that article sums it up almost perfectly, and there's more interesting stuff below.

Sending HTML Emails

The next thing you'll want to do is make your emails send in HTML format so you can offer your user a chance to use bold and colored text (usually through an editor like TinyMCE).

<?
/* PHP Form Mailer - HTML Version - No reCAPTCHA */
/* I do not recommend using this one in production. Spammers will use it! */
/* Written by Scott Arciszewski on 2013-03-22 */
  define("CONTACT_EMAIL", "[email protected]"); // Replace me
  // It has been neglected!
  if(isset($_POST['replyTo']) && isset($_POST['subject']) && isset($_POST['body']) ) {
$age = preg_replace('/[^0-9]+/', '', $_POST['age']);
$sex = preg_replace('/[^A-Za-z]+/', '', $_POST['sex']);
$loc = htmlentities($_POST['location'], ENT_QUOTES | ENT_HTML5, 'UTF-8');
$message = htmlentities($_POST['body'], ENT_QUOTES | ENT_HTML5, 'UTF-8');
$body = <<<ENDOFHTMLBODY
<!DOCTYPE html>
<html>
  <body>
<table>
  <colgroup>
<col style="font-weight: bold; text-align: right;" />
<col style="font-family: 'Courier New';" />
  </colgroup>
  <tr>
<td>Age:</td>
<td>{$age}</td>
  </tr>
  <tr>
<td>Sex:</td>
<td>{$sex}</td>
  </tr>
  <tr>
<td>Location:</td>
<td>{$loc}</td>
  </tr>
</table>
{$message}
  </body>
</html>
ENDOFHTMLBODY;
mail(CONTACT_EMAIL, $subject, $body, "Content-Type: text/html;charset=UTF-8\r\nFrom: {$_POST['replyTo']}\r\nReply-to: {$_POST['replyTo']}");
// Do other stuff (thank the user, or do a header("Location: /thanks.php"); redirect;
exit;
  } else {
// Put your layout header here
?>
<form method="post">
  From: <input type="text" name="replyTo" required="required" /><br />
  Subject: <input type="text" name="subject" required="required" /><br />
  <!-- These are all custom fields! -->
Your age: <input type="text" name="age" required="required" /><br />
Your sex: <input type="text" name="sex" required="required" /><br />
Your location: <input type="text" name="location" required="required" /><br />
  <!-- Now we return to business as usual! -->
  Your Message:<br />
  <textarea name="body" required="required"></textarea><br />
  <button type="submit"><b>Send</b> Email</button>
</form>
  <?
  // Put your layout footer here
}
?>

There are a couple things I added here:

  • I used PHP's function, preg_replace(), to filter the inputs provided by the user.
    • preg_replace('/[^0-9]+/', '', $_POST['age']) - This removes anything that isn't a digit (0-9)
    • preg_replace('/[^A-Za-z]+/', '', $_POST['sex']); - This removes anything that isn't an uppercase or lowercase letter. This way, the form accepts any of the following: M, F, Male, Female, Transgender
  • Then, I used htmlentities() to filter the location and the user's message. This was done to prevent HTML from being injected into your email (Hi, my name is <iframe src="http://evilhackerUrl"></iframe>).
  • Added Content-Type: text/html;charset=UTF-8 to the mail() header

This code still will not allow the user to use HTML (it's stripped away with the htmlentities function). For XSS/CSRF resistance, you still need to use a plugin like HTMLPurifier.

Putting It All Together

So if you set out to create a PHP Form Mailer that asks for detailed information, uses reCAPTCHA to stop spammers, and sends out an HTML-formatted email, you should end up with something like this:

<?
/* PHP Form Mailer - HTML Version - With reCAPTCHA */
/* Written by Scott Arciszewski on 2013-03-22 */
################################################################################
# CONFIGURATION                                                                #
  define("CONTACT_EMAIL", "[email protected]");
  define("RECAPTHCAPRIVATE", "FILLMEINFILLMEINFILLMEINFILLME");
  define("RECAPTHCA_PUBLIC", "LLMEINFILLMEINFILLMEINFILLMEIN");                
#                                                                              #
################################################################################
  // Alias function for the htmlentities() variant
require_once "recaptchalib.php";
require_once "HTMLPurifier.auto.php"; // http://htmlpurifier.org
$XSS = new HTMLPurifier();

function noHTML($input) {
  return htmlentities($input, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
if(isset($_POST['replyTo']) && isset($_POST['subject']) && isset($_POST['body']) ) {
    $resp = recaptcha_check_answer(
          RECAPTHCAPRIVATE,
          $_SERVER["REMOTE_ADDR"],
          $_POST["recaptcha_challenge_field"],
          $_POST["recaptcha_response_field"]
        );
if($resp->is_valid) {
  $age = preg_replace('/[^0-9]+/', '', $_POST['age']);
  $sex = preg_replace('/[^A-Za-z]+/', '', $_POST['sex']);
  $loc = noHTML($_POST['location']);
  $message = $XSS->purify($_POST['body']); // HTML Purifier
  $body = <<<ENDOFHTMLBODY
<!DOCTYPE html>
<html>
  <body>
    <table>
      <colgroup>
        <col style="font-weight: bold; text-align: right;" />
        <col style="font-family: 'Courier New';" />
      </colgroup>
      <tr>
        <td>Age:</td>
        <td>{$age}</td>
      </tr>
      <tr>
        <td>Sex:</td>
        <td>{$sex}</td>
      </tr>
      <tr>
        <td>Location:</td>
        <td>{$loc}</td>
      </tr>
    </table>
    {$message}
  </body>
</html>
ENDOFHTMLBODY;
  mail(CONTACT_EMAIL, $subject, $body, "From: {$_POST['replyTo']}\r\nReply-to: {$_POST['replyTo']}");
  // Do other stuff (thank the user, or do a header("Location: /thanks.php"); redirect;
  exit;
} else {
  // Put your layout header here
    ?>
    <form method="post">
      From: <input type="text" name="replyTo" required="required" /><br />
      Subject: <input type="text" name="subject" required="required" /><br />
      <!-- These are all custom fields! -->
        Your age: <input type="text" name="age" required="required" /><br />
        Your sex: <input type="text" name="sex" required="required" /><br />
        Your location: <input type="text" name="location" required="required" /><br />
      <!-- Now we return to business as usual! -->
      Your Message:<br />
      <textarea name="body" required="required"></textarea><br />
      <?
        $error = '';
        echo recaptcha_get_html(RECAPTHCA_PUBLIC, $error, true);
        echo $error;
      ?>
      <button type="submit"><b>Send</b> Email</button>
    </form>
  <?
  // Put your layout footer here
}
?>

Download this code here.

Further Considerations

Do you want users to be able to include attachments with their emails? That's an article for a different time.

Blog Archives Categories Latest Comments

Want to hire Scott Arciszewski as a technology consultant? Need help securing your applications? Need help with secure data encryption in PHP?

Contact Paragon Initiative Enterprises and request Scott be assigned to your project.