Статья, в которой наглядно рассмотрим процесс создания своей собственной простой CAPTCHA в форме с использованием PHP, Bootstrap, jQuery и AJAX.

CAPTCHA (капча) – это простой тест, предназначенный для того, чтобы отличить человека от компьютера. Т.е. это такой тест, который человек решает легко, а компьютер научить его выполнять крайне сложно.

CAPTCHA используется для того, чтобы на сайте или блоге предотвратить выполнение компьютером (программами) различных действий: выполнения регистраций, отправления сообщений, скачиваний материалов и много другого. Т.е. иметь гарантию того, что действия выполняются людьми, а не роботами.

В большинстве случаев CAPTCHA отображается как некоторый искаженный или наложенный на фон текст (код), который посетителю сайта необходимо разобрать и ввести его в некоторое поле. Кроме текста (кода) используется и другие алгоритмы: найти среди множества картинок правильные, собрать пазл, переместить слайдер, нарисовать связь между несколькими картинками и др.

Разрабатывать CAPTCHA (капчу) будем для формы регистрации, расположенной в модальном окне. Данная форма будет состоять из двух полей: логина и email. Создадим форму и модальное окно с использованием классов и компонентов платформы Twitter Bootstrap 3.

Создание модального окна и формы с CAPTCHA


Файл index.html:

<html lang="ru">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Валидация формы с captcha</title>  
  <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />
</head>
<body>
<!-- Модальное окно -->
<div id="myModal" class="modal fade" tabindex="-1" role="dialog">
  <div class="modal-dialog">
    <div class="modal-content">
      <!-- Заголовок модального окна -->
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal">×</button>
        <h4 class="modal-title" id="myModalLabel">Регистрация</h4>
      </div>
      <!-- Основная часть модального окна, содержащая форму для регистрации -->
      <div class="modal-body">
        <!-- Форма для регистрации -->
  	<form id="myForm" method="post" role="form" name="myForm">
	  <!-- Блок для ввода логина -->
	  <div class="form-group has-feedback">
	    <label for="login" class="control-label">Введите логин:</label>
            <div class="input-group">
	      <span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>         
	      <input type="text" class="form-control" required="required" name="login" pattern="[A-Za-z]{6,}" value="">
	    </div>
	    <span class="glyphicon form-control-feedback"></span>
	  </div>
	  <!-- Блок для ввода email -->
	  <div class="form-group has-feedback">
	    <label for="email" class="control-label">Введите email:</label>
            <div class="input-group">
	      <span class="input-group-addon"><i class="glyphicon glyphicon-envelope"></i></span>
	      <input type="email" class="form-control" required="required" name="email" value="">
	    </div>
	    <span class="glyphicon form-control-feedback"></span>
	  </div>
          <!-- Конец блока для ввода email-->
	  <hr>
          <!--Изображение, содержащее код CAPTCHA-->		  
	  <img id="img-captcha" src="captcha.php">
          <!--Элемент, запрашивающий новый код CAPTCHA-->
	  <div id="reload-captcha" class="btn btn-default"><i class="glyphicon glyphicon-refresh"></i> Обновить</div>
	  <!--Блок для ввода кода CAPTCHA-->
	  <div class="form-group has-feedback">
            <label id="label-captcha" for="captcha" class="control-label">Пожалуйста, введите указанный на изображении код:</label>
	    <input id="text-captcha" name="captcha" type="text" class="form-control" required="required" value="">
	    <span class="glyphicon form-control-feedback"></span>
          </div>
        </form>
      </div>
      <!-- Нижняя часть модального окна -->
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Отмена</button>
        <button id="save" type="button" class="btn btn-primary">Регистрация</button>
      </div>
    </div>
  </div>
</div>

<div class="container">
  <div class="alert alert-success hidden" id="success-alert">
    <h2>Успех</h2>
    <div>Ваши данные были успешно отправлены.</div>
  </div>
  <!-- Кнопка для открытия модального окна -->
  <button id="btn-modal" type="button" class="btn btn-lg btn-success" data-toggle="modal" data-target="#myModal">
    Регистрация
  </button>
</div>

<script src="js/jquery-2.1.4.min.js" type="text/javascript"></script>
<script src="js/bootstrap.min.js" type="text/javascript"></script>
</body>
</html>

Принцип работы создаваемой CAPTCHA изобразим на следующем рисунке:

Принцип работы создаваемой CAPTCHA

Формировать код CAPTCHA будем на сервере с помощью файла captcha.php. В этом файле мы создадим переменную captchastring, которая будет состоять из цифр, строчных и прописных букв латинского алфавита. Создавать случайный код будем посредством перемешивания этих букв и цифр с помощью PHP функции str_shuffle() и последующего извлечения из полученной строки первых 6 символов. Полученная подстрока будет являться CAPTCHA кодом, которую сохраним в эту же переменную (captchastring). После этого будем хранить код CAPTCHA в переменной сессии code.

Чтобы передать CAPTCHA код в форму, его предварительно необходимо защитить от роботов, т.е. сделать так, чтобы его мог прочитать только человек. Для этого возьмем некоторый фон (background.png) и напишем на нём с помощью некоторого шрифта код. Полученное изображение будем использовать для передачи его в форму.

Файл captcha.php:

<?php
//открывает сессию
session_start();

//присваивает PHP переменной captchastring строку символов
$captchastring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz';
//получает первые 6 символов после их перемешивания с помощью функции str_shuffle
$captchastring = substr(str_shuffle($captchastring), 0, 6);
//инициализация переменной сессии с помощью сгенерированной подстроки captchastring,
//содержащей 6 символов
$_SESSION["code"] = $captchastring;

//Генерирует CAPTCHA

//создает новое изображение из файла background.png 
$image = imagecreatefrompng('background.png');
//устанавливает цвет (R-200, G-240, B-240) изображению, хранящемуся в $image
$colour = imagecolorallocate($image, 200, 240, 240);
//присваивает переменной font название шрифта
$font = 'oswald.ttf';
//устанавливает случайное число между -10 и 10 градусов для поворота текста 
$rotate = rand(-10, 10);
//рисует текст на изображении шрифтом TrueType (1 параметр - изображение ($image), 
//2 - размер шрифта (18), 3 - угол поворота текста ($rotate), 
//4, 5 - начальные координаты x и y для текста (18,30), 6 - индекс цвета ($colour),
//7 - путь к файлу шрифта ($font), 8 - текст ($captchastring) 
imagettftext($image, 18, $rotate, 28, 32, $colour, $font, $captchastring);
//будет передавать изображение в формате png
header('Content-type: image/png');
//выводит изображение
imagepng($image);
?>

Проверять CAPTCHA код, который ввёл пользователь, будем в файле verify.php на сервере. Осуществлять это будем посредством сравнения значения хранящейся в переменной сессии code ($_SESSION["code"]) и значения $_POST["captcha"]. В качестве результата сравнения будем возвращать строку true, если данные соответствуют или false, если данные не соответствуют.

Файл verify.php:

<?php
//открывает сессию
session_start();
//проверяет соответствие коду CAPTCHA
if ($_SESSION["code"] == $_POST["captcha"]) {
  //сообщаем строку true, если код соответствует
  echo 'true';
} 
else {
  //сообщаем строку false, если код не соответствует
  echo 'false';
}
?>

Последним этапом разработки формы с CAPTCHA является создание кода JavaScript с применением библиотеки jQuery и AJAX.

Данный код будет выполнять следующее:

  • выводить новый код CAPTCHA при открытии модального окна;
  • выводить новый код CAPTCHA при нажатии на кнопку "Обновить";
  • выполнять валидацию полей формы "Логин" и "Email" с помощью HTML5 функции checkValidity();
  • визуально выделять поля формы, которые прошли и не прошли валидацию;
  • отправлять на проверку код CAPTCHA, который ввёл пользователь на сервер через AJAX в файл verify.php и получать от него результат. Если результат положительный и форма валидна, то код будет скрывать модальное окно и отображать сообщение об успехе. А если результат отрицательный, то код будет выделять поле, содержащее код CAPTCHA, который ввёл пользователь, красным цветом и значком "Ошибка".

Код JavaScript, который необходимо поместить в отдельный файл и подключить к нужным страницам (для демонстрации поместим его в конец файла index.html):

$(function() {
//выводит новый код CAPTCHA при открытии модального окна  
$('#myModal').on('show.bs.modal', function () {
  $('#img-captcha').attr('src', 'captcha.php?id='+Math.random()+'');
});
//выводит новый код CAPTCHA при нажатии на кнопку Обновить
$("#reload-captcha").click(function() {
  $('#img-captcha').attr('src', 'captcha.php?id='+Math.random()+'');
});  
//при нажатии на кнопку Регистрация (id="save")
$('#save').click(function() {
  //переменная formValid
  var formValid = true;
  //перебирает все элементы управления input, кроме CAPTCHA 
  $('input').each(function() {
    //если текущий элемент CAPTCHA, то пропустить его
    if  ($(this).attr('id') == 'text-captcha') { return true; }
    //найти предков, которые имеют класс .form-group, для установления success/error
    var formGroup = $(this).parents('.form-group');
    //найти glyphicon, который предназначен для показа иконки успеха или ошибки
    var glyphicon = formGroup.find('.form-control-feedback');
    //для валидации данных используем HTML5 функцию checkValidity
    if (this.checkValidity()) {
      //добавить к formGroup класс .has-success, удалить has-error
      formGroup.addClass('has-success').removeClass('has-error');
      //добавить к glyphicon класс glyphicon-ok, удалить glyphicon-remove
      glyphicon.addClass('glyphicon-ok').removeClass('glyphicon-remove');
    } else {
      //добавить к formGroup класс .has-error, удалить .has-success
      formGroup.addClass('has-error').removeClass('has-success');
      //добавить к glyphicon класс glyphicon-remove, удалить glyphicon-ok
      glyphicon.addClass('glyphicon-remove').removeClass('glyphicon-ok');
      //отметить форму как не валидную 
      formValid = false;  
    }
  });
  //проверяем элемент input, в который пользователь вводит код CAPTCHA
  //получаем значение элемента input, содержащего код CAPTCHA
  var captcha = $("#text-captcha").val();
  //если код CAPTCHA пустой, то сразу сообщаем, что он не правильный
  if (captcha=='') {
    inputCaptcha = $("#text-captcha");
    formGroupCaptcha = inputCaptcha.parents('.form-group');
    glyphiconCaptcha = formGroupCaptcha.find('.form-control-feedback');
    formGroupCaptcha.addClass('has-error').removeClass('has-success');
    glyphiconCaptcha.addClass('glyphicon-remove').removeClass('glyphicon-ok');
  }
  //иначе запрашиваем результат у сервера через ajax
  else  { 
    var dataString = 'captcha=' + captcha;
    $.ajax({
      type: "POST",
      url: "verify.php",
      data: dataString,
      success: function(result) {
	inputCaptcha = $("#text-captcha");
        formGroupCaptcha = inputCaptcha.parents('.form-group');
        glyphiconCaptcha = formGroupCaptcha.find('.form-control-feedback');
	//если результат, который вернул сервер, равен true, 
	//то отмечаем, что код валидный и изменяет цвет элементов на зелёный
        if (result==="true") {
          formGroupCaptcha.addClass('has-success').removeClass('has-error');
          glyphiconCaptcha.addClass('glyphicon-ok').removeClass('glyphicon-remove');
          if (formValid) {
            //скрыть модальное окно
            $('#myModal').modal('hide');
            //отобразить сообщение об успехе
            $('#success-alert').removeClass('hidden');
            $('#success-alert').removeClass('hidden');
          }         
        } 
	//иначе отмечает, что код не валидный и изменяет цвет элементов на красный
	else {
          formGroupCaptcha.addClass('has-error').removeClass('has-success');
          glyphiconCaptcha.addClass('glyphicon-remove').removeClass('glyphicon-ok');
        }
      }
    });
  }
});
});

На следующем изображении отображена не валидная форма:

Форма, в которой все поля заполнены пользователем не правильно

На этом изображении отображена форма, у которой все поля, заполненные пользователем, валидны:

Форма, в которой все поля заполнены пользователем правильно