Creating a graphic captcha with the choice of an extra option


At work, it was necessary to solve the problem of spam, as old captcha were easily bypassed by spambots. Googling and not finding the right options, I decided to write my own, and I have long wanted to, to be honest.

And so, the essence of captcha is that the user displays several icons and you need to choose the one that logically does not fit into the general row. I think the Internet has such a sea of ​​options, but I did not find it (well, to be honest, I didn’t really look for it).

Let's start

The principle of operation is as follows: two groups of icons are assembled, one is an array with the correct options, the other with the wrong one. It is assembled into a common array, while the key of the wrong picture (the serial number of the array element) is recorded in the session.
From all the icons we collect one image, which we will then cut using the background-position.
The form is a radio button with a value from 1 to the number of icons. We send the form with the value pressed by using POST and compare it with the number that we put in the session, everything is quite simple.

And so, there is a directory where groups of icons lie:

All functionality is implemented in one class. We will analyze in more detail:

1. Several private variables for storing values ​​that are written in the constructor.

private $icon_dir;  //директория, где лежат папки с иконками
private $icon_size;  //размер иконки
private $first_group_num;  //номер первой группы иконок
private $second_group_num;  //номер второй группы иконок
private $icon_count;  //количество отображаемых в капче иконок
private $group_count;  //количество групп иконок
private $address;  //адрес, по которому будет генерироваться капча

2. Actually the constructor itself, to which 4 parameters are passed.

function __construct($icons_count, $icon_dir, $icon_size, $address)
        $this->icon_dir = $icon_dir;
        $this->icon_size = $icon_size;
        $this->group_count = scandir($this->icon_dir);
        $this->group_count = count($this->group_count) - 2;
        $this->first_group_num = rand(1, $this->group_count);
        $this->second_group_num = rand(1, $this->group_count);
        $this->icon_count = $icons_count;
        while ($this->first_group_num == $this->second_group_num) {
            $this->second_group_num = rand(1, $this->group_count);
        $this->address = $address;

3. With a set of correct and incorrect options, everything is simple, regular scandir () and writing to an array, unless writing to the session of the desired option occurs as follows:

$_SESSION['iconcaptcha'] = array_search($true_icon, $array_of_icons) + 1;

where $ true_icon is the icon we are looking for, and $ array_of_icons is the final icon array.

4. Create the final image from the icons.

private function makeSprite()
{ //соединим иконки в одно изображение
        $icons = array();
        $array_of_icons = $this->getArrayOfIcons();
        for ($i = 0; $i < $this->icon_count; ++$i) {
            $icons[] = imagecreatefrompng($array_of_icons[$i]);
        //для прозрачности png
        $tmp_sprite = imagecreatetruecolor($this->icon_count * $this->icon_size, $this->icon_size);
        imagealphablending($tmp_sprite, false); imagesavealpha($tmp_sprite, true);
        $tmp_icon = imagecreatetruecolor($this->icon_size, $this->icon_size);
        imagealphablending($tmp_icon, false); imagesavealpha($tmp_icon, true);
        foreach ($icons as $key => $val) {
            imagecopyresampled($tmp_icon, $val, 0, 0, 0, 0, $this->icon_size, $this->icon_size, $this->icon_size, $this->icon_size);
            imagecopy($tmp_sprite, $tmp_icon, $key * $this->icon_size, 0, 0, 0, imagesx($tmp_icon), imagesy($tmp_icon));
        header("Content-type: image/png");

5. We get the address where we will cause the creation of a full-fledged picture.

private function getCaptchaAddress($address)
{ //получим изображение по адресу $address
        $req = end(explode('/', $_SERVER['REQUEST_URI']));
        $req = explode('?', $req);
        if ($req[0] == $address) $this->makeSprite();

6. And finally, the final method, deducing the form itself and all everything. Of course, we should remove the styles here, but since here the background-image is set, which provokes the call to makeSprite () and the captcha is actually created (as well as solving the caching problem with the same crooked method), I decided to leave it.

function getCaptchaForm()
        $captcha_form = '
'; for ($i = 1; $i <= $this->icon_count; ++$i) { $captcha_form .= '
'; } $captcha_form .= '
'; return $captcha_form; }

Here's what the call looks like in one of the examples, this thing happens in the configuration file:

$icon_dir = $_SERVER['DOCUMENT_ROOT']."/design/site/images/captcha/";
$icon_captcha = new IconCaptcha(4, $icon_dir, 32, 'iconcaptcha');
$captcha_form = $icon_captcha->getCaptchaForm();
define('ICONCAPTCHA', $captcha_form);

Next, we simply substitute ICONCAPTCHA in the form we need, and check it with a trivial comparison: $ _POST ['radio_val'] == $ _SESSION ['iconcaptcha'].

Here you can click the demo:
Here you can see the whole class:

Also popular now: