A Basic PHP Captcha (Image Verification) with Refresh Feature

Posted on February 10, 2009, Filled under PHP,  Bookmark it

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!

Do you wish to receive the latest updates as soon as they are posted? Get our RSS Feed or Subscribe to the Newsletter!

Get our RSS Feed!

Sponsors

14 Replies to "A 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'] ) )
    {

  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

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>


  

CommentLuv Enabled