PHP jQuery AJAX Contact Form CAPTCHA Code Sample (BotDetect v3.0; deprecated)
This code sample demonstrates the use of BotDetect Captcha in a scenario using jQuery and AJAX to validate individual form fields against a PHP server backend. The approach in this scenario is useful in situations where duplication of server-side validation routines on the client side is impractical.
First Time Here?
Check the BotDetect PHP Captcha Quickstart for key integration steps.
To make the sample brief and concrete, it is implemented as an AJAX contact form with Captcha protection.
The sample shows proper steps to be taken when validating the Captcha in various ways this scenario provides: be it partial validation or complete form validation and submission using AJAX; or server side validation only when Javascript is disabled.
index.php
<?php // PHP v5.2.0+ required session_start(); // include BotDetect Captcha library files require("botdetect.php"); // create & configure the Captcha object $ContactCaptcha = new Captcha("ContactCaptcha"); $ContactCaptcha->UserInputID = "CaptchaCode"; $ContactCaptcha->CodeLength = 3; $ContactCaptcha->ImageWidth = 150; $ContactCaptcha->ImageStyle = ImageStyle::Graffiti2; require("process_form.php"); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>PHP jQuery AJAX Contact Form CAPTCHA Sample</title> <link type="text/css" rel="Stylesheet" href="StyleSheet.css" /> <!-- include the captcha stylesheet --> <link type="text/css" rel="Stylesheet" href="<?php echo CaptchaUrls:: LayoutStylesheetUrl() ?>" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <script type="text/javascript" src="js/jquery-1.7.2.min.js"></script> <script type="text/javascript" src="js/validation.js"></script> </head> <body> <form method="post" action="index.php" id="contactForm" name="contactForm"> <h1>PHP jQuery AJAX Contact Form CAPTCHA Sample</h1> <fieldset> <legend>Contact Form</legend> <?PHP echo showValidationMessage("Form"); ?> <div class="input"> <label for="Name">Name:</label> <input type="text" name="Name" id="Name" class="textbox" value="<?php echo getValue('Name');?>" /> <?php // name validation failed, show error message echo showValidationMessage("Name"); ?> </div> <div class="input"> <label for="Email">Email:</label> <input type="text" name="Email" id="Email" class="textbox" value=" <?php echo getValue('Email');?>" /> <?php // email validation failed, show error message echo showValidationMessage("Email"); ?> </div> <div class="input"> <label for="Message">Short message:</label> <textarea class="inputbox" id="Message" name="Message" rows="5" cols="40"><?php echo getValue('Message');?></textarea> <br /> <?php // message validation failed, show error message echo showValidationMessage("Message"); ?> </div> <div class="input"> <?php // only show the Captcha if it hasn't been already solved for the current message if(!$ContactCaptcha->IsSolved) { ?> <label for="CaptchaCode">Retype the characters from the picture: </label> <?php echo $ContactCaptcha->Html(); ?> <input type="text" name="CaptchaCode" id="CaptchaCode" class="textbox"/> <?php // Captcha validation failed, show error message echo showValidationMessage("CaptchaCode"); }?> </div> <input type="submit" name="SubmitButton" id="SubmitButton" value="Submit" /> </fieldset> </form> </body> </html>
The form and Captcha loading code is mostly basic and standard way of loading and configuring the BotDetect Captcha. In addition to the form elements, the showValidationMessage()
php method outputs per field validation messages. Also note that the Captcha is not shown if it has already been solved by the user.
Main server side functionality is provided in the process_form.php
file, which is included after creating and configuring the Captcha object.
process_form.php
<?php // An array of validation responses $validationResult = array(); // Type of request we are interested in. $_POST, $_GET or both - $_REQUEST $request = $_REQUEST; foreach($request as $formItem => $value) { $validationMethod = "Validate" . ucfirst($formItem); if (is_callable($validationMethod)) { // This is an array of validation result arrays for each field // see setElementValidationResponse() for more details. $validationResult[$formItem] = call_user_func($validationMethod, $value); } } // Total form validation result $isFormValid = true; foreach($validationResult as $formItem){ $isFormValid = $isFormValid && $formItem['isValid']; } // Respond according to results and request type // Regular response to POST form submission if (key_exists("SubmitButton", $request)) { // We want to make sure our Captcha is solved before continuing if (!$isFormValid || !$ContactCaptcha->IsSolved) { // Form validation failed, show our error message $validationResult['Form'] = setElementValidationResult(false, null, "Please review your input."); } else { // We send the message with content from the validator // Additional sanitization should be implemented along with the validation $isSent = sendMessage($validationResult['Name']['validContent'], $validationResult['Email']['validContent'], $validationResult['Message'][ 'validContent']); if ($isSent === true) { // each message requires a new Captcha challenge $ContactCaptcha->Reset(); $validationResult['Form'] = setElementValidationResult(true, null, 'Your message was sent.'); } else { $validationResult['Form'] = setElementValidationResult(false, null, "The server had problems sending your message."); } } } // JSON response when AJAX parameter is passed if (isset($_GET['AJAX']) && $_GET['AJAX'] == 1) { header('Cache-Control: no-cache, must-revalidate'); header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Content-type: application/json'); echo json_encode($validationResult); // Terminate further output after JSON exit; } // we are processing a valid submited form function sendMessage($name, $email, $message) { return true; // TODO: send email only after configuring your email server settings /* $to = "YOUR_EMAIL_HERE"; $subject = "Message from your website"; $headers = 'From: ' . $email; $headers .= '\r\n\'Reply-To: ' . $email; $headers .= '\r\n\'X-Mailer: PHP/' . phpversion(); $body = $name . " (" . $email . ") sent the following message:\n" . $message; $body = wordwrap($body, 70); return mail($to, $subject, $body, $headers); */ } // form a proper validation response to be assigned to a element // validation response to the field is an array of following: // "isValid" - status // "validContent" - optionaly filtered content // "validationMessage" - message for the user // Note that both valid and non-valid messages can be supplied. // This array format is a convention between both serverside methods // and clientside JS methods. function setElementValidationResult($status, $validContent = null, $validationMessage = null) { $result['isValid'] = $status; if ($status && $validContent) $result['validContent'] = $validContent; if ($validationMessage) $result['validationMessage'] = $validationMessage; return $result; } // validate the Captcha function ValidateCaptchaCode($code) { global $ContactCaptcha; // We want to check if the user already solved the Captcha for this message $isHuman = $ContactCaptcha->IsSolved; if (!$isHuman) { // Validate the captcha // Both the user entered $code and $instanceId are used. global $request; if (array_key_exists('CaptchaInstanceId', $request)) { // ajax validation of Captcha input only $instanceId = $request["CaptchaInstanceId"]; $isHuman = $ContactCaptcha->AjaxValidate($code, $instanceId); } else { // regular full form post validation of all fields $isHuman = $ContactCaptcha->Validate($code); } } if ($isHuman === true) { return setElementValidationResult(true, null, "Solved!"); } else { return setElementValidationResult(false, null, "Please retype the code."); } } // name validation function ValidateName($name) { $name = stripcslashes($name); if (strlen($name) > 2 && strlen($name) < 30) { return setElementValidationResult(true, $name, "Ok!"); } else { return setElementValidationResult(false, $name, "Please enter your name."); }; } // email validaton function ValidateEmail($email) { if (filter_var($email, FILTER_VALIDATE_EMAIL)) { return setElementValidationResult(true, $email, "Ok!"); } else { return setElementValidationResult(false, $email, "This email is not valid."); } } // message validation function ValidateMessage($message) { $message = stripcslashes($message); $headerInjection = preg_match("/(bcc:|cc:|content\-type:)/i", $message); if (strlen($message) > 2 && strlen($message) < 255 && !$headerInjection) { return setElementValidationResult(true, $message, "Ok!"); } else { return setElementValidationResult(false, $message, "Please enter your message."); } } // remember user input if validation fails function getValue($fieldName) { $value = ''; if (isset($_REQUEST[$fieldName])) { $value = $_REQUEST[$fieldName]; } return $value; } // Validation message helper function used in HTML // the validation result is stored in a global array function showValidationMessage($element) { global $validationResult; $message = ""; $messageClass[] = "validatorMessage"; if (is_array($validationResult) && array_key_exists($element, $validationResult)) { $elementStatus = $validationResult[$element]; if ($elementStatus['isValid'] == false) { $messageClass[] = "incorrect"; } elseif ($elementStatus['isValid'] == true) { $messageClass[] = "correct"; } if (array_key_exists('validationMessage', $elementStatus) && $elementStatus[ 'validationMessage'] != null) { $message = $elementStatus['validationMessage']; } } $validator = $element . "ValidatorMessage"; $messageClass = implode(" ", $messageClass); $messageHtml = '<span class="' . $messageClass . '" id="' . $validator . '">' . $message . '</span>'; return $messageHtml; } ?>
There are a few parts to the server side functionality: the form processing and response part, validation methods and some methods to output validation methods.
The form processing is made dynamically, it provides a some general infrastructure for forms to lessen the amount of code redundancy. The result is that additional or different form fields only require adding a validation method in a correct naming scheme: Validate[Fieldname]()
. Also, this code handles differentiating POST and AJAX requests with form data.
Validation methods are straightforward, with the returning type being an array of validation state and response messages, supplied by the setElementValidationResult()
.
The captcha validation method, ValidateCaptchaCode()
, operates on the field CaptchaCode and uses the existing $ContactCaptcha object created in index.php. The validation is performed by supplying the user entered data and an instance ID to the Validate()
method on that object.
Printing errors is handled by the function showValidationMessage()
which is used throughout index.php
where specific field errors should be shown. This method works with the array arrangement that setElementValidationResult()
provides.
Finally, the contact form data is sent via sendMessage()
method.
js/validation.js
// Define the name of the Captcha field. // It serves to access BotDetect Captcha client-side API later. // https://captcha.com/doc/php/api/captcha-client-side-reference.html var captchaUserInputId = "CaptchaCode"; $(document).ready(function() { // AJAX argument is added to differentiate from regular POST. var validationUrl = "index.php?AJAX=1" // Collect form elements we want to handle. var formElements = $('#contactForm input, #contactForm textarea'); var form = $('#contactForm'); formElements.blur( function(){ var postData = {}; // Additional check to skip over empty fields // This igores non-relevant triggering of onBlur if (this.value != ''){ postData[this.id] = this.value; } if(this.id == captchaUserInputId){ // In case of our Captcha field, we also send the InstanceId captchaUserInputField = $('#' + captchaUserInputId).get(0); postData["CaptchaInstanceId"] = captchaUserInputField.Captcha.InstanceId; } if(this.id == "SubmitButton"){ return false; } $.post(validationUrl, postData, postValidation); } ); form.submit( function(){ var postData = {} formElements.each( function(){ postData[this.id] = this.value; } ); $.post(validationUrl, postData, postValidation); return false; } ); }); function postValidation(data){ if (data[captchaUserInputId]){ // Get the Captcha instance, as per client side API captcha = $('#' + captchaUserInputId).get(0).Captcha; if(!data[captchaUserInputId]["isValid"]){ // We want to get another image if the Captcha validation failed. // User gets one try per image. captcha.ReloadImage(); } } if (data["Form"] && data["Form"]["isValid"]){ $("#SubmitButton").attr("disabled", "disabled"); } updateValidatorMessages(data); } // Handling the display of validation messages function updateValidatorMessages(data){ for(var elementKey in data){ validatedElement = data[elementKey]; var elementValidatorMessage = $("#" + elementKey + "ValidatorMessage"); if(validatedElement.hasOwnProperty("validationMessage")){ elementValidatorMessage.text(validatedElement["validationMessage"]); }else{ elementValidatorMessage.empty(); } if(validatedElement["isValid"]){ elementValidatorMessage.toggleClass("correct", true); elementValidatorMessage.toggleClass("incorrect", false); }else{ elementValidatorMessage.toggleClass("correct", false); elementValidatorMessage.toggleClass("incorrect", true); } } }
User entered data for each field is sent to the server on field blur (lost focus) and on form submit. The server processes each field with the corresponding validate method and returns an array of results.
When sending the Captcha field, an exception is made. Along with the user entered code, Captcha instance Id needs to be sent along, so the server-side validation can properly validate.
Please Note
The information on this page is out of date and applies to a deprecated version of BotDetect™ CAPTCHA (v3.0).
An up-to-date equivalent page for the latest BotDetect Captcha release (v4) is BotDetect v4 Captcha documentation index.
General information about the major improvements in the current BotDetect release can be found at the What's New in BotDetect v4.0 page.
Current BotDetect Versions
-
BotDetect ASP.NET CAPTCHA
2019-07-22v4.4.2 -
BotDetect Java CAPTCHA
2019-07-22v4.0.Beta3.7 -
BotDetect PHP CAPTCHA
2019-07-22v4.2.5