ASCII-каптча, каптча псевдографикой. Часть II. В которой генерируется каптча.

Итак, закончили мы на том, что нарисовали все цифры, создали массивы php с псевдографическими изображениями цифр и написали отладочную функцию, которая выводит конкретную цифру. Продолжаем!

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

$pgstring="#$%@!?0"; //символы, из которых будут составлены цифры

Аналогично и для «пустых» символов:

$spstring="- "; //пробельные символы

Тут все зависит от вкуса, цвета и лично ваших глазок. Главное, чтобы в строке с возможными «пустыми» символами и в строке с возможными символами, изображающими цифру, не встретилось одинаковых символов, иначе при определенных условиях, пользователь вполне может получить вот такую вот гадость, вместо каптчи:

###### ###### ###### ###### ###### ###### ######
###### ###### ###### ###### ###### ###### ######
###### ###### ###### ###### ###### ###### ######
###### ###### ###### ###### ###### ###### ######
###### ###### ###### ###### ###### ###### ######

Заведем еще две переменные, которые будут хранить выбранные символы для «пробельных» и символов изображения:

$pgchar="";
$spchar="";

Пока просто оставим их пустыми, потом к ним еще вернемся.

Приступаем к написанию скрипта. Небольшие и вспомогательные функции.

Первым делом, нам понадобится функция, генерирующая код каптчи нужной длины. Она довольно проста:

function getcaptchacode($codelen)
		{
			$ans="";
			for ($i=0;$i<$codelen;$i++)
			{
				$r=rand(0,9);
				$ans=$ans.$r;
			}
			return $ans;
		}

В качестве параметра ($codelen) функции передается длина необходимого нам кода каптчи, далее функция генерирует строку, состоящую из символов от 0 до 9 заданной длины. На самом деле на каждой итерации цикла for генерируется ($r=rand(0,9);
) случайное число в диапазоне от 0 до 9, но PHP такой странный язык, что отдельно (если кто не знает) заниматься преобразованием числа в строку, нам не требуется. После генерации числа от 0 до 9 просто присоединяем его к строке $ans ($ans=$ans.$r;), а конвертацией занимается интерпретатор.
Если философствовать, то это вообще-то большой минус, но в данном случае он вполне себе плюс. Но философствовать мы не будем, лучше продолжим.

Функция, возвращающая случайный символ из заданной строки.

//получает случайный символ из строки
function randomchar($srcstr)
{
        $i=rand(0,strlen($srcstr)-1);			
	return $srcstr{$i};
}

Тут вообще все просто — на входе передаем строку с набором символов ($srcstr), случайно выбираем позицию любого символа в строке, начиная с первого (нулевого) и заканчивая последним (длина строки, возвращаемая стандартной функцией strlen - 1). Записываем полученное значение в переменную $i ($i=rand(0,strlen($srcstr)-1);) обращаемся к конкретному символу в строке по ранее сохраненному номеру (позиции) с помощью конструкции «{}» и возвращаем его (return $srcstr{$i}).

Формирование изображения каптчи. Определение функции, основных внутренних переменных и подготовительные действия.

Этим делом будет заниматься функция getpgnum:

function getpgnum($numstr,$numarr,$spchr,$spctr,$startsp,$endsp)
		{
			...
		}

Ей передаются следующие параметры:

$numstr (строка, состоящая из цифр) — Строка цифр, в случае каптчи, это будет код, сгенерированный ранее вызванной функцией getcaptchacode.
$numarr — массив, содержащий псевдографические изображения всех цифр (ранее созданный $allnum)
$spchr (строка) — символ для промежутка между цифрами каптчи
$spctr (целое число) — количество символов между цифрами каптчи.
Т.е. если мы в качестве параметра $spchr зададим символ [!], а в параметре $spctr передадим число, например 4, то цифры каптчи будут разделены четырьмя восклицательными знаками. Вообще использовать можно любой символ, даже случайно выбранный. На практике я использую пробел. А тут восклицательные знаки для наглядности.

$startsp (логическое значение) — если установлено в true, то перед самим изображением каптчи так же будут добавлены символы, заданные в параметре $spchr в количестве заданном в параметре $spctr
$endsp (логическое значение) — то же самое, что и предыдущий параметр, но только для пробелов в конце строк изображения каптчи.

Определяем основные внутренние переменные функции:

Переменная, в которой будет храниться готовое псевдографическое изображение каптчи — результат работы функции:
$ans="";
Переменная, для хранения строки из пробелов между числами:
$spstr="";
Переменная, для хранения отдельной цифры из строки $numstr:
$num=0;
Текущая строка формируемого псевдографического изображения:
$curline="";

Проверяем, что строка в переменной $numstr состоит только из цифр:

if (!is_numeric($numstr)) //если в строку запихали не цифру
{
	echo ("ERROR: NOT NUMBER ".$numstr); //выводим ошибку и
                                            //завершаем скрипт
	die();
}

Формируем строку символов между цифрами каптчи:

//формируем строку пробельных символов
for ($i=0;$i<$spctr;$i++)
{
       $spstr=$spstr.$spchr;
}

Формирование изображения каптчи. Краткое описание алгоритма.

Формируя изображение каптчи, алгоритм будет действовать примерно, как ЭЛТ-монитор, т.е. формировать изображение сразу всех цифр, но построчно, т.е. брать первую строку, из всех изображений цифр, указанных в переменной $numstr, дополнять ее после перехода к следующей цифре из переменной $numstr пробелами. Получившуюся строчку при надобности обрабатывать (добавлять начальные и удалять конечные пробелы, добавлять перенос строки) и присовокуплять обработанную строчку к значению, возвращаемому функцией.

Вот видео, иллюстрирующее процесс:


Прямая ссылка

Формирование изображения каптчи. Основной код с описанием.

//проходим по всем строчкам изображения
//цифры все одинаковой высоты - 5 строк
for ($j=0;$j<5;$j++)
{
        //проходим по строке с числом
	for ($i=0;$i<strlen($numstr);$i++)
	{
		$num=$numstr{$i}; //вытаскиваем отдельную цифру						$imgnum=$numarr[$num]; //вытаскиваем изображение конкретной цифры
		//$imgnum[$j] - конкретная строчка изображения
					
		//формируем текущую линию для изображения всех цифр каптчи
		$curline=$curline.$imgnum[$j].$spstr;
	}
				
	//линия сформирована
	if ($startsp) //если нужны стартовые пробелы - добавляем
	{
		$curline=$spstr.$curline;
	}
				
        if (!$endsp) //если НЕ нужны конечные пробелы - удаляем
	{
		$curline=substr($curline,0,strlen($curline)-$spctr);
	}
				
	$ans=$ans.$curline."\r\n"; //в конце строчки добавляем перенос строки
	$curline=""; //очищаем переменную для хранения текущей строки каптчи
}

Во внешнем цикле (for ($j=0;$j<5;$j++)) проходим по всем строчкам изображения каптчи, поскольку у нас все цифры одинаковой высоты, то это можно прямо на месте и указать ($j<5). Сразу же начинаем внутренний цикл, формирующий конкретную строчку изображения всей каптчи (for ($i=0;$i<strlen($numstr);$i++)). Этот цикл пройдет по всей строке с заданным кодом ($numstr).

В нем сначала извлекаем из строки конкретную цифру и сохраняем ее значение в переменной $num ($num=$numstr{$i}).

Далее, копируем псевдографическое изображение отдельной цифры в переменную $imgnum ($imgnum=$numarr[$num];).

Формируем текущую строку ($curline) изображения всех цифр каптчи с пробелами ($spstr) между строками изображения конкретной цифры ($curline=$curline.$imgnum[$j].$spstr). Переменная $j, задается во внешнем цикле - это номер строки изображения, обрабатываемый в данный момент.

После внутренний цикл завершается, и готовая строка ($curline) дообрабатывается во внешнем цикле, ей добавляются или из нее удаляются начальные/конечные пробелы, в зависимости от установленных переменных ($startsp и $endsp):

//линия сформирована
if ($startsp) //если нужны стартовые пробелы - добавляем
{
    $curline=$spstr.$curline;
}
				
if (!$endsp) //если НЕ нужны конечные пробелы - удаляем
{
    $curline=substr($curline,0,strlen($curline)-$spctr);
}

Далее добавляются символы переноса строки, сформированная строка присоединяется к общему ответу функции:
$ans=$ans.$curline."\r\n";
Текущая строка изображения обнуляется, и внешний цикл переходит к формированию следующей строки.

$curline="";   //очищаем переменную для хранения текущей строки
                         //каптчи

Если цикл формирования изображения завершился, то функция возвращает значение:

return $ans;

Замена символов в изображении на случайные

С функциями вроде закончили. Осталось, как я и обещал в самом начале, немного подгадить злодею, подбирающему каптчу. Ранее были заданы переменные $pgchar и $spchar, пока никак не использованные, строки $pgstring и $spstring содержащие заданный набор символов, а также создана функция randomchar, возвращающая случайный символ из строки.
В первой части мы условились, что символы, формирующие изображение цифры, это символы $, а символы, занимающие пустые места в изображении - *.

Настало время ОТКРЫТЬ ВРАТА ШТАЙНЕРА, ХА-ХА-ХА использовать это и функцию randomchar, наконец.

После области функций пишем следующий код.

1. Случайно выбираем символ для символа изображения ($pgchar) и пустого ($spchar), из содержащихся в заранее заданных строках ($pgstring и $spstring):

$pgchar=randomchar($pgstring);
$spchar=randomchar($spstring);

2. Теперь заменяем заранее определенные символы * и $ в массиве с изображением цифр, на полученные случайные:

//заменяем символы в псевдографике на случайные
for ($i=0; $i<count($allnum); $i++) //цикл по массиву со всеми цифрами
{
for ($j=0; $j<count($allnum[$i]); $j++) //цикл по всем строчкам с псевдографикой
	{
		$allnum[$i][$j]=str_replace("$",$pgchar,$allnum[$i][$j]);
		$allnum[$i][$j]=str_replace("*",$spchar,$allnum[$i][$j]);
	}
}

Итого

В данный момент, мы имеем готовый include-модуль, формирующий код каптчи и ее псевдографическое изображение. В следующей части будут примеры ее использования.

Код каптчи

Смотреть на PasteBin

Начало здесь
Продолжение

2 Responses to ASCII-каптча, каптча псевдографикой. Часть II. В которой генерируется каптча.

  1. Pingback: ASCII-каптча, каптча псевдографикой. Часть I. | Персональный блог Толика Панкова

  2. Pingback: ASCII-каптча, каптча псевдографикой. Часть III. Примеры использования. | Персональный блог Толика Панкова

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

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