Создание капчи (captcha) на PHP
Со временем сайт набирает популярность и возникает необходимость в организации защиты от спам-ботов. В этой статье я расскажу как можно создать собственную капчу на PHP.
Капча (captcha) представляет из себя изображение с некоторым текстом, который предлагается набрать пользователю в поле ввода для подтверждения его "человечности". Дело в том, что человеку одинаково легко читать текст, независимо от того, является ли он непосредственно машинным текстом или изображением. В то время как компьютеру требуется гораздо более сложный алгоритм для "чтения" текста в виде картинки. Очевидно, что капча каждый раз должна отображать случайную последовательность символов.
Капчи используются, как правило, при заполнении каких-либо форм на сайте. Алгоритм работы следующий: на форме присутствует изображение-captcha с некой случайной последовательностью символов. Рядом с ним имеется поле для ввода содержимого капчи пользователем. Изображение, по сути, является PHP-скриптом, который его формирует. При этом сгенерированное текстовое содержимое капчи где-то сохраняется. При отправке формы скрипт сравнивает сохраненное значение капчи с тем, что ввел пользователь. Если значения совпадают, то запрос принимается, иначе отклоняется.
Предлагаемая мной реализация предельно проста, в то же время, в ней имеется поддержка TrueType шрифтов, а также альфа-канал (прозрачный фон), поэтому капча будет хорошо вписываться в любой дизайн.
Обратите внимание, что для работы скрипта требуется PHP-расширение GD, подключите его в настройках.
Для начала необходимо определиться со шрифтом. Соответствующий выбранному шрифту ttf-файл необходимо положить в директорию с будущим скриптом капчи. Я для капчи выбрал шрифт Comic Sans MS, ему соответствует файл comic.ttf. Файлы шрифтов можно найти в системном каталоге шрифтов вашей операционной системы, либо загрузить из Интернета.
Далее создается скрипт captcha.php. В начале скрипта объявляются необходимые переменные:
$letters = 'ABCDEFGKIJKLMNOPQRSTUVWXYZ';
В виде строки перечисляется алфавит. Это символы, которые будут участвовать в формировании капчи. Я перечислил заглавные латинские буквы, можно добавить, например, цифры.
$caplen = 6;
В этой переменной задается длина капчи (6 символов).
$width = 120; $height = 20;
Ширина и высота генерируемого изображения. Нужно, во-первых, подобрать размер в соответствии с дизайном вашего сайта, во-вторых, задать оптимальную ширину для указанной длины капчи, чтобы буквы не были слишком плотно прижаты друг к другу.
$font = 'comic.ttf';
Здесь указывается файл шрифта. В принципе, его можно разместить в поддиректории, допустим, fonts, тогда содержимое переменной должно быть такого формата: fonts/comic.ttf.
$fontsize = 14;
Размер шрифта.
С переменными разобрались. Далее, приступаем непосредственно к реализации. Во-первых, необходимо указать клиенту запрошенного скрипта, что ответный контент представляет из себя не текст, а изображение. Для этого переопределяем содержимое HTTP-заголовка content-type:
header('Content-type: image/png');
Создаем изображение с заданными размерами:
$im = imagecreatetruecolor($width, $height);
Выставляем флаг необходимости сохранения альфа-канала изображения:
imagesavealpha($im, true);
Создаем цвет фона. Это будет полностью прозрачный цвет:
$bg = imagecolorallocatealpha($im, 0, 0, 0, 127);
Заливаем этим цветом наше созданное изображение:
imagefill($im, 0, 0, $bg);
Этими действиями мы подготовили наше изображение для наложения на него капчи. Следующая строка необходима не всегда, зависит от настроек веб-сервера. Она переопределяет путь к поиску шрифтов. Оставьте ее закомментированной, если вдруг капча не будет формироваться при тестировании, а в логе ошибок Apache будет появляться ошибка "imagettftext(): Could not find/open font", попробуйте ее раскомментировать:
//putenv( 'GDFONTPATH=' . realpath('.') );Инициализируем переменную, в которой будет содержаться текстовое значение капчи:
$captcha = '';
Далее организовывается цикл с количеством итераций равным длине капчи (caplen):
for ($i = 0; $i < $caplen; $i++)
На каждом шаге цикла генерируется очередной символ капчи и рисуется на изображении. Берем случайный символ из нашего алфавита и добавляем его в капчу:
$captcha .= $letters[ rand(0, strlen($letters)-1) ];
Вычисляем положение сгенерированного символа на изображении по оси x:
$x = ($width - 20) / $caplen * $i + 10;
Это положение зависит от ширины изображения, длины капчи и порядкового номера символа. Далее мы добавляем немного "случайности" в это положение:
$x = rand($x, $x+4);
Вычисляем положение сгенерированного символа на изображении по оси y:
$y = $height - ( ($height - $fontsize) / 2 );
Положение зависит от размера шрифта и высоты изображения.
Генерируем случайный цвет для символа. Этот цвет не должен быть слишком светлым, поэтому каждый из компонентов цвета (R, G и B) генерируем в диапазоне 0-100:
$curcolor = imagecolorallocate( $im, rand(0, 100), rand(0, 100), rand(0, 100) );
Для текущего символа случайным образом генерируем его угол наклона в диапазоне -25..25 градусов, чтобы буквы на капче "плясали":
$angle = rand(-25, 25);
И, наконец, рисуем символ со всеми выше полученными характеристиками на изображении:
imagettftext($im, $fontsize, $angle, $x, $y, $curcolor, $font, $captcha[$i]);
На этой строке оканчивается тело цикла. Когда цикл отработает, в переменной captcha будет содержаться текстовое значение капчи, а изображение im будет представлять из себя отрисованную капчу. Необходимо где-то сохранить значение капчи, чтобы основной скрипт, который использует ее, мог проверить значение пользователя. Лучшее для этого место — сессионная переменная. Инициализируем сессию и сохраняем значение капчи:
session_start(); $_SESSION['captcha'] = $captcha;
Наконец, выводим сформированное изображение captcha:
imagepng($im);
И освобождаем память, выведенную под изображение:
imagedestroy($im);
Ниже вы можете увидеть демонстрацию работы полученной капчи (попробуйте обновить изображение):
Чуть позже добавлю готовый скрипт капчи и пример его использования в HTML-формах.
неплохо, но зачем так усложнять алгоритм рандома??? не проще хранить значение в массиве и посредством интерполяции выводить соответствующее изображение
Спасибо, отличная статья.
Агроменное спасибо аффтару!!! Изящная капчушка получаеццо. Они не пройдут!!!
Кстати, пример использования я так и не показал :)