I don’t like CAPTCHAs. I don’t know anyone who does. But most forms need some sort of protection against spam, especially where heavyweight spam detection services (e.g. Akismet) aren’t suitable. The downfalls of CAPTCHAs are many – hard to read, annoying, impossible for those with vision difficulties – and the benefits are slim. So, a few months ago I wrote a little function (and I do mean little – like 10 lines of code) to generate a random math question to ask the user in plain text instead of a CAPTCHA. The idea was that a bot wouldn’t be able to answer it, since it requires some human logic. And you know what? It worked – spam went way down on our websites.

So, fast forward a few months and now I’m writing a CakePHP CMS, and I’ve decided to turn my ‘math captcha’ function into a Component. So here it is: my Math Captcha Component. This component generates a random equation and registers the answer as a session variable. The programmer can then check the form submitter’s answer against that registered answer using the validation function provided in the component.

Updated – January 19/2010: Download the Component from Github

Usage is really simple – I’ll run through putting it into an equally simple contact form, which looks a lot like the one done by Jonathan Snook. The Contact model looks like this (app/models/contact.php):

<?php
class Contact extends AppModel {
var $name = 'Contact';
var $useTable = false;

var $_schema = array(
'name'		=>array('type' => 'string', 'length' => 100),
'email'		=>array('type' => 'string', 'length' => 255),
'comments'	=>array('type' => 'text')
);

var $validate = array(
'name' => array(
'rule'=>array('minLength', 1),
'message'=>'Please enter a name so the Geek know what to call you!' ),
'email' => array(
'rule'=>'email',
'message'=>'Please enter an email address so the Geek knows how to reach you.' ),
'details' => array(
'rule'=>array('minLength', 1),
'message'=> 'Don\'t forget to enter some comments.' )
);
}
?>

No DB; manual schema; just a placeholder, really.

The Contact controller is set up like this (app/controllers/contact_controller.php):

<?php
class ContactController extends AppController {
var $name = 'Contact';
var $uses = 'Contact';
var $components = array('RequestHandler', 'Email', 'Session', 'MathCaptcha');

function index() {
if ($this->RequestHandler->isPost()) {
$this->Contact->set($this->data);
if ($this->MathCaptcha->validates($this->data['Contact']['security_code'])) {
if ($this->Contact->validates()) {
$this->Email->to = Configure::read('SiteSettings.email_form_address');
$this->Email->subject = 'Contact from message from ' . $this->data['Contact']['name'];
$this->Email->from = $this->data['Contact']['email'];

$this->Email->send($this->data['Contact']['comments']);
}
} else {
$this->Session->setFlash(__('Please enter the correct answer to the math question.', true));
}
}

$this->set('mathCaptcha', $this->MathCaptcha->generateEquation());
}
}
?>

So, we’ve added MathCaptcha to our list of components. There are various configuration options which you can set when adding MathCaptcha to the $components array – the config array (with defaults) looks like this:

private $__defaults = array(
'operand' =&amp;amp;amp;gt; '+',
'minNumber' =&amp;amp;amp;gt; 1,
'maxNumber' =&amp;amp;amp;gt; 5,
'numberOfVariables' =&amp;amp;amp;gt; 2
);

In the index() method, you can see the usage: if we’ve got a POST request, we call the component’s validates() method and pass to it the relevant data from the form – the user’s answer to the question. If it validates then we continue with the rest of the data validation, otherwise we give an error message. You’ll notice that the generateEquation() method is called regardless; we want a new question generated each time the page loads.

Finally, we just need one line in the view to grab the ‘security_code’. Here’s the entire contact form (app/views/contact/index.ctp):

<?php
echo $form->create('Contact', array('url' => $this->here));
echo $form->input('name');
echo $form->input('email');
echo $form->input('comments');
echo $form->input('security_code', array('label' => 'Please Enter the Sum of ' . $mathCaptcha));
echo $form->end(array('name' => 'Send', 'class' => 'input_btn'));
?>

I’ve called the form field ‘security_code’, but you can call it whatever you want.

And that’s it! A plain text math ‘captcha’ in almost no time.