jQuery Validated Form for PWP

 Before you begin, read Sending Email in PHP

This lesson will guide you, at a high level, to implement a PHP driven custom contact form using Swiftmailer, jQuery Validate, and Google reCAPTCHA. Knowledge of the Composer package manager and PHP driven forms is a pre-requisite.

This form integrates secure front and back-end validation, and is spam and injection-attack resistant. :D

Hey You! Contact Us! :D

* Required Fields

Overview

This form requires the following dependencies:

  1. jQuery Form, jQuery Validate, and jQuery Additional Methods loaded in your HTML <head>.
  2. Swiftmailer AND Google reCAPTCHA installed via Composer (see Sending Email in PHP)
  3. Registered Google reCAPTCHA API Keys for your site/domain.
  4. Google reCAPTCHA JavaScript loaded in your HTML <head>

This form requires that you create the following files:

  1. HTML Form (see below)
  2. Form Validation JavaScript file (see below)
  3. PHP Mailer file with reCAPTCHA integrated (see Sending Email in PHP, and reCAPTCHA integration instructions below)

Refer to the following documentation:

Instructions

Before you begin, complete the following steps:

  1. First, register your site/domain on Google reCAPTCHA and have your API keys handy.
  2. Install Google reCAPTCHA and Swifmailer into your project via Composer. See the sample composer.json file below.
  3. Be sure that all the required jQuery/JavaScript dependencies are loaded in your HTML <head>.

Create your HTML contact form. Use the provided HTML code below as a guide. Pay close attention to the following key points:

  • The <form> id: You will need to reference this in your JavaScript Validator file.
  • The <form> action attribute: This should be the path to your mailer.php file.
  • The <form> method attribute should be: POST.
  • Each form input needs both an id AND a name attribute. These should have the same value.
  • Below your HTML form, insert an empty <div id="output-area"></div>. This will hold your error and/or success messages.

After your form has been created, create your mailer.php file. There will be a few lines that need to be added to integrate reCAPTCHA. See code below.

Create your JavaScript form validator file. Remember to link to it in your HTML <head> after your jQuery dependencies.

Required Dependencies

Link these libraries and files in your HTML <head> after jQuery.

<!-- jQuery Form, Additional Methods, Validate -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.form/4.2.2/jquery.form.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/additional-methods.min.js"></script>

<!-- Your JavaScript Form Validator -->
<script src="js/form-validate.js"></script>

<!-- Google reCAPTCHA -->
<script src='https://www.google.com/recaptcha/api.js'></script>

composer.json

Create/update your composer.json file, and run composer install. If you have already have a composer.lock file, then run composer update.

{
  "require": {
    "swiftmailer/swiftmailer": "@stable",
    "google/recaptcha": "~1.1"
  }
}

mail-config.php

For security, we will put all of our sensitive mail configuration data into a file called mail-config.php. We need to .gitignore this file. See sample code below.

Add this line to your .gitignore: **/php/mail-config.php

<?php
/**
 * mail-config.php
 * This file contains your reCAPTCHA API keys and your recipient's email addresses.
 *
 * @param string $siteKey your public reCAPTCHA API key
 * @param string $secret your secret reCAPTCHA API key
 * @param array $MAIL_RECIPIENTS array of email addresses and corresponding recipient names to send form responses to
 *
 * @author Rochelle Lewis <rlewis37@cnm.edu>
 *
 * This file contains sensitive information and should ALWAYS be gitignored!
 **/

// your Google reCAPTCHA keys here
$siteKey = '-- reCAPTCHA SITE KEY --';
$secret = '-- reCAPTCHA SECRET KEY --';

/**
 * attach the recipients to the message
 * notice this an array that can include or omit the the recipient's real name
 * use the recipients' real name where possible; this reduces the probability of the Email being marked as spam
 **/
$MAIL_RECIPIENTS = ["mail@email.com" => "Recipient Name"];

PHP Mailer with reCAPTCHA Integration

Add a require_once() statement to load your mail-config.php to the PHP Form Mailer. To enable reCAPTCHA integration, there are ~5 lines of code to add to the mailer file. See example below. Update all necessary paths, variables and values to line up with YOUR project.

<?php
/**
 * mailer.php
 *
 * This file handles secure mail transport using the Swiftmailer
 * library with Google reCAPTCHA integration.
 *
 * @author Rochelle Lewis <rlewis37@cnm.edu>
 **/

// require all composer dependencies
require_once(dirname(__DIR__, 2) . "/vendor/autoload.php");

// require mail-config.php
require_once("mail-config.php");

// verify user's reCAPTCHA input
$recaptcha = new \ReCaptcha\ReCaptcha($secret);
$resp = $recaptcha->verify($_POST["g-recaptcha-response"], $_SERVER["REMOTE_ADDR"]);

try {
	// if there's a reCAPTCHA error, throw an exception
	if (!$resp->isSuccess()) {
		throw(new Exception("reCAPTCHA error!"));
	}

	/**
	 * Sanitize the inputs from the form: name, email, subject, and message.
	 * This assumes jQuery (NOT Angular!) will be AJAX submitting the form,
	 * so we're using the $_POST superglobal.
	 **/
	$name = filter_input(INPUT_POST, "name", FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
	$email = filter_input(INPUT_POST, "email", FILTER_SANITIZE_EMAIL);
	$subject = filter_input(INPUT_POST, "subject", FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
	$message = filter_input(INPUT_POST, "message", FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);

	// create Swift message
	$swiftMessage = new Swift_Message();

	/**
	 * Attach the sender to the message.
	 * This takes the form of an associative array where $email is the key for the real name.
	 **/
	$swiftMessage->setFrom([$email => $name]);

	/**
	 * Attach the recipients to the message.
	 * $MAIL_RECIPIENTS is set in mail-config.php
	 **/
	$recipients = $MAIL_RECIPIENTS;
	$swiftMessage->setTo($recipients);

	// attach the subject line to the message
	$swiftMessage->setSubject($subject);

	/**
	 * Attach the actual message to the message.
	 *
	 * Here we set two versions of the message: the HTML formatted message and a
	 * special filter_var()'d version of the message that generates a plain text
	 * version of the HTML content.
	 *
	 * Notice one tactic used is to display the entire $confirmLink to plain text;
	 * this lets users who aren't viewing HTML content in Emails still access your
	 * links.
	 **/
	$swiftMessage->setBody($message, "text/html");
	$swiftMessage->addPart(html_entity_decode($message), "text/plain");

	/**
	 * Send the Email via SMTP. The SMTP server here is configured to relay
	 * everything upstream via your web host.
	 *
	 * This default may or may not be available on all web hosts;
	 * consult their documentation/support for details.
	 *
	 * SwiftMailer supports many different transport methods; SMTP was chosen
	 * because it's the most compatible and has the best error handling.
	 *
	 * @see http://swiftmailer.org/docs/sending.html Sending Messages - Documentation - SwitftMailer
	 **/
	$smtp = new Swift_SmtpTransport("localhost", 25);
	$mailer = new Swift_Mailer($smtp);
	$numSent = $mailer->send($swiftMessage, $failedRecipients);

	/**
	 * The send method returns the number of recipients that accepted the Email.
	 * If the number attempted !== number accepted it's an Exception.
	 **/
	if($numSent !== count($recipients)) {
		// The $failedRecipients parameter passed in the send() contains an array of the Emails that failed.
		throw(new RuntimeException("unable to send email"));
	}

	// report a successful send!
	echo "<div class=\"alert alert-success\" role=\"alert\">Email successfully sent.</div>";

} catch(Exception $exception) {
	echo "<div class=\"alert alert-danger\" role=\"alert\"><strong>Oh snap!</strong> Unable to send email: " . $exception->getMessage() . "</div>";
}

JavaScript Validator File

This file sets up the jQuery validate function which will provide front-end validation for your form. Upon successful front-end validation, this file will AJAX submit your form to the PHP mailer file, which will then run back end validation on the CAPTCHA and form input before successfully sending an email. Your form id and input name values MUST be referenced correctly here.

$(document).ready(function(){

  /**
  * jQuery Validate Function
  *
  * This function provides front-end validation for your form.
  * If all tests set up here pass, the form data is AJAX submitted
  * to the mailer.php file.
  *
  * Update this file as needed for your form.
  * All ids and name values must match up to your form here.
  *
  * @author Rochelle Lewis <rlewis37@cnm.edu>
  **/

  /* begin validate function here */
  $("#my-contact-form").validate({

    // setup handling of form errors
    debug: true,
    errorClass: "alert alert-danger",
    errorLabelContainer: "#output-area",
    errorElement: "div",

    // rules here define what is good or bad input
    // each rule starts with the form input element's NAME attribute
    rules: {
      name: {
        required: true
      },
      email: {
        email: true,
        required: true
      },
      message: {
        required: true,
        maxlength: 2000
      }
    },

    // error messages to display to the end user when rules above don't pass
    messages: {
      name: {
        required: "Please enter your name."
      },
      email: {
        email: "Please enter a valid email address.",
        required: "Please enter a valid email address."
      },
      message: {
        required: "Please enter a message.",
        maxlength: "2000 characters max."
      }
    },

    // AJAX submit the form data to back end if rules pass
    submitHandler: function(form) {
      $("#my-contact-form").ajaxSubmit({
        type: "POST",
        url: $("#my-contact-form").attr("action"),

        success: function(ajaxOutput) {
          // clear the output area's formatting
          $("#output-area").css("display", "");

          // write the server's reply to the output area
          $("#output-area").html(ajaxOutput);

          // reset the form if it was successful
          if($(".alert-success").length >= 1) {
            $("#my-contact-form")[0].reset();
          }
        }
      })
    }

  });/* end validate function here */

});/*end document.ready()*/

HTML Form Sample Code

Do NOT copy and paste this code as it is. This is provided as an example, and must be customized for your own project.

<!--
Begin Sample Contact Form

Create your own contact form.
Do not copy and paste this one.
-->
<form id="rochelles-demo-form" action="php/mailer.php" method="post">
  <div class="form-group">
    <label for="contactDemoName">Name <span class="text-danger">*</span></label>
    <div class="input-group">
      <div class="input-group-prepend">
        <span class="input-group-text"></span>
        <i class="fa fa-user" aria-hidden="true"></i>
      </div>
      <input type="text" class="form-control" id="contactDemoName" name="contactDemoName" placeholder="Name">
    </div>
  </div>
  <div class="form-group">
    <label for="contactDemoEmail">Email <span class="text-danger">*</span></label>
    <div class="input-group">
      <div class="input-group-prepend">
        <span class="input-group-text"></span>
        <i class="fa fa-envelope" aria-hidden="true"></i>
      </div>
      <input type="email" class="form-control" id="contactDemoEmail" name="contactDemoEmail" placeholder="Email">
    </div>
  </div>
  <div class="form-group">
    <label for="contactDemoSubject">Subject</label>
    <div class="input-group">
      <div class="input-group-prepend">
        <span class="input-group-text"></span>
        <i class="fa fa-pencil" aria-hidden="true"></i>
      </div>
      <input type="text" class="form-control" id="contactDemoSubject" name="contactDemoSubject" placeholder="Subject">
    </div>
  </div>
  <div class="form-group">
    <label for="contactDemoMessage">Message <span class="text-danger">*</span></label>
    <div class="input-group">
      <div class="input-group-prepend">
        <span class="input-group-text"></span>
        <i class="fa fa-comment" aria-hidden="true"></i>
      </div>
      <textarea class="form-control" rows="5" id="contactDemoMessage" name="contactDemoMessage" placeholder="I <3 CopyPasta :D~ (2000 characters max)"></textarea>
    </div>
  </div>

  <!-- reCAPTCHA -->
  <div class="g-recaptcha" data-sitekey="--YOUR RECAPTCHA SITE KEY--"></div>

  <button class="btn btn-success" type="submit"><i class="fa fa-paper-plane"></i> Send</button>
  <button class="btn btn-warning" type="reset"><i class="fa fa-ban"></i> Reset</button>
</form>

<!--empty area for form error/success output-->
<div class="row">
  <div class="col-xs-12">
    <div id="output-area"></div>
  </div>
</div>