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
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
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
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
10th Mar, 2009
You're welcome!
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
22nd Apr, 2010
very very helpful article.
25th Jun, 2010
You saved my brain :) thank you.
26th Jun, 2010
Thank you so very much, this was a life saver....
3rd Dec, 2010
This is very helpful. thanks
Add Comment