PHP Form CAPTCHA Code Sample (BotDetect v3.0; deprecated)

The PHP Form Captcha code sample shows how to add BotDetect CAPTCHA protection to a typical PHP form.

First Time Here?

Check the BotDetect PHP Captcha Quickstart for key integration steps.

Captcha validation is integrated with other form fields validation, and only submissions that meet all validation criteria are accepted.

If the Captcha is successfully solved but other field validation fails, the Captcha is hidden since the users have already proven they are human.

This kind of validation could be used on various types of public forms which accept messages, and are at risk of unwanted automated submissions.

For example, it could be used to ensure bots can't submit anything to a contact form, add guestbook entries, blog post comments or anonymous message board / forum replies.

Downloaded Location

The PHP form Captcha code sample is included in the samples/php_form_captcha_sample folder of the download package.

index.php

<?php session_start(); ?>
<?php require("botdetect.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>BotDetect PHP CAPTCHA Form Sample</title>
  <link type="text/css" rel="Stylesheet" href="StyleSheet.css" />
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <link type="text/css" rel="Stylesheet" 
    href="<?php echo CaptchaUrls::LayoutStylesheetUrl() ?>" />
</head>
<body>
  <form method="post" action="process_form.php" id="form1">

    <h1>BotDetect PHP CAPTCHA Form Sample</h1>
    
    <fieldset>
      <legend>CAPTCHA included in PHP form validation</legend>
      
      <div class="input">
        <label for="Name">Name:</label>
        <input type="text" name="Name" id="Name" class="textbox" 
          value="<?php echo getValue('Name');?>" />
        <?php echo getValidationStatus('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 echo getValidationStatus('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>
        <?php echo getValidationStatus('Message'); ?>
      </div>
      
      
      <div class="input">
        <?php // Adding BotDetect Captcha to the page 
          $FormCaptcha = new Captcha("FormCaptcha");
          $FormCaptcha->UserInputID = "CaptchaCode";
          $FormCaptcha->CodeLength = 3;
          $FormCaptcha->ImageWidth = 150;
          $FormCaptcha->ImageStyle = ImageStyle::Graffiti2;
          
          // only show the Captcha if it hasn't been already solved 
          for the current message
          if(!$FormCaptcha->IsSolved) { ?>
            <label for="CaptchaCode">Retype the characters from the 
              picture:</label>
            <?php echo $FormCaptcha->Html(); ?>
            <input type="text" name="CaptchaCode" id="CaptchaCode" 
              class="textbox" />
            <?php echo getValidationStatus('CaptchaCode');
          }
        ?>
      </div>
      
      <input type="submit" name="SubmitButton" id="SubmitButton" 
        value="Submit"  />
        
    </fieldset>
    
  <?php 
    // remember user input if validation fails
    function getValue($fieldName) {
      $value = '';
      if (isset($_REQUEST[$fieldName])) { 
        $value = $_REQUEST[$fieldName];
      }
      return $value;
    }
    
    // server-side validation status helper function
    function getValidationStatus($fieldName) {
      // validation status param, e.g. "NameValid" from "Name"
      $requestParam = $fieldName . 'Valid';
      if ((isset($_REQUEST[$requestParam]) && $_REQUEST[$requestParam] == 0)) {
        // server-side field validation failed, show error indicator
        $messageHtml = "<label class='incorrect' for='{$fieldName}'>*</label>";
      } else {
        // server-side field validation passed, no message shown
        $messageHtml = '';
      }
      return $messageHtml;
    }
  ?>
    
  </form>
</body>
</html>

Beside form processing code.

We also add simple helper functions that check server-side validation status of each field (getValidationStatus()) and perserve user input if form validation fails (getValue()).

Note that we aren't perserving the Captcha code, which will always change upon failed validation so it makes no sense to remember the value. To hide the Captcha challenge after the user solves it (regardless of other field validation status), the Captcha object's Html value is only added to the page if not already solved (if(!$FormCaptcha->IsSolved) { ...).

process_form.php

<?php 

  session_start();
  require("botdetect.php");


  $form_page = "index.php";
  $view_page = "messages.php";
  
  // directly accessing this script is an error
  if (!$_SERVER['REQUEST_METHOD'] == "POST") {
    header("Location: ${form_page}");
    exit;
  }

  // submitted data
  $name = $_REQUEST['Name'];
  $email = $_REQUEST['Email'];
  $message = $_REQUEST['Message'];
  $form_page = $form_page . "?Name=" . urlencode($name) . "&Email=" . 
    urlencode($email) . "&Message=" . urlencode($message);

  // total form validation result
  $isPageValid = true;

  
  // Captcha validation 
  $FormCaptcha = new Captcha("FormCaptcha");
  $FormCaptcha->UserInputID = "CaptchaCode";
  if (!$FormCaptcha->IsSolved) {
    $isHuman = $FormCaptcha->Validate();
    $isPageValid = $isPageValid && $isHuman;
    $form_page = $form_page . "&captchaCodeValid=" . $isHuman;
  }
  
  // name validation
  $isNameValid = ValidateName($name);
  $isPageValid = $isPageValid && $isNameValid;
  $form_page = $form_page . "&nameValid=" . $isNameValid;
  
  // email validation
  $isEmailValid = ValidateEmail($email);
  $isPageValid = $isPageValid && $isEmailValid;
  $form_page = $form_page . "&emailValid=" . $isEmailValid;
  
  // message validation
  $isMessageValid = ValidateMessage($message);
  $isPageValid = $isPageValid && $isMessageValid;
  $form_page = $form_page . "&messageValid=" . $isMessageValid;
  
  if (!$isPageValid) { 
    // form validation failed, show error message
    header("Location: ${form_page}");
    exit;
  }
  
  // keep a collection of submitted valid messages in Session state
  SaveMessage($name, $email, $message);
  $FormCaptcha->Reset(); // each message requires a new Captcha 
  // challenge
  header("Location: ${view_page}");
  exit;
  
  
  // name validation
  function ValidateName($name) {
    $result = false;
    if (strlen($name) > 2 && strlen($name) < 30) {
      $result = true;
    }
    return $result;
  }
  
  // email validaton
  function ValidateEmail($email) {
    $result = false;
    if (strlen($email) < 5 || strlen($email) > 100) {
      $result = false;
    } else {
      $result = (1 == preg_match('/^(.+)@(.+)\.(.+)$/', $email));
    }
    return $result;
  }
  
  // message validation
  function ValidateMessage($message) {
    $result = false;
    if (strlen($message) > 2 && strlen($message) < 255) {
      $result = true;
    }
    return $result;
  }
  
  // data storage
  function SaveMessage($name, $email, $message) {
    // we want to keep the sample code simple, so we'll store the 
    // messages in Session state despite it being unfit for real-world 
    // use in such scenarios;
    // using a database or another appropriate persistence medium 
    // would complicate the sample code
    $_SESSION['Message_' . strtolower(md5(uniqid(mt_rand(), true)))] = 
      htmlentities($name) . ' (' . htmlentities($email) . ') says: ' . 
      htmlentities($message);
  }
  
?>

Form submission validation is performed in this file, which checks all required fields and redirects the user back to the form if validation fails. We use simple querystring parameters to pass validation results and remembered field values back to the form page (since this sample warrants simplicity over real-world validation best practices).

Captcha validation is treated no different than other field validation, and will work as long as we initialize the object with the same CaptchaId ("FormCaptcha") and UserInputID ("CaptchaCode") we used on the form page to add the Captcha.

Once form validation succeeds, the submitted values are saved and the Captcha challenge is reset ($FormCaptcha->Reset()). This means each individual message requires Captcha validation, which helps prevent attacks where a malicious user could solve the Captcha once and then automate further form posts within the same Session.

messages.php

<?php session_start(); ?>
<!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>BotDetect CAPTCHA PHP Form Sample</title>
  <link type="text/css" rel="Stylesheet" href="StyleSheet.css" />
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>

  <h1>BotDetect CAPTCHA PHP Form Sample</h1>
  
  <h2>View Messages</h2>

  <?php
    $count = 0;
    foreach($_SESSION as $key => $val) {
      if (false !== strpos($key, "Message_") && isset($val)) {
        echo "<p class='message'>${val}</p>";
        $count++;
      }
    }
    
    if ($count == 0) {
      echo '<p class="message">No messages yet.</p>';
    }
  ?>
  
  <br />
  
  <p class="navigation"><a href="index.php">Add Message</a></p>
    
</body>
</html>

This page displays all successfully submitted messages, and the user is automatically redirected here after validation of all form fields (including Captcha) is passed.


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.