Basic PHP Captcha (Image Verification) with Refresh Feature

Posted on February 10, 2009, under PHP 

This a simple PHP Captcha Class useful to prevent automatic submissions of your forms. It’s very simple to implement and it uses the GD library to generate the image with the security code. Here’s the full class code (I will explain you how it works below):

<?php
class Captcha {

// Number of characters
public $chars_number = 4;

// Numbers (1), Letters (2), Letters & Numbers (3)
public $string_type = 3;

// Font Size
public $font_size = 5;

// Border Color (optional)
public $border_color = '239, 239, 239';

// Path to TrueType Font
public $tt_font = 'arial.ttf';

/* Show Captcha Image */
public function show_image($width = 88, $height = 31)
{
if(isSet($this->tt_font))
{
if(!file_exists($this->tt_font)) exit('The path to the true type font is incorrect.');
}

if($this->chars_number < 3) exit('The captcha code must have at least 3 characters.');

$string = $this->generate_string();

$im = ImageCreate($width, $height);

/* Set a White & Transparent Background Color */
$bg = ImageColorAllocateAlpha($im, 255, 255, 255, 127); // (PHP 4 >= 4.3.2, PHP 5)
ImageFill($im, 0, 0, $bg);

/* Border Color */

if($this->border_color)
{
list($red, $green, $blue) = explode(',', $this->border_color);

$border = ImageColorAllocate($im, $red, $green, $blue);
ImageRectangle($im, 0, 0, $width - 1, $height - 1, $border);
}

$textcolor = ImageColorAllocate($im, 191, 120, 120);

$y = 24;

for($i = 0; $i < $this->chars_number; $i++)
{
$char = $string[$i];

$factor = 15;
$x = ($factor * ($i + 1)) - 6;
$angle = rand(1, 15);

imagettftext($im, $this->font_size, $angle, $x, $y, $textcolor, $this->tt_font, $char);
}

$_SESSION['security_code'] = md5($string);

/* Output the verification image */
header("Content-type: image/png");
ImagePNG($im);


exit;
}

private function generate_string()
{
if($this->string_type == 1) // letters
{
$array = range('A','Z');
}
else if($this->string_type == 2) // numbers
{
$array = range(1,9);
}
else // letters & numbers
{
$x = ceil($this->chars_number / 2);

$array_one = array_rand(array_flip(range('A','Z')), $x);

if($x <= 2) $x = $x - 1;

$array_two = array_rand(array_flip(range(1,9)), $this->chars_number - $x);

$array = array_merge($array_one, $array_two);
}

$rand_keys = array_rand($array, $this->chars_number);
	
$string = '';

foreach($rand_keys as $key)
{
$string .= $array[$key];
}

return $string;
}

}
?>
Variables Info

public $chars_number – The number of characters for the security code.
public $string_type What kind of string will be generated? 1 = numbers, 2 = letters, 3 = letters & numbers (default).
public $font_size – The size of the TrueType Font. It is recommended to set it depending on both the number of characters and the size (width & height) of the Captcha image.
public $border_color – Sets the color of the border using RGB values. (ex: 211, 211, 211 (white grey) )
public $tt_font – Path to the true type font file

How is this class working?

The function which generates the Captcha is show_image(string $width, string $height). First it checks if the path to the true type font is correct. If it isn’t the script will close after an error message will be shown:

if(isSet($this->tt_font))
{
if(!file_exists($this->tt_font)) exit('The path to the true type font is incorrect.');
}

The security code should have at least 3 characters:

if($this->chars_number < 3) exit('The captcha code must have at least 3 characters');

Further on, the security code will be generated by calling the generate_string() function:

$string = $this->generate_string();

How does this function work?

The value of $string_type is analyzed. Based on the chosen type (letters, numbers, letters & numbers) an array will be generated. Afterwards, we will use array_rand() to create a second array with random keys selected from the first array:

$rand_keys = array_rand($array, $this->chars_number);

What's next? Loop through the recently created array to generate the security code:

$string = '';

foreach($rand_keys as $key)
{
$string .= $array[$key];
}

After the string is generated we can move on with the creation of the image. The first function used is ImageCreate() to create a new palette based image with the specified size.

resource imagecreate ( int $width, int $height )

$im = ImageCreate($width, $height);

Set a white & transparent background for the recently created image:

/* Set a White & Transparent Background Color */
$bg = ImageColorAllocateAlpha($im, 255, 255, 255, 127); // (PHP 4 >= 4.3.2, PHP 5)
ImageFill($im, 0, 0, $bg);

Add the border with the specified color (default is grey):

/* Border Color */

if($this->border_color)
{
list($red, $green, $blue) = explode(',', $this->border_color);

$border = ImageColorAllocate($im, $red, $green, $blue);
ImageRectangle($im, 0, 0, $width - 1, $height - 1, $border);
}

Allocate a color for the text:

$textcolor = ImageColorAllocate($im, 191, 120, 120);

Write the text to the image using imagettftext() by calculating the right coordinates:

$y = 24;

for($i = 0; $i < $this->chars_number; $i++)
{
$char = $string[$i];

$factor = 15;
$x = ($factor * ($i + 1)) - 6;
$angle = rand(1, 15);

imagettftext($im, $this->font_size, $angle, $x, $y, $textcolor, $this->tt_font, $char);
}

Now, we need to register the session for the generated string. For security reasons we will use md5() to register a 32 characters hash of the code.

$_SESSION['security_code'] = md5($string);

Finally, output the image and exit:

/* Output the verification image */
header("Content-type: image/png");
ImagePNG($im);

exit;

How can I implement this Captcha with the refresh feature?

This class can be called from a HTML file. Here's the code:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
 <head>
  <title>Captcha Verification Image</title>

<script type="text/javascript">

<!--
function new_captcha()
{
var c_currentTime = new Date();
var c_miliseconds = c_currentTime.getTime();

document.getElementById('captcha').src = 'image.php?x='+ c_miliseconds;
}
-->
</script>

 </head>

 <body onload="new_captcha();">
 <form action="check.php" method="post">

 <table>
 <tr>
 <td>&nbsp;</td>
 <td><img id="captcha" src="image.php" alt="">

 &nbsp;<a href="JavaScript: new_captcha();">
 <img border="0" alt="" src="refresh.png" align="bottom"></a></td>
</tr>
 <tr>
 <td>Security Code:</td>
 <td><input type="text" name="security_code" value="">

 &nbsp;<input type="submit" name="submit" value="Check"></td>
</tr>
</table>
</form>
 </body>
</html>

How is the Captcha refreshed?

As you can notice the image has the id attribute equal with 'captcha' and the SRC equal with 'image.php'. Everytime the refresh icon is clicked the JavaScript new_captcha() function is called setting an unique SRC for the image. This way the user will not access a recently cached version of the captcha and the image.php will re-load. The variable 'c_milliseconds' is unique every time the user accesses the page that calls the captcha and contains the number of milliseconds since midnight of January 1, 1970.

function new_captcha()
{
var c_currentTime = new Date();
var c_milliseconds = c_currentTime.getTime();

document.getElementById('captcha').src = 'image.php?x='+ c_milliseconds;
}

The new_captcha() is triggered in every page re-load:

<body onload="new_captcha();">

This way, a new image is generated and the previously shown image is not cached even if the user uses the browser's BACK button to return to the page with the captcha.

In image.php we will call the class that will output the captcha:

include_once 'common.php';
include_once 'class.captcha.php';

$captcha = new Captcha();

$captcha->chars_number = 5;
$captcha->font_size = 14;
$captcha->tt_font = 'verdana.ttf';

$captcha->show_image();

In common.php, the session is started and the right headers are sent:

<?php
session_start();
header('Cache-control: private'); // IE 6 FIX
// always modified 
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); 
// HTTP/1.1 
header('Cache-Control: no-store, no-cache, must-revalidate'); 
header('Cache-Control: post-check=0, pre-check=0', false); 
// HTTP/1.0 
header('Pragma: no-cache');
?>

captcha

How will the submitted code be checked?

In check.php, we will verify if the security code was submitted. If so, we will remove any whitespace from the beginning and ending of the script using the trim() function. Then, we will encrypt it using md5(). The returned hash is compared to the value of the registered session 'security_code'. If it's a match then the correct code was submitted. If it isn't, an error message will be shown:

<?php
include_once 'common.php';

if(isSet($_POST['security_code']))
{
$security_code = trim($_POST['security_code']);

$to_check = md5($security_code);

if($to_check == $_SESSION['security_code'])
{
echo "The security code is <font color='green'>correct</font>.";
}
else
{
echo "The security code is <font color='red'>incorrect</font>.";
}
}
?>


Do you want to increase / decrease the number of characters and also the size of the Captcha Image? Here are some examples of how you can do that:

1) Use an image with 4 characters and a smaller size (70 x 30)

<?php
include_once 'common.php';
include_once 'class.captcha.php';

$captcha = new Captcha();

$captcha->chars_number = 4;
$captcha->font_size = 14;
$captcha->tt_font = 'verdana.ttf';

$captcha->show_image(70, 30);
?>

captcha-4-chars


2) Use an image with 8 characters and a bigger size (120 x 30)

<?php
include_once 'common.php';
include_once 'class.captcha.php';

$captcha = new Captcha();

$captcha->chars_number = 8;
$captcha->font_size = 14;
$captcha->tt_font = 'verdana.ttf';

$captcha->show_image(132, 30);
?>

captcha-8-chars

If you have any questions regarding this class, please ask! Happy coding!

Comment via Facebook

comments

26 Replies to "Basic PHP Captcha (Image Verification) with Refresh Feature"

  1. Great article. thanx for ur sharing.

  2. Very nice!!

  3. Wow! thanks man!
    great job!

  4. The downloaded file not run well,
    it asks me a check.php file :(

    What mistakes done there?

  5. # // Numbers (1), Letters (2), Letters & Numbers (3)
    # public $string_type = 3;

    This line works vice-versa…
    That is if i choose 1 it displays letters, and if i choose 2 it displays numbers and 3 is working fine!

    thanks mate i love this

  6. Download the code, it DOES NOT work as demo

  7. The solution is the check.php should be like this:

    if( isset($_POST['submit']))
    {
    if( $_SESSION['security_code'] == md5($_POST['security_code']) && !empty($_SESSION['security_code'] ) )
    {

    1. thx. it coollll…

  8. great article. very helpful. working perfectly.
    thanks for this post dear.

  9. Hi. First forgive me, my English is bad

    I have 2 problems.
    The refresh of CAPTCHA not working (“in the file form.html”). I don’t know where this error, because when you click “refresh link” it does not update the CAPTCHA.
    And finally when you click submit, validation does not occur and the script returns the mensage exist in the recomenda_ERRO.html file.

    I usualled the original file check and now I´m testing the solution sended for ABUT.

    Please help me!!!

    This Code I adapted and I´m testing. But don´t work.
    <?
    include_once '../capcode/common.php';

    $hoje_tmp = getdate();
    $hoje = ($hoje_tmp[hours].":".$hoje_tmp[minutes].":".$hoje_tmp[seconds]."hs");

    $remetente_nome = $_POST["remetente_nome"]; //trata a variável – Nome
    $remetente_mail = $_POST["remetente_mail"]; //trata a variável – E-mail
    $destinatario_nome = $_POST["destinatario_nome"]; //trata a variável – Destinatário
    $destinatario_mail = $_POST["destinatario_mail"]; //trata a variável – E-mail do destinatário
    $mensagem = $_POST["mensagem"]; //trata a variável – Mensagem

    global $email; //transforma em variavel global a variável e-mail

    if( isset($_POST['submit']))
    {
    if( $_SESSION['security_code'] == md5($_POST['security_code']) && !empty($_SESSION['security_code'] ) )
    {
    $enviou = mail("$destinatario_mail", "$remetente_nome esta recomendando nosso site",
    "Olá, $destinatario_nome.

    $remetente_nome, visitou o endereço http://www.site.com e achou que você iria gostar de conhecer também.

    Mensagem escrita por $remetente_nome:

    $mensagem

    ______________________________________________________________________________
    Mensagem enviada via formulário on-line as $hoje
    "From: $remetente_nome “, “Bcc: contato@site.com“);

    if ($enviou)
    {
    Header(“Location: recomenda_ok.html”);
    }
    else
    {
    Header(“Location: recomenda_erro.html”);
    }
    }
    else
    {
    Header(“Location: recomenda_erro.html”);
    }
    }
    ?>

    FORM.HTML

    Formulário de envio

    Seu nome:

    Seu e-mail:

    Destinatário(a):

    Destinatário(a) e-mail:

    Mensagem:

    Digite o código de Validação:

    ATENÇÃO! Todos os campos são obrigatórios.

    1. @Rodrigo, can you show me where you have added the form and the captcha? Send me the URL so I can check them.

  10. Captcha images are not displaying why..?check the demo also!

    1. The captcha image can be seen now ;-)

  11. Thank you very much for publishing this lesson. The code as well as the tutorial seem to be very thorough. The code works right out of the box so thanks for that. I’ll definitely will visit your site often.

  12. great work man ,well done

  13. thank you verry much,good job!
    but when i change the dimension and the number of character the image will not appear..
    Why?

  14. I have a little problem!
    I’ve tried this code and it works properly on my localhost, but if i upload to a server the security image is not shown at all! I tried it on another server and it was almost the same problem, the image was created there but the code was not shown! How could i solve this, and whats the problem?? I tried to change path as well, but didnt work! :(

    Thank you very much in advance!

    1. I found that PHP version 5.2.13 has two different FreeType versions installed. Running local (Apache) everything works fine with FreeType version 2.39, but on my server (CGI) I find I have FreeType version 2.37 and it will NOT load the image.

      Any tip what to do next ?

  15. Hi dear,

    it is working well in IE but in Mozilla captcha image is not coming.
    wheres the error…?

  16. Great Tutorial. However for those who don’t have the GdLibs installed you can use ImageMagick – See here http://www.thephpanswers.com/viewtopic.php?f=11&t=3 it wouldnt take long to add the refresh feature :)

  17. I m Download the code, it DOES NOT work as demo

  18. the download file didn’t work…
    Can you look againnn…

    1. It looks like the CAPTCHA font can’t be read from its location. Make sure you put the right path to the TrueType Font.

  19. Thanks for your refresh feature!

  20. You might have to add
    // Set the enviroment variable for GD
    putenv(‘GDFONTPATH=’ . realpath(‘.’));”
    to class.captcha.php to alter the GD path

Leave a Reply


* = required fields

  (will not be published)


XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Note: If you want to post CODE Snippets, please make them postable first!
(e.g. <br /> should be converted to &lt;br /&gt;)

POSTING RULES:

  • The comment must be relevant with the topic of the post.
  • Only comments with real email addresses will get approved. So, emails like 'abc@domain.com' will not be accepted.
  • Do not post the same message in multiple articles through the site.
  • Do not post advertisements, junk mail or pyramid schemes.
  • In case you post a link to another site, please explain briefly where the link goes as a courtesy to other users.
  • Do not post comments such as: "Thank you", "Awesome", "Nice tutorial", "Merci", etc.