Создание графической капчи с выбором лишнего варианта

от автора

image

На работе потребовалось решить проблему со спамом, так как старую капчу легко обходили спамботы. Погуглив и не найдя нужных вариантов, решил написать свой, да и давно хотелось, если честно.

И так, суть капчи в том, что пользователю отображается несколько иконок и необходимо выбрать ту, которая логически не вписывается в общий ряд. Думаю в интернете таких вариантов море, но я не нашел (ну если честно не особо то и искал).

Начнем

Принцип работы следующий: собирается две группы иконок, одна это массив с правильными вариантами, другая с неправильным. Собирается в общий массив, при этом ключ неправильной картинки (порядковый номер элемента массива) записывается в сессию.
Из всех иконок собираем одно изображение, которое потом будем резать с помощью background-position.
Форма представляет собой радиокнопки с value от 1 и до количества иконок. Отправляем с помощью POST форму с нажатым value и сверяем его с тем числом, что мы положили в сессию, все довольно просто.

И так, есть директория, где лежат группы иконок:

Весь функционал реализован в одном классе. Разберем поподробнее:

1. Несколько приватных переменных, для хранения значений, которые пишутся в конструкторе.
private $icon_dir;  //директория, где лежат папки с иконками private $icon_size;  //размер иконки private $first_group_num;  //номер первой группы иконок private $second_group_num;  //номер второй группы иконок private $icon_count;  //количество отображаемых в капче иконок private $group_count;  //количество групп иконок private $address;  //адрес, по которому будет генерироваться капча 
2. Собственно сам конструктор, которому передается 4 параметра.
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. С набором правильных и неправильного вариантов все просто, обычный scandir() и запись в массив, разве что запись в сессию нужного варианта происходит следующим образом:
$_SESSION['iconcaptcha'] = array_search($true_icon, $array_of_icons) + 1; 

где $true_icon — искомая нами иконка, а $array_of_icons конечный массив с иконками.

4. Создание конечного изображения из иконок.
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");         imagepng($tmp_sprite);          imagedestroy($tmp_sprite);         exit; } 
5. Получаем адрес, по которому будем вызывать создание полноценной картинки.
private function getCaptchaAddress($address) { //получим изображение по адресу $address         $req = end(explode('/', $_SERVER['REQUEST_URI']));         $req = explode('?', $req);          if ($req[0] == $address) $this->makeSprite(); } 
6. И наконец финальный метод, выводящий саму форму и все все все. Следовало бы тут, конечно, убрать стили, но т.к. тут задается background-image, который провоцирует вызов makeSprite() и собственно создается капча (а так же таким вот кривым методом решаем проблему с кэшированием), то решил оставить.
function getCaptchaForm() {         $this->getCaptchaAddress($this->address);          $captcha_form = '<style>                             #icon_captcha label div {                                 background-image: url(/'.$this->address.'?'.rand().');                                 width: '.$this->icon_size.'px;                                 height: '.$this->icon_size.'px;                             }                              #icon_captcha {                                 margin-bottom: 20px !important;                                 display: table !important;                             }                              #icon_captcha div {                                 width: '.$this->icon_size.'px;                                 display: inline-block;                                 margin-right: 20px;                             }                              #icon_captcha div input {                                 width: '.$this->icon_size.'px;                                 margin-left: 0;                                 float: left;                                 border: none;                             }                              #icon_captcha div label img {                                 border: none;                             }                          </style>                          <div id="icon_captcha">';          for ($i = 1; $i <= $this->icon_count; ++$i) {             $captcha_form .= '<div>                                 <label for="val_'.$i.'">                                     <div class="i'.$i.'" style="background-position: '                                             .(($i - 1)*(-$this->icon_size)).                                      'px 0;"></div>                                 </label>                                 <input type="radio" name="radio_val" id="val_'.$i.'" value="'.$i.'" />                               </div>';         }          $captcha_form .= '</div>';          return $captcha_form; } 

Вот как выглядит вызов на одном из примеров, происходит это дело в конфигурационном файле:

require(INC_DIR.'libs/IconCaptcha.class.php'); $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); 

Далее просто подставляем ICONCAPTCHA в нужную нам форму, а проверяем ее банальным сравнением: $_POST[‘radio_val’] == $_SESSION[‘iconcaptcha’].

Тут можно пощелкать демо: iconcaptcha.hut4.ru/
Тут посмотреть класс целиком: github.com/L1Qu0R/iconcaptcha/blob/master/IconCaptcha.class.php

ссылка на оригинал статьи http://habrahabr.ru/post/177805/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *