Experiences with Zend_Captcha_Image

Posted

Yesterday I had to add a CAPTCHA to a form on a site already using some ZF components, which gave me a chance to try out the CAPTCHA component in ZF.

As well as the usual obfuscated image, Zend_Captcha supports figlet (ASCII words) and reCAPTCHA (a 3rd party CAPTCHA service to help transcribing old books). Although I love the idea of reCAPTCHA, I've always thought its standard input box looks a bit like a banner advert (and thus is easy to miss), so I went with the familiar squiggly image (Zend_Captcha_Image).

Most of the standalone CAPTCHA scripts I've used in the past work as follows: You include an image tag in your HTML which points at a PHP script. This script generates an image on the fly (and shows it), as well as storing the generated word in a session. You then include a chunk of code in your form processing script that checks the submitted data against the session word.

The ZF component works slightly differently, in that it generates an actual image file (stored on the file system), and then outputs an image tag that points at this. The generated word is stored in the session (using Zend_Session), and the object has a method for comparing the two when the form is submitted.

Although not a requirement, the component was obviously designed to be used in conjunction with Zend_Form, and it took some digging around in the code to work out how to use it without. Certainly the code example in the docs is incomplete, so hopefully this article will help anyone trying to do the same.

Firstly the absolute minimum amount of code required to generate (and show) a CAPTCHA image:

<?php
require_once('Zend/Captcha/Image.php');

$captcha = new Zend_Captcha_Image(array(
        'font' => '/path/to/fonts/Example.ttf'
));

$id = $captcha->generate();

require_once('Zend/View.php');
echo $captcha->render(new Zend_View());

this will create a PNG image in the folder images/captcha (which needs to be writeable). There are garbage collection methods that will automatically remove old images after a configurable amount of time. You will need to replace the font path with the path to an actual TTF font file on your system. You can either download a free font (of which there are many on the Web), or point it at a font already installed on your server (check /usr/share/fonts/truetype/ in Ubuntu).

View this script in a browser and you should see an image like this:

so, now you need to wrap this in a form, provide a box for the user to enter their input, and then check this data after a post. And this is where the documentation starts to let it down, since no example of this is provided.

Checking the POST data is done using the function isValid(), which has two parameters - $value and $context. $context is optional, and even after looking at the code I'm not clear what this is for, so I've ignored it. You would assume $value to be the string the user enters, but it actually needs to be an array with two keys: input and id. $value['input'] should be what the user typed into the form, and $value['id'] should be the MD5 hash returned when the CAPTCHA was generated ($id in my code example above).

If you are using this with Zend_Form, the supplied decorator will output form fields that generate this array for you. But if not, here's a working example:

<?php
require_once('Zend/Captcha/Image.php');

$captcha = new Zend_Captcha_Image(array(
        'font' => '/path/to/fonts/Example.ttf'
));

if (!empty($_POST)) {
        if ($captcha->isValid($_POST['captcha'])) {
                echo 'Passed CAPTCHA check';
        }
}

$id = $captcha->generate();
?>

<form method="post" action="">
<?php
require_once('Zend/View.php');
echo $captcha->render(new Zend_View());
?>

<input type="text" name="captcha[input]" />
<input type="hidden" name="captcha[id]" value="<?=$id?>" />

<input type="submit" name="submit" value="Submit" />
</form>

It's important that the isValid() check happens before the generate() (as in the example above), as otherwise you'll be checking against a new CAPTCHA and the check will always fail. Normally you would only run the generate() on non-POST requests, or if the check fails (to give the user a fresh image to try).

A number of other options can be supplied to customise the image, e.g.:

<?php
$captcha = new Zend_Captcha_Image(array(
        'wordLen' => 5,
        'font' => '/path/to/fonts/Example.ttf',
        'imgDir' => './captcha/',
        'imgUrl' => '/captcha/',
        'width' => 150,
        'height' => 55,
        'dotNoiseLevel' => 40,
        'lineNoiseLevel' => 3
));

wordLen controls the number of characters in the image (default is 8). imgDir and imgUrl control where the image files are stored. width and height affect the dimensions of the generated image. dotNoiseLevel and lineNoiseLevel control the number of dots and lines added.

Comments (9) Tags: captcha, zend framework

Comments

deepak
27th Jan, 2009

Hi,

Can you also show me some example on how to use the same thing using ZendFrom. I am having trouble showing the image using ZendFrom but I can see that the image is generated every time the form is rendered.

Thanks

Anees
16th Feb, 2009

Hi,
Thank you for sharing your experience about Zend_captcha

I am trying to implement the same using AJAX
but it is getting failed because i couldnt assign the id into Captcha array.
If you have any idea please help

Regards
Anees

axeff
10th Mar, 2009

"It's important that the isValid() check happens before the generate() (as in the example above), as otherwise you'll be checking against a new CAPTCHA and the check will always fail."

You saved my day!!!

thx for that

Tim Fountain
10th Mar, 2009

You're welcome!

alok
22nd Jun, 2009

it's very helpful but when i am creating form whith zend_form then how to show captcha image.while captch image created in cpatcha/img folder but not display in browser. pls help me

zend framework captcha
22nd Apr, 2010

very very helpful article.

57
25th Jun, 2010

You saved my brain :) thank you.

Ivan
26th Jun, 2010

Thank you so very much, this was a life saver....

captcha zf
3rd Dec, 2010

This is very helpful. thanks

Add Comment

(Never shown on the site)

(Newlines preserved, format with Markdown)