Sankho Mallik . com

Tutorial: Using Zend_Captcha_Image

by Sankho on Dec.17, 2008, under Tutorials, Zend Framework

Update - check out the post at AmpTools, if you’re using a copy of Zend Framework version 1.7.8 or above - the below code may not work for you.

—–

Recently on a project I was developing using the Zend Framework, I needed to validate a form using a CAPTCHA image.

CAPTCHA example image

CAPTCHA example image

For those who don’t know, CAPTCHA stands for Completely Automated Public Turing test to tell Computers and Humans Apart. Ever been to a website and seen an image like the one to the right? Basically, it’s a way to make sure a human is filling out the form and not just some robot. How? Robots can’t decifer what words *might* be from an image - only a human can.

Zend Framework has a set of great classes included to take away a lot of the headaches involved with setting up a CAPTCHA system. The one I use is Zend_Captcha_Image, which creates an image for you and sets up a Session so you will be able to validate all information appropriately. Problem is, there’s little documentation out there on this.

The best example I found was on Robert Basic’s Blog. My only gripe about it was that it used Zend_Form.

Zend_Form is a great class and all, but if you’re like me, you don’t like using it because you give up the control you get over Form creation + validation. It’s a great way to skip writing HTML for a form - but what if your client wants a very specific design attached to their form? Then Zend_Form becomes a hassle to design to a specification. And yes, Zend_Form makes validation easy, but what if your client wants to set up very specific validation messages? Again, Zend_Form becomes more trouble than it’s worth. Yes, it is flexible enough as a class to edit it any way you want, but I feel NOT using Zend_Form will ultimately make your forms more flexible.

So, beyond the break, I’ll show you everything you need to know to use Zend_Captcha_Image WITHOUT Zend_Form! If you’d like to learn the implementation using Zend_Form, please visit Robert Basic’s Blog.

Here’s a list of what we need to do to get our instance of Zend_Captcha_Image up and running.

  1. Make sure we have GD installed.
  2. Make a new folder in our web directory with 777 permissions. This will store the generated CAPTCHA images.
  3. Write generateCaptcha() function
  4. Write validateCaptcha() function
  5. Show a quick example, bask in Zend Framework aided CAPTCHA awesomeness, and stop fearing robots until the Matrix comes true

1.) Make sure we have GD installed.

The first thing you need to be sure of is that you have GD running on your copy of PHP. This is pretty easy. Just run this code on any PHP page to find out:

  1. if( function_exists("gd_info") ) {
  2.   echo ‘yes’;
  3. } else {
  4.   echo ‘no’;
  5. }

I should hope this is pretty self-explanatory. If you don’t have GD installed, contact your hosting provider for more info. More on GD installation later should future comments find them necessary!

Anyways, hopefully you have the GD library installed. It’s an image manipulation library very commonly used by PHP programmers to make images & thumbnails “on-the-fly”, or in the background unbenknownst to your user.

2.) Make CAPTCHA images folder

So you have GD installed, and you’re good to go. Before we can get to the code, you need to create a folder for the captcha images within your public/ directory and chmod it to 777 (all file permissions open).

This should be pretty simple, and to clarify, by your public/ directory I of course mean whatever folder your server is pointing to. This is so the captcha images can be accessible by HTTP request, for example through the folder http://www.your-site.com/images/captcha/

I hope you’re deep enough in the game for that to make sense. After you’ve made that folder, take note of it, for you’ll use it in step 3.

So now what? Let’s generate the two functions that will serve as the base for our Captcha code. One is called generateCaptcha() and the other is called validateCaptcha(). Let’s look over generateCaptcha() first.

3.) Write generateCaptcha() function

  1. //generates an instance of Zend_Captcha
  2. //returns ID of captcha session
  3. function generateCaptcha() {
  4.  
  5.   $captcha = new Zend_Captcha_Image();
  6.  
  7.   $captcha->setTimeout(‘300′)
  8.               ->setWordLen(‘6′)
  9.               ->setHeight(‘80′)
  10.               ->setFont(‘/path/to/your/fontFile.ttf’)
  11.               ->setImgDir(‘/path/to/your/image/captchaDirectory’);  
  12.  
  13.   $captcha->generate();    //command to generate session + create image
  14.  
  15.   return $captcha->getId();   //returns the ID given to session & image
  16.  
  17. }   //end function generateCaptcha

So, let’s go over this function. First we instantiated a new Object of class Zend_Captcha_Image. Then we used it’s functions to feed it some basic information. $captcha->setTimeout(’300′) sets the time the session will stay active, in seconds. So, upon loading the form, the user has 5 minutes to respond. ->setWordLen(’6′), pretty obviously sets how many characters are used in the CAPTCHA image. 6 is pretty decent, as shown in the example above. ->setHeight(’80′) sets the heigh of the image. The default may be good for you; I only chose it because my font file was a bit big.

->setFont(’/path/to/your/fontFile.ttf’) points to which font you want to use for the image. I’d recommend a san serif font, as others may be too hard for humans to read. Do a quick google search for .ttf files, you should find one online without too much trouble. Contact me if you want one that I used though!

Finally, ->setImgDir(’/path/to/your/image/captchaDirectory’) should point to the directory you created in step 2.

Let’s talk abit about what the Zend Framework is doing for us with the generate(); command. Basically, it makes a random string of numbers and lower case letters and makes an image out of this using your font and some GD trickery/artistry to make it look funny, and more importantly - (even more) non robot readable. Then, it stores all the information in a session called up by…

4.) Write validateCaptcha() function

  1. //validates captcha response
  2. function validateCaptcha($captcha) {
  3.                 $captchaId = $captcha[‘id’];
  4.                 $captchaInput = $captcha[‘input’];
  5.                 $captchaSession = new Zend_Session_Namespace(‘Zend_Form_Captcha_’ . $captchaId);
  6.                 $captchaIterator = $captchaSession->getIterator();
  7.                 $captchaWord = $captchaIterator[‘word’];
  8.                 if( $catchaWord ) {
  9.                      if( $captchaInput != $captchaWord ){
  10.                           return false;
  11.                      } else {
  12.                           return true;
  13.                      }
  14.                 } else {
  15.                      return false;
  16.                 }
  17. }

vaidateCaptcha() is pretty simple as well. First, you get data sent by the form through the function in $captcha, an array you set up in the controller later on. It contains 2 pieces of information, - the CAPTCHA session’s id, and what the user (hopefully human!) entered into the form. Using the id, it accesses the session, and gets the correct word. The word is compared to what the input entered, and the function returns appropriately.

5.) This is where you make it clap

Here’s a quick example of what the code would look like in the backend + frontend. I’ll write the code using Zend’s MVC standards if someone requests, but I think you guys can figure this out.

  1. //setup CAPTCHA
  2. $captchaId = generateCaptcha();        //generates CAPTCHA image and session, returns session’s ID
  3.  
  4. if( isset($_POST[‘captcha’]) ) {       //if a post value is sent, process form
  5.   $captcha = $_POST[‘captcha’];     //get array sent in $_POST form
  6.   if( validateCaptcha($captcha) ) {
  7.       //if a true value is sent, process form
  8.   } else {
  9.      //captcha invalid, text doesn’t match
  10.   }
  11. }

And now the frontend.

  1. <!–?=$_SERVER[‘PHP_SELF’] ?–><form action="<?=$_SERVER['PHP_SELF'] ?>" method="POST">
  2.   <img src="/your/captchaDirectory/<!–?=$captchaId ?–><?=$captchaId ?>.png" alt=""/><br />
  3.   What’s the word above say? <input type="text" name="captcha[input]" />
  4.  <input type="hidden" value="<!–?=$captchaId ?–><?=$captchaId ?>" name="captcha[id]"/>
  5.  <input type="submit" value="test me"/>
  6. </form>

And that’s it! Hopefully this is useful to someone on the internet!

Share and Enjoy:
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
:, , , , ,
41 comments for this entry:
  1. Robert

    Hi mate!

    Thanks for the pingback ;) I’ll update my example to link back to yours, if someone wants it this way :)

    Until then, I submitted your post to dzone.com: http://www.dzone.com/links/tutorial_using_zendcaptchaimage.html

    Cheers!

  2. A Zend_Captcha example ~ Robert Basic

    [...] #2: Here’s an example of using Zend_Captcha without the whole Zend Framework [...]

  3. samuele

    Realy useful tutorial

  4. Jani Hartikainen

    I recently needed a quick captcha generation code for a project, so I did pretty much what you’re doing here.

    However, the problems began when I didn’t want to use Zend_Session. There just was absolutely no way it would work without Zend_Session, so I just ended up gutting Zend_Captcha_Image and Zend_Captcha_Word into a single class with everything related to Zend_Session removed.

    Tricky. Hope they add a way to generate captchas without those dependencies.

  5. Sankho

    Jani - thanks for commenting!

    I looked into what you said, and you don’t necessarily have to use Zend_Session. Instead you can access all the necessary info through the $_SESSION variable.

    You just need the captchaId given in the $_POST to get to it. Try:

    $sessionName = ‘Zend_Form_Captcha_’ . $captchaId;
    $captchaArray = $_SESSION[$sessionName];
    $realWord = $captchaArray['word'];

    The Zend Framework names the captcha array simply “Zend_Form_Captcha_” + the captcha Id given upon initial generation.

    This seemed to work for me, and I didn’t have to call an instance of Zend_Session_Namespace that way.

    BTW, the way I figured out what the Session was called was by running a quick var_dump($_SESSION) and taking a peek at the array!

    Hope this helps!

  6. Использование Zend_Captcha | Zend Framework по-русски

    [...] Zend_Captcha без связки с Zend_Form, вам будет полезна статья Using Zend_Captcha_Image, опубликованная в блоге Sankho [...]

  7. [Web] 連結分享 « 網站製作學習誌

    [...] Tutorial: Using Zend_Captcha_Image [...]

  8. Php Echo _session

    I found your site on faves.com bookmarking site.. I like it ..gave it a fave for you..ill be checking back later

  9. Dan

    What I did for the MVC part was use baseUrl(); ?>/images/captcha/captchaId ?>.png for the image url (having already set baseUrl). And then of course sending the image to the view with $this->view->captchaId

    Thanks for your help!

  10. Navdeep Singh

    Hi….
    Its a very good example, and running good on my local server, but i m facing these errors, while runing it on line….
    can i get some help???

    Warning: imageftbbox() [function.imageftbbox]: Could not find/open font in /home/.lujak/abc/abc.com/project/library/Zend/Captcha/Image.php on line 483

    Warning: imagefttext() [function.imagefttext]: Could not find/open font in /home/.lujak/abc/abc.com/project/library/Zend/Captcha/Image.php on line 486

    Warning: imagepng() [function.imagepng]: Unable to open ‘/sharanbrar.com/zumlink/public/images/captcha/2aff183d0435671b6974ea492d77d037.png’ for writing: No such file or directory in /home/.lujak/abc/abc.com/project/library/Zend/Captcha/Image.php on line 557

  11. Sankho

    Navdeep,

    It would appear to me that you don’t have the GD library installed.

    Did you run phpinfo() on a test php page? If a section about the GD image library didn’t come up, or it’s clearly indicated as not being installed, you need to install the GD library.

    It’s not too hard; takes some editing of your php.ini file, hopefully you won’t have to install the actual package files yourself. They are usually installed in most copies of PHP these days.

    Do a quick google search for the how-to’s; but if you can’t find any I’ll write a post about it, just comment again.

    Hope that helps!

  12. jan

    Hi Navdeep Singh , Sankho

    I was having same problems, it is not GD think it is in setting correct directory path with DOT… I found this in zend manual, you must put dot before path…

    ./images/captcha/

    setImgDir($imgDir) and getImgDir() allow you to specify the directory in which captcha images are stored. This defaults to “./images/captcha/”, which should look relative to the bootstrap script.

    thanks alot for tutorial!
    Jan from cz

  13. medelups

    Благодарю!!!У Вас часто появляются очень интересные посты! Очень поднимаете мое настроение.

  14. SOKOFFISCOWL

    вы шутите…21 век на дворе, неужели нет ничего достойного внимания, как энциклопедия.Милые мои, вот нет снега в
    гордах, это тоже тема и история, пересмотрите темы.Я почту просматриваю, мне шлют не пойми что, не знаю кто, столько мусора, может оно и нужно, но не в дневнике.Я так понимаю, дневник это часть твоей души.Нам дается право выбирать - пользуйтесь. А информация бесполезной не бывает

  15. Playmn

    У вас RSS в кривой кодировке!

  16. Anycleeffede

    Очень понравилось, даже не ожидала.

  17. Toingeoffite

    А Вы не задумывались о том, чтобы параллельно завести еще один блог, на смежную тему? У Вас неплохо получается

  18. mr925

    Delete captcha images generated affer validate

  19. heermafaro

    Опубликовал на своем блоге вашу статью, и напечатол там конечно-же обратную ссылку на вас. Но вот зашел посмотреть поевился ли трекбек, а его нет…

  20. Drealiabibra

    Неплохой пост, но много лишнего.

  21. Dardgaib

    Очень интересно!!! Только не очень могу понять как часто обновляется ваш блог?

  22. Vofanigo

    Классная статья, кстати автору хочу предложить установить от яндекс.денег фишку на сайт “Дай рубль”. Я бы дал, так сказать на поддержание.

  23. Gainibum

    Ваш пост навел меня на думки *ушел много думать* …

  24. snusly

    Опутеть как интересно, во задвигаете. Класс!

  25. expaft

    Неплохой пост, но много лишнего.

  26. Sankho

    Great suggestion mr925; when I have some time I’ll update the post to include this.

    How would you go about it, and where?

  27. lepelle

    Thanks!
    this was most helpful. Me too, I do not really like Zend_Form. As we say in Sweden, most of the time it is “crossing the river to get a bucket of water”.

    Regarding the deletion of pngs, question raised by mr925, I guess Zend_Captcha has a garbage collection built in.
    But if you go for your own deletion, I think the best would be to run garbage collection. Deleting after validation will only catch the cases who came all the way to validation.

  28. Heibecrell

    Опубликовал на своем блоге вашу статью, и напечатол там конечно-же обратную ссылку на вас. Но вот зашел посмотреть поевился ли трекбек, а его нет…

  29. BeginZend

    The Zend tutorial was usefull. But is it possible that you can send me the MVC standards. I’m sorta struggling over here with the arrangement. Hope you can help me out. thanks

  30. Santyago

    Спасибо! У Вас часто появляются очень интересные посты! Однако я бы убрал пару тройку фраз.

  31. Neller

    Hi, great tutorial.

    I have everything working fine however I’m wondering how secure this way is?

    For example if someone edited the $_POST variable before it hits the website and removes 1 of the inputs (for example - name=”captcha[id]“)

    Then when it hits the server it triggers (Undefined index: input ) and the captcha validates even if the codes dont match due to the error .

    Hope that makes sense.

  32. annombpreepay

    проба пера

  33. Kuban

    Thank you! Very informative article!

  34. Andrey

    Thanks for your help!

  35. sankho

    Neller, I believe I understand what you are saying, and there’s a fairly easy fix to this - I should of realized this possibility earlier. Thanks for commenting!

    In line 5 of my example function validateCaptcha, we instantiate the PHP session created by the captcha on your server earlier, in the previous generateCaptcha function. To access the correct PHP session, we need the $captcha array sent through the POST - but you took it away.

    Since we can’t access the session, we can’t get the correct word, and the variable representing this - $captchaWord - ultimately ends up with a null value.

    The value for $captchaInput also becomes null. Yep - it’s because it was held in the removed $captcha array.

    So then, in the last step of the function, it checks if $captchaInput and $captchaWord are equal - and they are. They’ve both become null.

    I’m rewriting that step with a quick fix for this. If the session isn’t found, the function returns false. This should lock down the Captcha. Test it out and let me know!

  36. sankho

    Johnny,

    I’ll try to write an article on developing under an MVC environment soon.

    Basically, it’s largely a custodial thing. But if you stay with an app for log enough, MVC becomes more valuable as your site starts to requires consistent change. And of course, change is a consistent thing for the web.

    It’s also great if you need others to edit your code later on; it decreases the learning curve required to get comfortable with someone else’s code.

    I’ll try and shoot you an email when I get around to writing this article. Thanks for the comment and request!

  37. kumar

    Warning: imageftbbox() [function.imageftbbox]: Invalid font filename in C:\wamp\www\tribarter\library\Zend\Captcha\Image.php on line 483

    Warning: imagefttext() [function.imagefttext]: Invalid font filename in C:\wamp\www\tribarter\library\Zend\Captcha\Image.php on line 486

    Warning: imagepng() [function.imagepng]: Unable to open ‘images/captcha/604ac905d0fff65dbb081c97b9ece8b7.png’ for writing: No such file or directory in C:\wamp\www\tribarter\library\Zend\Captcha\Image.php on line 557

  38. kumar

    getting these errors

  39. sankho

    Kumar,

    Those errors are occurring because you haven’t downloaded a .ttf font file for the Captcha generator to use, and you haven’t made a directory for the images to be created and visible to the public. When I wrote in “/path/to/your/fontFile.ttf” I literally meant the path to YOUR font file - not mine. That’s some code you need to write in yourself.

    So go download a .ttf file somewhere, and place it in a directory on your server, then point to that file in the code. Then, create a folder underneath your site’s root directory and chmod it to 775 so it’s writeable by the server + others can view it.

    Hope this makes sense… pretty standard stuff you’re gonna have to learn how to do if you haven’t yet.

  40. Zend Framework Captcha | amptools

    [...] posts, I don’t think google is catching them. I did find some useful code on this blog, sankhomalik’s tutorial on using the zend image captcha. It does make some decent notes, so please go read this post first (and thank him, cause so far he [...]

Leave a Reply

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Visit our friends!

A few highly recommended friends...

Archives

All entries, chronologically...