Simple captcha component for CakePHP 1.x

Updated on – April 11, 2013

Download Captcha Component for CakePHP 1.x

Download Captcha Component for Cakephp 2.x

Model validation to validate captcha field value included with examples. Functions(rules) required for model validation are also written in the model file.
———————————————————-

Follow these simple steps to make it working.

Copy attached font file (monofont) to ‘webroot’ folder.

Copy component file (captcha.php) to app/controllers/components

Create a function similar to the following in your controller. I have used “signups” controller for demonstration purpose. You should change controller name accordingly.

[php]function captcha() {
$this->autoRender = false;
$this->layout=’ajax’;
if(!isset($this->Captcha)) { //if Component was not loaded throug $components array()
App::import(‘Component’,’Captcha’); //load it
$this->Captcha = new CaptchaComponent(); //make instance
$this->Captcha->startup($this); //and do some manual calling
}
//$width = isset($_GET[‘width’]) ? $_GET[‘width’] : ‘120’;
//$height = isset($_GET[‘height’]) ? $_GET[‘height’] : ’40’;
//$characters = isset($_GET[‘characters’]) && $_GET[‘characters’] > 1 ? $_GET[‘characters’] : ‘6’;
//$this->Captcha->create($width, $height, $characters); //options, default are 120, 40, 6.
$this->Captcha->create();
}[/php]

Call the captcha action from within your img tag in the view file as below:

[php]echo $form->create("Signups");
echo $html->image($html->url(array(‘controller’=>’signups’, ‘action’=>’captcha’), true),array(‘style’=>”,’vspace’=>2)); ?>
echo ‘Enter the given security code:’;
echo $form->input(‘Signup.captcha’,array(‘autocomplete’=>’off’,’label’=>false,’class’=>”,’error’=>__(‘Failed validating code’,true)));
echo $form->submit(__(‘ Submit ‘,true));
echo $form->end();[/php]

And you are done. The captcha image should look like Cakephp Captcha COmponent

Also, you can re-generate the captcha by adding (replacing code in line 2 of the above) ajax code something similar to the following:

[php]<div id="captchaID" ><?php
echo $html->image($html->url(array(‘controller’=>’signups’, ‘action’=>’captcha’), true),array(‘style’=>”,’vspace’=>2)); ?></div>
<?php echo $ajax->link(‘Can not read this code? Reload.’,’regenerate code’,array(‘url’=>’reload_captcha’,’update’=>’captchaID’)); ?>[/php]

In the code above it calls a reload_captcha function of the current controller. You can place reload_captcha in current controller or preferrably in the app_controller to make it available universally. Here’s the code:

[php]function reload_captcha() {
App::import(‘Component’,’Captcha’); //load it
$this->Captcha = new CaptchaComponent(); //make instance
$this->Captcha->startup($this); //and do some manually calling
$this->layout=’ajax’;
Configure::write(‘debug’,0);
$this->viewPath = ‘elements’.DS;
$this->render(‘reload_captcha’);
}[/php]

It renders a reload_captcha element. Create a reload_captcha.ctp file and place it in views/elements with the following code:

[php]<?php echo $html->image($html->url(array(‘controller’=>’signups’, ‘action’=>’captcha’), true),array(‘style’=>”,’vspace’=>2)); ?>[/php]

There is model validation code included in the files with example of how to validate it. Files include a model with new validation entry along with two rules (functions).

Download Captcha Component for Cakephp 1.x
Download Captcha Component for Cakephp 2.x

68 thoughts on “Simple captcha component for CakePHP 1.x

  1. Controller = $collection->getController();
    parent::__construct($collection, $settings);
    }

    function startup() {
    $this->font=ROOT.DS.APP_DIR.DS.’Plugin’.DS.’Tickets’.DS.’webroot’.DS.$this->fontFile ;
    }

    private function generateCode($characters) {
    /* list all possible characters, similar looking characters and vowels have been removed */
    $possible = ‘23456789bcdfghjkmnpqrstvwxyz’;
    $code = ”;
    $i = 0;
    while ($i generateCode($characters);
    $this->Controller->Session->write(‘security_code’,$code);
    /* font size will be 75% of the image height */
    $font_size = $height * 0.70;
    $image = @imagecreate($width, $height) or die(‘Cannot initialize new GD image stream’);
    /* set the colours */
    $background_color = imagecolorallocate($image, 220, 220, 220);
    $text_color = imagecolorallocate($image, 10, 30, 80);
    $noise_color = imagecolorallocate($image, 150, 180, 220);
    /* generate random dots in background */
    for( $i=0; $i<($width*$height)/3; $i++ ) {
    imagefilledellipse($image, mt_rand(0,$width), mt_rand(0,$height), 1, 1, $noise_color);
    }
    /* generate random lines in background */
    for( $i=0; $ifont, $code) or die(‘Error in imagettfbbox function’);
    $x = ($width – $textbox[4])/2;
    $y = ($height – $textbox[5])/2;
    $y -= 5;
    imagettftext($image, $font_size, 0, $x, $y, $text_color, $this->font , $code) or die(‘Error in imagettftext function’);
    // prevent client side caching
    header(“Expires: Wed, 1 Jan 1997 00:00:00 GMT”);
    header(“Last-Modified: ” . gmdate(“D, d M Y H:i:s”) . ” GMT”);
    header(“Cache-Control: no-store, no-cache, must-revalidate”);
    header(“Cache-Control: post-check=0, pre-check=0”, false);
    header(“Pragma: no-cache”);
    /* output captcha image to browser */
    while (@ob_end_clean());
    header(‘Content-Type: image/jpeg’);
    imagejpeg($image,null,90);
    imagedestroy($image);
    }

    function getVerCode() {
    return $this->Controller->Session->read(‘security_code’);
    }
    }
    ?>

  2. Add this line before header(‘Content-Type : image/jpeg’)

    while (@ob_end_clean());

    this will clean buffers. It is useful when you are using Microsoft IIS

  3. Those getting corrupt output add following lines of code before image render function

    while (ob_get_level()) {
    ob_end_clean();
    }

  4. @Vahid : I think you have skipped few lines from the top… PLease post it again. or Check or modify your post.

  5. I prepared this for cakephp 2.2 :

    _controller = $collection->getController();
    parent::__construct($collection, $settings);
    }

    function startup( ) {
    $this->Controller=$this->_controller ;
    }

    function generateCode($characters) {
    /* list all possible characters, similar looking characters and vowels have been removed */
    $possible = ‘23456789bcdfghjkmnpqrstvwxyz’;
    $code = ”;
    $i = 0;
    while ($i generateCode($characters);

    /* font size will be 75% of the image height */
    $font_size = $height * 0.70;
    $image = @imagecreate($width, $height) or die(‘Cannot initialize new GD image stream’);
    /* set the colours */
    $background_color = imagecolorallocate($image, 220, 220, 220);
    $text_color = imagecolorallocate($image, 10, 30, 80);
    $noise_color = imagecolorallocate($image, 150, 180, 220);
    /* generate random dots in background */
    for( $i=0; $i<($width*$height)/3; $i++ ) {
    imagefilledellipse($image, mt_rand(0,$width), mt_rand(0,$height), 1, 1, $noise_color);
    }
    /* generate random lines in background */
    for( $i=0; $ifont, $code) or die(‘Error in imagettfbbox function’);
    $x = ($width – $textbox[4])/2;
    $y = ($height – $textbox[5])/2;
    $y -= 5;
    imagettftext($image, $font_size, 0, $x, $y, $text_color, $this->font , $code) or die(‘Error in imagettftext function’);
    // prevent client side caching
    header(“Expires: Wed, 1 Jan 1997 00:00:00 GMT”);
    header(“Last-Modified: ” . gmdate(“D, d M Y H:i:s”) . ” GMT”);
    header(“Cache-Control: no-store, no-cache, must-revalidate”);
    header(“Cache-Control: post-check=0, pre-check=0”, false);
    header(“Pragma: no-cache”);
    /* output captcha image to browser */
    header(‘Content-Type: image/jpeg’);
    imagejpeg($image);
    imagedestroy($image);
    $this->Controller->Session->Write(‘security_code’,$code);
    }

    function getVerCode() {
    return $this->Controller->Session->read(‘security_code’);
    }
    }
    ?>

    Just pass the time() to link will refresh the captcha through an ajax request.

    e.g.
    Js->link(‘Reload’,array(‘action’=>’reloadCaptcha’,time()),array(‘update’=>’#elem’)) ; ?>

  6. Hi..i would like to share in Display problem in captcha in cakephp2.0

    I change something in captcha.php in controller/component folder

    1. I rename captcha.php into CaptchaComponent.php
    2. class CaptchaComponent extend Component … instead of “Object”
    3. there is an error in:
    function startup( &$controller ) {
    $this->Controller =& $controller;
    }
    .. delete the “&” in it would be like this $this->Controller = $controller

    and there the captcha showed up!.. I HOPE IT HELP!

    Great component! Thumbs up! :D

  7. great component!

    After editing captcha.php component like WWW_ROOT.DS.$this->font, it all worked fine for me. Well, not the reload stuff.

    I am just wondering how to validate the captcha. I played around with this: ‘captcha’=>array(
    ‘rule’ => array(‘equalTo’, $this->captcha),
    ‘message’ => ‘You lie!’,
    ‘allowEmpty’ => false
    )
    And its just not working. but i dont know why :(

    1. There is already an example included in the signup Modal. If you check around:

      var $validate = array(
      ‘captcha’=>array(
      ‘rule’ => array(‘matchCaptcha’)
      ),
      );

      function matchCaptcha($inputValue) {
      return $inputValue[‘captcha’]==$this->getCaptcha(); //return true or false after comparing submitted value with set value of captcha
      }

      I hope that helps.

  8. great component!

    After editing captcha.php component like WWW_ROOT.DS.$this->font, it all worked fine for me. Well, not the reload stuff.

    i am just wondering how to validate the captcha. I played around with

  9. Hi, Thanks for your great component.I need it for cakephp2. Can you help me to use your great component in cakephp2?

    Thanks alot

    1. Hi Amir,

      Apologies for late reply. I didn’t get sufficient time yet to make it working with 2.0. I think i will do it soon.

      Thanks,
      Arvind.

    1. @m16u – No. No 2.0 version yet.. I am hoping for someone make a 2.0 version of it and post it. Anyways I will check if i get time in near future to do it.

      For someone who follows the 2.0 migration guide it should not be very difficult to make it working with 2.0 in no time.

  10. I got this to work except for the reload part. I am using cake 2 and changed the reload code to the following:

    Js->link(‘Can not read this code? Reload.’, ‘add’, array(‘update’=>’captchaID’)); ?>

    … this refreshes the whole form so the users data is lost.

    I also tried putting reload_captcha into the Elements folder and tried the following:

    Js->link(‘Can not read this code? Reload.’, ‘reload_captcha’, array(‘update’=>’captchaID’)); ?>

    .. but this just causes the captcha to display on it’s own without the form.

  11. Thanks a lot for such a simple captcha..

    I am working on my cakePHP 1.3 in ubuntu OS..My images are not displaying with captcha..I tried signup controller and its related file…Even if i am not getting any error i have this problem with my project
    1.Result I got when i submit captcha form empty:Captcha code validated!
    2.Captcha image is not displayed
    3.I am working in local.And i checked font permission and tried executing with all possible debug value…But no way out..
    Can you please help me

  12. Got it working.
    1. You have to apply random string (or time stamp) to the src as parameter to prevent caching.
    2. For IE, AJAX request caching must be disabled (explicit)… gotta love IE

  13. Hi Arvind,

    I’ve tried the demo of your captcha implementation @ givebackindia.com and the reload function is not working under Firefox 9, Opera 11.6 and IE 8. It appears to be working only under Chrome.

  14. Add WWW_ROOT . DS before $this->font in imagettfbbox and in imagettftext. which will solve imagettfbbox(): Could not find/open font error

  15. Tried to use with cakephp2.0
    showing error
    call_user_func_array() expects parameter 1 to be a valid callback, class ‘CaptchaComponent’ does not have a method ‘initialize’

  16. Hi Arvind,

    Thank for your quick response.

    Anyway, I have fixed that issue. That was just a small issue of Out Buffer. :)

    But, finally its work like a charm.

    Thanks for such a nice component.

  17. hi arvind,

    First of all thanks for a nice component. !!

    But i am getting some problem to display a image captcha on my one of the project.even i have done all the required steps to integrate it.but still image is not displaying on my captcha form.

    it would be great if you can help me here.

    Thank in Advance.

  18. Hi, I have the same problem as @chellapa.. monofont.tff have all permits, how can I fix it?

    Im usin 1.3.10

    Thank’s!

    1. Try uploading and replacing the font file again in binary mode rather than automatic or ascii mode. (Your FTP client might have those options). If it still does not work try using some other font file.

  19. hi there,
    while using this captcha following error occurs
    Warning (2): imagettfbbox() [http://php.net/function.imagettfbbox]: Could not find/open font [APP/controllers/components/captcha.php, line 51]
    could u pls tell me the way to fix it?

  20. Hi,

    I am able to view the captcha string while debugging SESSION .. but I am not able to view any image .. I have checked the font file .. I am able to view it from browser

  21. Thank you for this awesome component!
    Had it running in a couple of minutes on my Cake 1.3 projekt.
    Clean and simple to set up, i love it.

    For those who cant get the captcha image show up, make sure the captcha action is accessable by cake.
    Maybe you need to set a $this->Auth->allow(‘captcha’); in your beforeFilter() Function in your controller, if youre using admin routing.

    Thanks again Arvind, best Captcha for Cake yet imho.

  22. I try to re-generate captcha, but have an error missing view, I don’t know why. Maybe I don’t understand your code want to do.
    link(‘Can not read this code?’,’re-generate captcha’,array(‘url’=>’users/captcha’,’update’=>’captchaID’,’loading’=>’do this’,’loaded’=>’do that’)); ?>

    Can you explain it clearly? What is ‘re-generate captcha’? (I’m new) Thanks

    1. @HT – This is only an idea of how to re-generate the captcha. You may have to supply proper controller and action of captcha. For example if your controller name is “users” and captcha action name is “captcha” only then this url users/captcha is valid. Also you should have some knowledge of showing view content with the help of ajax. As you said you are new i would suggest you to just skip the “re-generate” thing, the characters shown in captcha are quite clear to read i guess.

  23. I work fine locally on my pc with wamp (with php 5.3 ) but on my server (linux, php 5.2.6) , the captcha is not displayed . I saw that Php GD is correctly installed.
    I don’t understand

    1. @Forst – I think you have got my email id.. If you wish you can send me your controller, component and view files so i could try to find out the issue for you. This captcha component is working just fine on number of websites hosted on linux with similar configuration to as you mention.

    1. @Forst

      There must be some error or anything at least. Do you try it while debug=2?

      Fyi, in the view file where you want to show captcha, you must have to set the correct path of capthca image. The “captcha” action of “signups” controller outputs captcha image so your captcha image source in your view file must include correct controller name and captcha action name. For example if you place captcha action inside you users controller, in your view file you should call captcha something like this (while referring to line 5 of views/signups/add.ctp file in original package):

      <?php echo $html->image($html->url(array('controller'=>'users', 'action'=>'captcha'), true),array('style'=>'','vspace'=>2)); ?>

      I hope that helps.

      1. @To All,

        I uploaded latest package today on 03 March, 2011. add.ctp file was modified for the image url. In the previous version it had $html->url(‘captcha’, true) assuming that you were on the same view file of the resident controller i.e. “signups”. I have replace it with more explanatory $html->url(array(‘controller’=>’signups’, ‘action’=>’captcha’)).

        You can even set this image source as a string, that is something like:
        image(“http://yourdomain.com/yourcontrollername/captcha”,array(‘style’=>”,’vspace’=>2)); ?>.

        Or even:

        <img src=”http://yourdomain.com/yourcontrollername/captcha” /> should equally work.

  24. My ‘ webroot’ directory is already have read permissions because i have also image, css and other on this directory

  25. I have problem with font dir , i’ve placed monofont.ttf in the directory ‘webroot’ but I have this error :

    Warning (2): imagettfbbox() [function.imagettfbbox]: Could not find/open font [APP/controllers/components/captcha.php”

  26. I have cakephp 1.2
    when I remove this line
    $this->Captcha = new CaptchaComponent();
    I can write/read the security code into the session

    1. @Ahmad
      Can you or can’t you? If you can, i guess you do include Captcha in $components array at the top of controller. Do you? Anyway i am adding new updated code with entire directory structure with corresponding files. This one is more clearer and also i have included model validation in this one.

  27. Fyi, if you are including “Captcha” component in your controller’s var $components variable (at the top of controller) you must do $this->Captcha->controller = $this just before calling the $this->Captcha->create() method to access the Session property of the controller (required to create the session variable ’security_code’).

    $this->Captcha->controller = $this ??? what’s the continuation?

  28. Hi Arvind,

    I guess the problem is I don’t set var $validate, but declare the validation rules inside the __construct() function, (because __(”, true) doesn’t work in var declarations!)

    The rest is the same though…

    I tried to declare var $validate but doesn’t allow ‘rule’ => array(‘equalTo’, $this->captcha) either, so I am forced to declare it in __constrcut(). but then there is the problem of setting the $this->captcha value before it created the validation rule.
    The only solution I could think of was to create the validation rule in the beforeValidate function.

    You are right that beforeValidate is called automatically :)

    Thanks for support and all!

  29. Hi Arvind,

    I wonder how you set the $this->captcha property, especially where and when.

    If I change my code, i.e. removing the beforeValidation call from the controller and function from the model(since there is nothing else in my case to do there) and move the validation from the beforeValidation function to the __construct() and leave everything else the same, the validation fails because $this->captcha is null.

    Btw i’m fairly new to CakePHP but the code i provided in my previous post worked fine for me :)

    1. Sorry for the confusion, Pim. The suggestion i offered is only in the context of code written by you. Let me explain.

      In controller you still need


      $this->User->setCaptchaCode($this->Session->read('security_code'));

      before calling save function and private $captch in the model as well.

      Skipping the use of beforeValidate you could simply define ‘captcha’ rule in User model’s $validate property along with other rules you may have as below:


      //validation rules start
      var $validate = array(
      ....................................
      ....................................
      'captcha'=>array(
      'rule' => array('equalTo', $this->captcha),
      'message' => __('Captcha verification failure', true)
      )
      ....................................
      ....................................
      ); //validation rules end

      And it should work just fine.

      Eventually this is exactly what you had been doing so far, i just wanted to skip the user of beforeValidate there. Anyway never mind it :)

  30. First of all, I want to thank you for sharing this! :)

    Since all the validation rules are check in the model, I wanted to check the captcha code in the model also.

    To do so I did the following:

    in the action in the controller that is called when you submit the form I added these lines:

    $this->User->create();
    $this->User->setCaptchaCode($this->Session->read('security_code'));
    $this->User->beforeValidate();

    User model:
    Add this at the top:

    private $captcha = null;

    create this method:

    function setCaptchaCode($captcha) {
    $this->captcha = $captcha;
    }

    finaly, add this piece of code to the beforeValidate function:

    if(isset($this->captcha)) {
    $this->validate['captcha'] = array(
    'rule' => array('equalTo', $this->captcha),
    'message' => __('Captcha verification failure', true)
    );
    }

    Hope this helps someone :)

    Happy baking ;)

    1. Hi Pim,

      Thanks for your thoughts. This is exactly what i did except once change. I did not use beforeValidate.

      I don’t think we need a check inside beforeValidate function. In fact a validation rule runs if the submitted form data has that particular field set within which we create a rule for. So just adding a rule should be okay. This is what i mean and doing here without any issue.


      //validation rules start
      var $validate = array(
      ....................................
      ....................................
      'captcha'=>array(
      'rule' => array('equalTo', $this->captcha),
      'message' => __('Captcha verification failure', true)
      )
      ....................................
      ....................................
      ); //validation rules end

      And then of course you would not need to call $this->User->beforeValidate(); in your controller. In fact we don’t need to call the callback functions like beforeValidate, beforeSave, AfterSave etc manually as they are called automatically. Don’t they?

  31. So let me explain. I have Captcha controller with the funti on Captchatest. The Captchatest has App::import('Component','Captcha');
    $this->Captcha->controller = $this;
    $this->Captcha->create();

    in the contacts controller i am using the component Captcha as well as Session. In the view of contacts i use
    echo $html->image('http://www.site.com/captcha/captchatest', array('style'=>'border:1px #ccc solid','vspace'=>2)); to load the captcha image and it works … but when i try to see the generated code in the contact us controller fails.

    P.S. I did some changes to the Captcha Component:

    Replaced
    ————————————————–
    function CaptchaComponent( $controller ) {
    $this->controller = $controller;
    }

    —————————————————
    With
    —————————————————
    function startup($controller)
    {

    $this->controller = $controller;
    }

  32. The session variable is not passed on from captcha.php… can you show me an example how to verify the $this->Session->read(’security_code’) in other controllers for ex.

    Thanks

    1. It should simply be accessible using $this->Session->read(’security_code’) in any controller provided the component is able to create ’security_code’ session variable successfully (see $this->controller->Session->write(‘security_code’,$code) near the bottom of the component file)

      Fyi, if you are including “Captcha” component in your controller’s var $components variable (at the top of controller) you must do $this->Captcha->controller = $this just before calling the $this->Captcha->create() method to access the Session property of the controller (required to create the session variable ’security_code’).

Leave a Reply