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
Начало здесь
Продолжение
Это репост заметки из моего блога на сайте http://tolik-punkoff.com
Оригинал заметки находится здесь: http://tolik-punkoff.com/2017/04/13/asci