Статьи

Простые капчи с PHP и GD

sample image

К настоящему времени мы все столкнулись с изображениями с картинки в онлайн-формах. Капчи — неизбежное зло, и эта статья научит вас, как они сделаны.

Обратите внимание, что, хотя существуют лучшие, автоматические сторонние решения для капч, такие как ReCaptcha, это руководство имеет целью просто объяснить и продемонстрировать, как на самом деле работает такая технология. Мы не будем объяснять, что на самом деле представляют собой капчи, поскольку предполагается, что это общеизвестно и уже более подробно описано в другом месте .

Рисование капчи

Перед продолжением необходимо установить библиотеку GD (Graphics Draw). Эта библиотека позволяет рисовать графику и изображения с помощью встроенных функций PHP. Чтобы установить его, запустите sudo apt-get install php5-gd или, если это не операционная система, основанная на Ubuntu, следуйте инструкциям .

Капчи обычно состоят из 3 вещей — формы, искажения и текста .
Мы выполним шаги, указанные ниже:

  1. Показать пустое изображение в браузере.
  2. Создать форму.
  3. Генерация случайных линий.
  4. Генерация случайных точек.
  5. Генерация случайного текста.

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

Показать пустое изображение

Изображение будет обрабатываться HTML, как если бы внешнее изображение отображалось с использованием тега «img». Используются две функции — одна для создания изображения и другая для отображения.

 <? php session_start (); 
 ?> 

     <title> demo.php </title> 
     <body   style = " background - color :# ddd ;   " > 

     <? php create_image (); display (); 
     /***** definition of functions *****/ 
     function  display () 
     { 
         ?> 

         <div   style = " text - align : center ; " > 
             <h3> TYPE THE TEXT YOU SEE IN THE IMAGE </h3> 
             <b> This is just to check if you are a robot </b> 

             <div   style = " display : block ; margin - bottom : 20px ; margin - top : 20px ; " > 
                 <img   src = "image.png" > 
             </div> //div1 ends </div>  //div2 ends <? php } 

     function  create_image () 
     { $image =  imagecreatetruecolor ( 200 ,   50 ); imagepng ( $image ,   "image.png" ); 
     } 

     ?> 
     </body> 
 <? php ?> 

Первая строка указывает на начало сеанса пользователя на нашей странице.

Функция display () не имеет ничего, кроме обычного HTML-кода, который отображает изображение в браузере. Кроме этого, только стилизация делается для того, чтобы вывод выглядел презентабельно.

Внутри функции create_image() переменная используется для ссылки на изображение, возвращаемое функцией imagecreatetruecolor() которая принимает ширину и длину изображения в качестве аргументов. imagepng() создает изображение png с указанным именем и путем (в том же каталоге).

Черное изображение будет выводиться после нашего первого шага.

scrn1

Обратите внимание, что функция imagepng() будет последней строкой нашей функции, и все последующие шаги должны быть вставлены в create_image() перед этим вызовом функции, иначе они не вступят в силу.

Создать форму

Для капчи может быть выбрана любая форма. Мы выберем прямоугольник с помощью функции imagefilledrectangle() . Требуется пять аргументов — ссылка на изображение, начало x-pos, начало y-pos, окончание x-pos, окончание y-pos и цвет фона. Вы можете использовать соответствующую функцию для эллипса для генерации эллиптической капчи.

Функция imagecolorallocate() выделяет цвет для переменной, так как принимает в качестве аргументов комбинацию цветов RGB. Следующий код должен быть добавлен в функцию create() .

 $background_color =  imagecolorallocate ( $image ,   255 ,   255 ,   255 ); imagefilledrectangle ( $image , 0 , 0 , 200 , 50 , $background_color ); 

Предыдущее изображение будет белым после этого шага.

sample image

Генерация случайных линий.

Теперь мы фактически начинаем с того, что искажаем часть капчи. В PHP линии генерируются от начальной точки (x1, y1) до конечной точки (x2, y2). Теперь, когда мы хотим, чтобы наши линии касались обоих концов блока, мы будем сохранять координаты <x1,x2> как <0,200> то есть полную ширину нашего блока. Координаты <y1,y2> будут генерироваться случайным образом. Это создаст только одну случайную строку. Мы сгенерируем несколько строк, поместив эту функцию в цикл for.

 $line_color =  imagecolorallocate ( $image ,   64 , 64 , 64 );  
 for ( $i = 0 ; $i < 10 ; $i ++)   { imageline ( $image , 0 , rand ()% 50 , 200 , rand ()% 50 , $line_color ); 
 } 

Функция imageline() принимает координаты x1, x2, y1, y2 в качестве аргументов в указанном порядке, кроме ссылки на изображение и цвета линии. Цвет линии был назначен так же, как и цвет фона на предыдущем шаге.

Координата y задается как rand()*%50 потому что это высота нашего поля и всегда будет возвращать значение меньше 50. В качестве альтернативы вы можете использовать rand(0,50) . Они дадут одинаковый выходной диапазон.

sample image

Генерация случайных точек.

Случайные точки будут генерироваться так же, как и случайные линии. Используется функция imagesetpixel() . Эта функция принимает значение координат, где точка будет помещена в поле.

 $pixel_color =  imagecolorallocate ( $image ,   0 , 0 , 255 ); 
 for ( $i = 0 ; $i < 1000 ; $i ++)   { imagesetpixel ( $image , rand ()% 200 , rand ()% 50 , $pixel_color ); 
 }    

X-координата генерируется случайным образом с помощью rand()*%200 как это ширина нашего поля, и это всегда будет возвращать значение меньше 200. В качестве альтернативы вы можете использовать rand(0,200) . Они дадут одинаковый выходной диапазон. Координата y генерируется как на шаге линий.

sample image

Создать случайный текст

Мы случайным образом укажем на позицию в строке (которая содержит алфавит как в нижнем, так и в верхнем регистре) и назначим ее переменной $letter

 $letters =   'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' ; $len =  strlen ( $letters ); $letter =  $letters [ rand ( 0 ,  $len - 1 )]; $text_color =  imagecolorallocate ( $image ,   0 , 0 , 0 ); 

Когда помещено в цикл, это выглядит так

 for   ( $i =   0 ;  $i <   6 ; $i ++)   { $letter =  $letters [ rand ( 0 ,  $len - 1 )]; imagestring ( $image ,   5 ,    5 +( $i * 30 ),   20 ,  $letter ,  $text_color ); $word .= $letter ; 
 } $_SESSION [ 'captcha_string' ]   =  $word ; 

Мы объясним линии

 $word .= $letter ; $_SESSION [ 'captcha_string' ]   =  $word ;     

в следующем разделе.

Функция imagestring() записывает текст в наше изображение. У него есть 6 аргументов:

  1. Ссылка на изображение.
  2. Размер шрифта текста (максимум 5).
  3. X-координата (изменяется пропорционально для каждого алфавита).
  4. y-координата (осталась прежней, хотя мы могли бы также изменить это случайно).
  5. Фактическая строка для записи.
  6. Цвет шрифта текста.

Вы также можете использовать функцию imagettftext() если вы хотите иметь больший шрифт и другой стиль шрифта. Требуется 2 дополнительных аргумента для угла и стиля шрифта текста.

Расчет x-координаты производится осмотром. Грубо говоря, буквы разнесены примерно на 35 пикселей (5+($i*30)) где $i=0,1,2,3,4,5,6 . Это потому, что если бы мы держали это значение в пределах 15-20 пикселей, была бы возможность перекрытия двух букв. Если бы значение было больше 40px, буквы вообще не поместились бы в поле.

Это сгенерирует текст из 6 букв алфавита. Мы всегда можем создать больше случайности, изменяя аспекты, которые были постоянными из-за простоты, такие как цвет, координаты y и т. Д.

Финальная капча будет выглядеть так

sample image

Текст, написанный на капче, будет меняться при каждом обновлении страницы.
Больше случайности может быть достигнуто путем создания дизайнов с пикселями или путем изменения цвета или размера.

Пользователи

Именно здесь принимается ответ пользователя, и после его обработки он / она получает ответ. Сначала создается простая форма с текстовым полем ввода и кнопкой отправки. Там может быть много способов обработки капчи в соответствии с требованиями сложных веб-приложений. Но для простоты этого примера мы будем обрабатывать его на той же странице.

Две строки, оставленные необъясненными в предыдущих фрагментах кода, теперь вступают в игру:

  1. $word.=$letter; — оператор конкатенации . используется для добавления всех отдельных букв одна за другой, генерируя 6-буквенное слово.
  2. $_SESSION['captcha_string'] = $word; Наша строка капчи хранится в переменной сеанса, которая будет использоваться в целях проверки.

Мы изменим определение display() чтобы добавить структуру, похожую на форму.

Будут использоваться две кнопки отправки: одна для отправки строки, а другая для обновления страницы.

Следующие строки будут добавлены между двумя закрывающими тегами div (см. Комментарии в предыдущей функции display() )

 function display() { ?> 

     <div   style = " text - align : center ; " > 
         <h3> TYPE THE TEXT YOU SEE IN THE IMAGE </h3> 
         <b> This is just to check if you are a robot </b> 

         <div   style = " display : block ; margin - bottom : 20px ; margin - top : 20px ; " > <img src="image <? php echo $_SESSION [ 'count' ]   ?> .png"> </div> <form action=" <? php echo $_SERVER [ 'PHP_SELF' ];   ?> " method="POST" / > <input   type = "text"   name = "input" /> 
         <input   type = "hidden"   name = "flag"   value = "1" /> 
         <input   type = "submit"   value = "submit"   name = "submit" /> 
         </form> <form action=" <? php echo $_SERVER [ 'PHP_SELF' ];   ?> " method="POST"> <input   type = "submit"   value = "refresh the page" > 
         </form> 
     </div> 

 <? php } 

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

  1. если страница только что загрузилась.
  2. если ответ пользователя был неверным.

Первое условие выполняется с помощью $flag который устанавливается в «1» при каждом нажатии кнопки «Отправить». Первоначально для него было установлено любое другое значение. Второе условие достигается проверкой, совпадает ли значение, хранящееся в нашей переменной сеанса, с пользовательским вводом (см. Код ниже).

Чтобы достичь этого, мы заменим следующие строки нашего начального шага в начале статьи:

  create_image (); display (); 

с:

 $flag =   5 ; 

 if   ( isset ( $_POST [ "flag" ]))   // check that POST variable is not empty 
 { $input =  $_POST [ "input" ]; $flag =  $_POST [ "flag" ]; 
 } 

 if   ( $flag ==   1 )   // submit has been clicked 
 { 
     if   (isset($_SESSION['captcha_string']) && $input ==  $_SESSION [ 'captcha_string' ])   // user input and captcha string are same 
     { 

         ?> 

         < div style = "text-align:center;" > 
             <h1> Your  answer is  correct !</ h1 > 

             < form action = " <?php echo $_SERVER['PHP_SELF']; ?>"  method = "POST" >   // refresh the page 
                 < input type = "submit"  value = "refresh the page" > 
             < /form> </ div > 

     <? php }   else   // incorrect answer, captcha shown again 
     { 

         ?> 
         < div style = "text-align:center;" > 
             <h1> Your  answer is  incorrect !< br > please try  again < /h1> </ div > 
         <? php create_image (); display (); 
     } 

 }   else   // page has just been loaded 
 { create_image (); display (); 
 } 

Обратите внимание, что функции create_image() и display() вызываются только в соответствии с двумя условиями, описанными выше.

Нам понадобится переменная сеанса с предыдущей страницы, поэтому сессия здесь не уничтожается. Сеанс будет автоматически уничтожен после закрытия окна браузера.

Капча будет выглядеть так

sample image

Если ввод неправильный, только тогда пользователю будет предложено снова.

sample image

Если ввод правильный, пользователю будет показано сообщение.

sample image

Существует небольшая оговорка — когда пользователь нажимает кнопку «Назад», любое изображение, уже имеющееся в кэше браузера, не будет перезагружаться, в то время как страница делает это. В POST-запросе кнопка «Назад» браузера покажет страницу «истек срок действия документа», но когда запрос GET, изображение не регенерируется.

Решение простое — каждый раз создавать уникальные имена изображений, чтобы браузер не находил их в кеше. Мы добавим уникальную строку, возвращенную нам встроенной функцией time() к имени изображения при создании и отображении в браузере.

Добавьте эту строку чуть ниже, где вы начали свою сессию:

 $_SESSION [ 'count' ]= time ();   // unique string stored 

Замените тег img src в функции display() на

 <img src="image <? php echo $_SESSION [ 'count' ]?> .png"> 

И часть, где мы создали изображение png в функции create_image() , также будет заменена на

 imagepng ( $image , "image" . $_SESSION [ 'count' ]. ".png" ); 

Изображения теперь будут называться что-то вроде image39342015.png . Эта процедура будет создавать изображения столько раз, сколько обновляется страница, что может тратить огромное количество дискового пространства, поэтому мы должны убедиться, что перед созданием изображения все остальные изображения расширения png будут удалены. Добавьте следующее непосредственно перед imagepng() функции imagepng() .

 $images =  glob ( "*.png" ); 
 foreach ( $images as  $image_to_delete ) 
 { unlink( $image_to_delete );       
 } 

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

Загрузите полный код здесь .

Вывод

Создание различных типов капч в PHP очень просто. В этой статье были рассмотрены три основные вещи, используемые для создания стандартной капчи — форма, искажение и текст . Эта статья была подтверждением концепции, и код, представленный здесь, не должен использоваться в производстве — тем более, что существуют отличные альтернативы, такие как ReCaptcha , которые также поддерживают вывод звука, чтобы помочь людям с нарушениями слуха. Мы надеемся, что вы нашли эту статью интересной. Оставьте свои комментарии и отзывы ниже!