Simple captcha component for CakePHP
This post is deprecated. Please check this post instead.
Captcha is the basic necessity in today’s data submission processes where numerous spammers are looking for space to occupy with their promos.
There are number of captcha component online and i have created this one to suit my cakephp needs by modifying one simple yet powerful captcha script.First of all here is the source code of CaptchaComponent class(app\controllers\components\captcha.php):
(I can configure it myself. Just download the zip package)
< ?php
class CaptchaComponent extends Object
{
var $controller;
function startup( &$controller ) {
$this->controller = &$controller;
}
function create() {
$alphabet = "0123456789abcdefghijklmnopqrstuvwxyz"; # do not change without changing font files!
# symbols used to draw CAPTCHA
//$allowed_symbols = "0123456789"; #digits
$allowed_symbols = "2345679abcdehkmnpqsuvxyz"; #alphabet without similar symbols (o=0, 1=l, i=j, t=f)
# folder with fonts
$fontsdir = 'fonts';
# CAPTCHA string length
$length = mt_rand(5,6); # random 5 or 6
//$length = 6;
# CAPTCHA image size (you do not need to change it, whis parameters is optimal)
$width = 200;
$height = 90;
# symbol's vertical fluctuation amplitude divided by 2
$fluctuation_amplitude = 4;
# increase safety by prevention of spaces between symbols
$no_spaces = false;
# show credits
$show_credits = false; # set to false to remove credits line. Credits adds 12 pixels to image height
$credits = 'Smartdata Inc.'; # if empty, HTTP_HOST will be shown
# CAPTCHA image colors (RGB, 0-255)
//$foreground_color = array(0, 0, 0);
//$background_color = array(220, 230, 255);
//$foreground_color = array(mt_rand(0,100), mt_rand(0,100), mt_rand(0,100));
//$background_color = array(mt_rand(200,255), mt_rand(200,255), mt_rand(200,255));
$foreground_color = array(100, 100, 100);
$background_color = array(255, 255, 220);
# JPEG quality of CAPTCHA image (bigger is better quality, but larger file size)
$jpeg_quality = 90;
$fontsdir_absolute= $fontsdir;
if ($handle = opendir($fontsdir_absolute)) {
while (false !== ($file = readdir($handle))) {
if (preg_match('/\.png$/i', $file)) {
$fonts[]=$fontsdir_absolute.'/'.$file;
}
}
closedir($handle);
}
//pr($fonts);die;
$alphabet_length=strlen($alphabet);
while(true){
// generating random keystring
while(true){
$keystring='';
for($i=0;$i< $length;$i++){
$keystring.=$allowed_symbols{mt_rand(0,strlen($allowed_symbols)-1)};
}
if(!preg_match('/cp|cb|ck|c6|c9|rn|rm|mm|co|do|cl|db|qp|qb|dp/', $keystring)) break;
}
$font_file=$fonts[mt_rand(0, count($fonts)-1)];
$font=imagecreatefrompng($font_file);
imagealphablending($font, true);
$fontfile_width=imagesx($font);
$fontfile_height=imagesy($font)-1;
$font_metrics=array();
$symbol=0;
$reading_symbol=false;
// loading font
for($i=0;$i<$fontfile_width && $symbol<$alphabet_length;$i++){
$transparent = (imagecolorat($font, $i, 0) >> 24) == 127;
if(!$reading_symbol && !$transparent){
$font_metrics[$alphabet{$symbol}]=array('start'=>$i);
$reading_symbol=true;
continue;
}
if($reading_symbol && $transparent){
$font_metrics[$alphabet{$symbol}]['end']=$i;
$reading_symbol=false;
$symbol++;
continue;
}
}
$img=imagecreatetruecolor($width, $height);
imagealphablending($img, true);
$white=imagecolorallocate($img, 255, 255, 255);
$black=imagecolorallocate($img, 0, 0, 0);
imagefilledrectangle($img, 0, 0, $width-1, $height-1, $white);
// draw text
$x=1;
for($i=0;$i< $length;$i++){
$m=$font_metrics[$keystring{$i}];
$y=mt_rand(-$fluctuation_amplitude, $fluctuation_amplitude)+($height-$fontfile_height)/2+2;
if($no_spaces){
$shift=0;
if($i>0){
$shift=1000;
for($sy=7;$sy< $fontfile_height-20;$sy+=1){
//for($sx=$m['start']-1;$sx<$m['end'];$sx+=1){
for($sx=$m['start']-1;$sx<$m['end'];$sx+=1){
$rgb=imagecolorat($font, $sx, $sy);
$opacity=$rgb>>24;
if($opacity<127){
$left=$sx-$m['start']+$x;
$py=$sy+$y;
if($py>$height) break;
for($px=min($left,$width-1);$px>$left-12 && $px>=0;$px-=1){
$color=imagecolorat($img, $px, $py) & 0xff;
if($color+$opacity<190){
if($shift>$left-$px){
$shift=$left-$px;
}
break;
}
}
break;
}
}
}
if($shift==1000){
$shift=mt_rand(4,6);
}
}
}else{
$shift=-10;
}
imagecopy($img,$font,$x-$shift,$y,$m['start'],1,$m['end']-$m['start'],$fontfile_height);
$x+=$m['end']-$m['start']-$shift;
}
if($x< $width-10) break; // fit in canvas
}
$center=$x/2;
// credits. To remove, see configuration file
$img2=imagecreatetruecolor($width, $height+($show_credits?12:0));
$foreground=imagecolorallocate($img2, $foreground_color[0], $foreground_color[1], $foreground_color[2]);
$background=imagecolorallocate($img2, $background_color[0], $background_color[1], $background_color[2]);
imagefilledrectangle($img2, 0, $height, $width-1, $height+12, $foreground);
$credits=empty($credits)?$_SERVER['HTTP_HOST']:$credits;
imagestring($img2, 2, $width/2-ImageFontWidth(2)*strlen($credits)/2, $height-2, $credits, $background);
// periods
$rand1=mt_rand(750000,1200000)/10000000;
$rand2=mt_rand(750000,1200000)/10000000;
$rand3=mt_rand(750000,1200000)/10000000;
$rand4=mt_rand(750000,1200000)/10000000;
// phases
$rand5=mt_rand(0,3141592)/500000;
$rand6=mt_rand(0,3141592)/500000;
$rand7=mt_rand(0,3141592)/500000;
$rand8=mt_rand(0,3141592)/500000;
// amplitudes
$rand9=mt_rand(330,420)/110;
$rand10=mt_rand(330,450)/110;
//wave distortion
for($x=0;$x<$width;$x++){
for($y=0;$y<$height;$y++){
$sx=$x+(sin($x*$rand1+$rand5)+sin($y*$rand3+$rand6))*$rand9-$width/2+$center+1;
$sy=$y+(sin($x*$rand2+$rand7)+sin($y*$rand4+$rand8))*$rand10;
if($sx<0 || $sy<0 || $sx>=$width-1 || $sy>=$height-1){
$color=255;
$color_x=255;
$color_y=255;
$color_xy=255;
}else{
$color=imagecolorat($img, $sx, $sy) & 0xFF;
$color_x=imagecolorat($img, $sx+1, $sy) & 0xFF;
$color_y=imagecolorat($img, $sx, $sy+1) & 0xFF;
$color_xy=imagecolorat($img, $sx+1, $sy+1) & 0xFF;
}
if($color==0 && $color_x==0 && $color_y==0 && $color_xy==0){
$newred=$foreground_color[0];
$newgreen=$foreground_color[1];
$newblue=$foreground_color[2];
}else if($color==255 && $color_x==255 && $color_y==255 && $color_xy==255){
$newred=$background_color[0];
$newgreen=$background_color[1];
$newblue=$background_color[2];
}else{
$frsx=$sx-floor($sx);
$frsy=$sy-floor($sy);
$frsx1=1-$frsx;
$frsy1=1-$frsy;
$newcolor=(
$color*$frsx1*$frsy1+
$color_x*$frsx*$frsy1+
$color_y*$frsx1*$frsy+
$color_xy*$frsx*$frsy);
if($newcolor>255) $newcolor=255;
$newcolor=$newcolor/255;
$newcolor0=1-$newcolor;
$newred=$newcolor0*$foreground_color[0]+$newcolor*$background_color[0];
$newgreen=$newcolor0*$foreground_color[1]+$newcolor*$background_color[1];
$newblue=$newcolor0*$foreground_color[2]+$newcolor*$background_color[2];
}
imagesetpixel($img2, $x, $y, imagecolorallocate($img2, $newred, $newgreen, $newblue));
}
}
$this->controller->Session->write('ver_code',$keystring);
ob_start();
if(function_exists("imagejpeg")){
// header("Content-Type: image/jpeg");
imagejpeg($img2, null, $jpeg_quality);
}else if(function_exists("imagegif")){
// header("Content-Type: image/gif");
imagegif($img2);
}else if(function_exists("imagepng")){
// header("Content-Type: image/x-png");
imagepng($img2);
}
$image = ob_get_clean();
$filename_prefix=md5($this->genPass().$this->RandPass());
$C_file= $filename_prefix.'.jpg';
if($this->controller->Session->check('oldcaptcha')) {
$oldcaptcha=$this->controller->Session->read('oldcaptcha');
}
else $oldcaptcha=NULL;
if(isset($oldcaptcha)) {
if(file_exists('img/captcha/'.$oldcaptcha)) {
unlink('img/captcha/'.$oldcaptcha);
}
}
clearstatcache();
$fh = @fopen("img/captcha/".$C_file,"w+");
@chmod("img/captcha/".$C_file,0777);
@fwrite($fh,$image);
$this->controller->Session->write('oldcaptcha',$C_file);
fclose($fh);
return $C_file;
}
function genPass() {
$vocales = "AaEeIiOoUu13580";
$consonantes = "BbCcDdFfGgHhJjKkLlMmNnPpQqRrSsTtVvWwXxYyZz24679";
$r = '';
for ($i = 0; $i < 4; $i++) {
if ($i % 2) {
$r .= $vocales{rand(0, strlen($vocales) - 1)};
} else {
$r .= $consonantes{rand(0, strlen($consonantes) - 1)};
}
}
return $r;
}
function RandPass() {
$randomPassword = "";
for($i=0;$i<5;$i++) {
$randnumber = mt_rand(48,120);
while (($randnumber >= 58 && $randnumber < = 64) || ($randnumber >= 91 && $randnumber < = 96)) {
$randnumber = mt_rand(48,120);
}
$randomPassword .= chr($randnumber);
}
return $randomPassword;
}
}
?>
It will be time taking to discuss the source code line by line. I would just say that the script uses custom font files and gd library to create a image with some random characters on it. It also creates a session containing the code and handles the deletions of old unnecessary files so it does not flood the server space with new files everytime.
Here is the Controller class’s source code(controllers\captcha_controller.php; found in zip files as well):
function create_captcha() {
App::import("Component","Captcha"); //including captcha class
$this->Captcha = new CaptchaComponent(); //creating an object instance
$this->Captcha->controller = & $this; //assign this conroller(CaptchaController) object to its captcha object's controller property.
$this->set('captcha_src',$captcha_src=$this->Captcha->create()); //create a capthca and assign to a variable
}
/**
* Process the form to check user has entered a valid captcha code
*
* @return void
* @access public
*/
function captcha_test() {
if(!empty($this->data)) { //if form is submitted
if($this->data['User']['ver_code']==$this->Session->read('ver_code')) { //comparing both codes
$this->flash(__("Captcha verification success",true),"captcha_test"); //user has entered a valid verification code
} else {
$this->flash(__("Captcha verification failure",true),"captcha_test"); //invalid code
}
}
$this->create_captcha(); //not form action performed, create a captch code and show the form
$this->render();
}
Just call the captcha component class on the fly and create a captcha and set a variable for our view section. Dont forget to pass controller object to component class object ($this->Captcha->controller = $this;).
Now we are ready to use captcha in our view section. We will show captcha image to the user and will ask them to enter the code into a input box so it can be validated against one stored in the session while creating the captcha image. Here are the source code for the view section(views\captcha\captcha_test.ctp).
echo $form->create(null,array('method'=>'post','action'=>'captcha_test'));
echo $html->image("captcha/".$captcha_src);
echo $form->input('User.ver_code');
echo $form->submit(__('Test Captcha',true));
echo $form->end();
Thats all for captcha form validation in a cakephp application!
To make it more simple and straight just download the zip package and do it like this:
#1. Place all files at their respective locations. For example place place captcha.php file inside your projects /app/controllers/component directory and so on.
#2. Make /app/webroot/img/captcha directory writable (chmod 0777)
#3. If using component in a different controller than this package’s captcha_controller.php copy paste both functions to your controller function area.
#4. Access website url like this conrollername(captcha)/captcha_test and you should see the captcha image having a text field under it.
Feel free to post your comments.
Thanks.
Possibly Related posts:
- Simple captcha component for CakePHP (New)
Updated on – December 23, 2011 Download files Follow these simple steps to make it working. (I assume that someone trying it has proficient knowledge... - TinyMce and Captcha collide in my cakephp application
I was struck in a very strange problem today. I was using captcha component to create and set a captcha session and image for view... - getid3 component for cakephp
What is getID3? getID3() is an open-source, cross-platform software library for the PHP language written by James Heinrich and Allan Hansen. getID3() can extract information... - Security component black hole, form submit dies on a blank page with no error
The problem started when i used Auth component in my current CakePHP project. Everything seemed working fine until i created an html form, submitted it... - Using Inflector singularize and camelize for taking out model name in CakePHP
We can use CakePHP library’s Inflector::singularize() and Inflector::camelize() methods for taking out corresponding model name from a controller name (provided we follow CakePHP’s naming conventions...
If you enjoyed this post, please consider to leave a comment or subscribe to the feed and get future articles delivered to your feed reader.






@Manoj – I am sorry but i may not be able to provide support to this component anymore. If you want to continue using the current component please check that you have all controller, model, views and component names properly setup and the your controller action and images are accessible. After a few sessions of debugging and tweaks you should be able to make it work from any controller.
Please check the new captcha component here http://www.devarticles.in/cakephp/simple-captcha-component-for-cakephp-new/. It is simpler and easy to use.
Its very nice. !! its working fine but how can i use it in different controller when i use it in different controller its not working. I have allocate all the files and directories at their respective place………………………..
Thanks
I’ve tried to update image dimension line 26,27 in CaptchaComponent i got:
PHP Fatal error: Maximum execution time of 30 seconds exceeded in /app/controllers/components/captcha.php on line 79
how can i update image dimension to make smaller a bit??
Did you copy/created captcha_controller.php in app/controllers directory?
Missing Controller
Error: CaptchaController could not be found.
Error: Create the class CaptchaController below in file: app/controllers/captcha_controller.php
Notice: If you want to customize this error message, create app/views/errors/missing_controller.ctp