Understanding Zend Captcha

A CAPTCHA image (Completely Automated Public Turing Test to Tell Computers and Humans Apart) is a novel solution to prevent spam. You will find these being used in almost all user registration pages. The most commonly used Captchas are images that look like :

 cap

The logic is quite simple.. Present a randomly generated image to the user and request that it be typed back. After the form is submitted, a check is made whether the text entered by the user matches the actual content of the graphic. If they do match, it is a human!

The rendered graphic should have sufficient noise (lines, dots, curved fonts etc) so that simple OCRs (Optical Character Readers) do not bypass it.

The Zend Framework provides a class named Zend_Captcha_Image that does all the heavy lifting required for captcha implementation. A complete working php file that demonstrates the salient points is listed below (the example has deliberately been kept simple by avoiding MVC/Zend_Form components):

<?php
include_once(“Zend/Captcha/image.php”);
include_once(“Zend/loader.php”);

$captcha=new Zend_Captcha_Image();
$captcha->setWordLen(‘4’)
        ->setHeight(’60’)
        ->setFont(‘arial.ttf’)
        ->setImgDir(‘captcha’)
        ->setDotNoiseLevel(‘5’)
        ->setLineNoiseLevel(‘5’);

//do this loop if form has been submitted!
if (isset($_POST[‘captcha’]))
{
    if ($captcha->isValid($_POST[‘captcha’]))
    {
        echo “Success!”;
        exit(0);
    }
    else
    {
        //captcha invalid.. redisplay..
        echo “Failed!”;
    }
}
//generate a new image on every loop..note that the location of this statement is important..
//if placed before the if isset command, the captcha check will never result in true!

$captchaId=$captcha->generate();

//display the form..
?>

<form action=”<?php echo $_SERVER[‘PHP_SELF’]?>” method=”post”>
    <p>What does the word below say?</p>
    <img src=”captcha/<?php echo $captchaId?>.png” alt=”captcha”>
    <input type=”text” name=”captcha[input]” />
    <input type=”hidden” value=”<?php echo $captchaId?>” name=”captcha[id]” />
    <input type=”submit” value=”Test me” />
</form>

Yeah.. it really is that simple!

The first two lines include the required Zend library files for use.

The next couple of lines instantiate the Zend_Captcha_Image object. Note that only the setFont() call is required.. All others are optional.

$captcha=new Zend_Captcha_Image();
$captcha->setWordLen(‘4’)
        ->setHeight(’60’)
        ->setFont(‘arial.ttf’)
        ->setImgDir(‘captcha’)
        ->setDotNoiseLevel(‘5’)
        ->setLineNoiseLevel(‘5’);

setWordLen : Number of characters in the rendered Captcha
setHeight: The height of the graphic
setFont: The font used to render the Captcha (Click here to download and use the Arial true type font)
setImgDir: The image directory where Zend will create the image files (.png). The webserver should have write permissions to this folder
setDotNoiseLevel: The noise generated by random dots in the Captcha
setLineNoiseLevel: The noise generated by random lines in the Captcha

Feel free to play around with these parameters to see the effect they have on the generated captcha image.

The next few lines of code (executed on form submit) does the check using the inbuilt method isValid() on the Captcha object. This function expects as parameter, an array containing

1. The input typed in by the user – captcha[‘input’]
2. The md5 hash of the generated captcha text that is conveniently returned by the generate() call and passed back as a hidden form variable – captcha[‘id’]

The line

$captchaId=$captcha->generate();

accomplishes quite a lot – It is responsible for the following:
1. Generating the captcha image and storing it in the specified image directory. PHP internally uses the gd graphics library to render the image.
2. Storing the “real value” corresponding to the image in a session variable
3. Returning an MD5 hash of the value that is generated. This hash value is used to name both the generated image file and the session variable (and that is why it needs to be passed into the isValid() method so the correct session variable can be queried)

The captcha image is created and stored in the folder specified by the setImgDir() function. The name follows the convention <md5>.png.. So, displaying the captcha to the user is simply a matter of displaying the generated image using an <img src> tag!

Zend_Captcha_Image class automatically deletes old/expired png files from the image directory (for those who don’t take my word for it, check the function _gc() routine in Zend/Captcha/Image.php.. This function is called after the generate() method – A randomization technique is employed to insure that the system does not spend too much time doing cleanup)

11 thoughts on “Understanding Zend Captcha

  1. Hi

    i hope its ok to ask for help cause i do not understand something. i use this code:

    //create captcha
    $captcha = new Zend_Captcha_Image(array(‘name’ => ‘captchaword’,’wordLen’ => 1,’dotNoiseLevel’ => 40,’lineNoiseLevel’ => 3));
    $captcha->setFont(“C:\WINDOWS\Fonts\Verdana.TTF”);
    $captcha->generate();
    $this->view->captcha = $captcha->render($this->view);

    if ($this->getRequest()->isPost()){
    if ($captcha->isValid($this->_request->getPost(‘captchaword’))) {
    echo $this->_request->getPost(‘captchaword’) . “”;
    echo “valid”;
    }else {
    echo “not valid”;
    }
    }

    i have tried to use the generate after the if\else but then the captcha is not generated. if i use it as in my code it is generated but (as you say) it is never validated.

    can you help please?

    best regards

    1. Hi Ron,
      A couple of problems with the code:
      1. The generate() method is being called both times – Initial render as well as on post
      2. The isValid() method takes an array as input parameter (when in doubt, always refer to the source code) –
      [input] – The user input of the captcha phrase
      [id] – the md9 of the generated captcha (this is returned by the generate() call
      3. The use of the default ‘render’ call assumes that the image directory is at ‘../images/captcha’

      Here is a modified version of your code that works as expected:
      IndexController.php:

      class IndexController extends Zend_Controller_Action
      {
      public function indexAction()
      {
      //create captcha
      $captcha = new Zend_Captcha_Image(array(‘name’ =>’captchaword’,
      ‘wordLen’ =>1,
      ‘dotNoiseLevel’ =>40,
      ‘ImgDir’=>’../images/captcha’,
      ‘lineNoiseLevel’ => 3));
      $captcha->setFont(“C:\WINDOWS\Fonts\Verdana.TTF”);

      if (!$this->getRequest()->isPost())
      {
      $this->genCaptcha ($captcha);
      }
      else
      {
      if ($captcha->isValid($this->getRequest()->getPost(‘captchaword’)))
      {
      echo “valid”;
      exit(0); //or redirect elsewhere
      }
      else
      {
      echo “not valid”;
      //regenerate captcha for next cycle..
      $this->genCaptcha ($captcha);
      }
      }

      }
      private function genCaptcha($captcha)
      {
      $id = $captcha->generate();
      $this->view->captchaId = $id;
      $this->view->captcha = $captcha->render($this->view);
      }
      }

      Note the use of private function genCaptcha().. this is invoked on the first call OR if the entered captcha phrase is incorrect.

      index.phtml (abridged):
      <form action="index" method="post">
      <p>What does the word below say?</p>
      <?php echo $this->captcha;?>
      <input type="text" name="captchaword[input]" />
      <input type="hidden" value="<?php echo $this->captchaId?>" name="captchaword[id]" />
      <input type="submit" value="Test me" />
      </form>
      Hope this helps!

  2. Hi

    i want to thank you for a great help and explanation. i am just starting to learn zend.

    best regards

    ron

    hope you`ll have here more tutorials on zend:)

    1. Hi,
      at first for this tutorial.

      Next, go no to my problem. I am new in ZF and I’d like to create the captcha in a Zend_form, but it still isn’t working to me.

      I also need advice how to set the imgDir. I have usual ZF directory structure with the “var” directory in ROOT_DIR and I would like to save captcha images to “var/captcha” direcotry. Images are in this directory created, but are not shown in html page 😦 and I don’t know the reason.

      My code is:

      $element = new Zend_Form_Element_Captcha('captcha',
      array('label' => "Ochrana proti spamu.",
      array('captcha' => 'Image',
      'wordLen' => 6,
      'font' => '...',
      'imgDir' => '../var/captcha',
      'timeout' => 300,
      'dotNoiseLevel' => 5,
      'lineNoiseLevel' => 5 )));
      $form->addElement($element);

      Can you help me please?
      Thx

      1. Hi,
        I would try setting the ‘font’ and the ‘imgUrl’ attributes like so:


        ...
        'font' => 'Arial',
        'imgurl' => '/[add_baseurl_here]/var/captcha',
        ...

  3. Excellent example. Zend really does make life easy, except if you are a spambot :-). Thanks for the informative post, this will certainly help a lot of people. It helped me and works great!

Leave a comment