В этой заключительной статье я покажу, как можно легко и аккуратно реализовать материал, изложенный в предыдущих статьях, при помощи RegSubmitter
RegSubmitter изначально задумывался мною как гибкий фреймворк для быстрого написания авторегистраторов. Поэтому в отличие от предыдущих разов, вам не надо будет задумываться обо всякой рутине вроде считывания данных из файла/запроса, заморачиваться с одновременной регистрацией нескольких аккаунтов и т.д. Вы напишете логику, а RegSubmitter сделает все остальное.
Сразу скажу, что в этой статье я буду пользоваться версией RegSubmitter 1.4.0, которая пока официально не зарелизена, но уже скоро будет :) Впрочем, все нижеизложенное будет работать и в 1.3.6, за исключением подтверждения по email.
Прежде всего, отмечу, что в RS есть два типа модулей: простые и "универсальные". Простые работают только с одним конкретным сервисом, и "универсальные" могут работать со многими сайтами на одинаковых движках. В рамках этой статьи мы каснемся только простых модулей и будем рассматривать все на примере memori.ru.
Модули в RegSubmitter разбиваются по категориям, и располагаются в папке modules/Category_Name/Module_ID/module_ID.php. Здесь module_ID - это буквенно-цифровой идентификатор модуля, который должен быть уникален для каждого модуля (впрочем, это не означает, что он должен быть нечитаем ;) ). Например, модуль, работающий с сервисом Memori.ru находится по адресу /modules/Rus/Memori/Memori.php.
В этом файле должен находиться класс с именем, совпадающим с Module_ID, и при этом унаследованным от класса Module.
Ниже я приведу полный код модуля с комментариями, но перед этим сделаю несколько заметок.
Первая состоит в том, что в начале класса идут параметры, задающие свойства модуля и то, как он будет обрабатываться RegSubmitter-ом. Во-вторых, я не буду отдельно описывать API RegSubmitter'a, но по ходу дела буду пояснять назначение тех или иных функций и их параметров.
class Memori extends Module
{
// Параметры модуля
var $name = 'Memori'; // Имя сервиса, отображаемое пользователю
var $id = 'Memori'; // Все тот же Module_ID
var $url = 'http://memori.ru/'; // URL сервиса. Настоятельно рекомендую указывать правильный :-)
var $icon = 'memori.png'; // Имя иконки. Должна лежать в одной папке с файлом модуля.
var $notice = ''; // Дополнительная информация, которую мы желаем сообщить пользователям
var $email_confirm = true; // Требуется ли подтверждение по email
var $core_version = 3; // Версия ядра, на которую рассчитан модуль, необходимо для проверки совместимости. Нынешняя, как не трудно догадаться - 3
var $encoding = 'cp1251'; // Кодировка сайта.
/**
* Функция, ответственная за загрузку капчи.
* Если ввод капчи не требуется, возвращает null. В противном случае возвращает массив данных,
* которые понадобятся при отправке формы, в т. ч. имя временного файла с кукисами, различные токены и т. п.
* Обязательно в массиве должен быть элемент image_file, чтобы RegSubmitter мог отобразить капчу пользователю для ввода.
*/
function GetForm()
{
// Запрашиваем страницу регистрации.
$page = $this->Get('http://memori.ru/registration/');
// Извлекаем токен
preg_match('##is', $page, $matches);
// Ноу-хау: допываем картинку из рекапчи
$js = $this->Get('http://api.recaptcha.net/challenge?k=6LfV6wgAAAAAAF327mU7j7lzG6s-p1SolxPA2Wx2');
preg_match("#challenge : '([^']+)'#", $js, $key);
// Собственно скачиваем картинку
$image = $this->Get("http://api.recaptcha.net/image?c=$key[1]");
// Генерируем имена временных файлов для хранения кукисов и картинки. Параметр - префикс имени файла.
$cookies_file = $this->TempFile('cookie');
$image_file = $this->TempFile('image');
$this->SaveCookies($cookies_file); // Сохраняем кукисы
file_put_contents($image_file, $image); // Сохраняем картинку
// Возвращаем данные, которые нам понадобятся потом.
return array(
'image_file' => $image_file, // Картинку капчи. ОБЯЗАТЕЛЬНО!
'cookies_file' => $cookies_file, // Файл с кукисами
'key' => $matches[1], // Токен формы, добытый в начале
'recaptcha' => $key[1], // Токен рекапчи
);
}
/**
* Эта функция занимается собственно произведением регистрации.
* Параметры:
* - $account массив из элеменитов login, password, email. Назначение, думаю, очевидно :)
* - $data тот самый массив, который мы вернули из функции GetForm()
* - $captcha текст, написанный по мнению пользователя на капче.
* Функция возвращает null в случае успеха или массив сообщений об ошибках, если они возникли.
*/
function SubmitReg($account, $data, $captcha)
{
// Собираем запрос
$post_data = array(
'login' => $account['login'], // Логин
'email' => $account['email'], // Емейл
'password' => $account['password'], // Пароль
'password_confirm' => $account['password'], // И снова пароль
'recaptcha_response_field' => $captcha, // Текст с картинки
'recaptcha_challenge_field' => $data['recaptcha'], // Токен рекапчи
'token' => $data['key'], // Токен формы
'inviter' => '', // Просто пустое поле. Видимо, для какой-то реферральной системы
);
// Загружаем сохраненные кукисы с прошлого раза
$this->LoadCookies($data['cookies_file']);
// Отправляем запрос. Первый параметр - куда, второй - параметры запроса,
// третий (опциональный, по умолчанию - false) - надо ли преобразовывать страницу из кодировки сайта в родную для RS (utf-8)
// Очевидно, что для веб-страниц его надо ставить true, а дял картинок - false. Аналогичный параметр есть и у метода $this->Get()
$html_data = $this->Post("http://memori.ru/register/", $post_data, true);
// Проверяем успешность регистрации
if(strstr($html_data, 'На указанный Вами электронный адрес'))
{
return null;
}
else
{
// Если не прошло, добываем ошибки.
preg_match_all('#([^#mu'
, $html_data, $matches);
return $matches[1];
}
}
/**
* Функция подтверждения по почте.
* Параметр $account содержит те же данные о логине, пароле и email-e, что и в случае с методом SubmitReg.
* возвращает null в случае успеха или массив с сообщениями об ошибках в ином случае.
*/
function Confirm($account)
{
// Ищем письмо по отправителю. Кроме того, в теле письма должен упоминаться домен из поля $this->url
$mail = $this->findMail('[email protected]');
// Загружаем тело сообщения
$text = $this->getMailBody($mail['msg_id']);
// Извлекаем ссылку подгверждения
preg_match('#http://memori.ru/regconfirm/[0-9]+/[0-9a-z]+/#i', $text, $url);
// Отправляем запрос
$html_data = $this->Get($url[0], true);
// Проверяем успешность
if(strstr($html_data, '/logout/'))
{
return null;
}
else
{
// Если что не так - ругаемся.
return array('Произошла ошибка активации или аккаунт уже был ранее подтвержден');
}
}
}
// Alles :-)
?>
Напоследок замечу, что в RS имеется очень удобная для отладки функция dbg($var). Будучи вызванная в любом месте кода она выведет дамп переменной var внизу страницы. Она особенно хороша тем, что работает в том числе и тогда, когда скрипт вызывается через AJAX.
Во вложении к посту полный архив модуля Memori. И, кстати, на этот раз в нем нет никаких ошибок ;)
| Прикрепленный файл | Размер |
|---|---|
| Memori.zip | 3.34 кб |
"preg_match_all('#
([^
#mu', $html_data, $matches);"
напомнило http://b23.ru/s67l :)
А вообще, почему PHP? Python же.
PHP потому как массовый потребитель с ним лучше знаком и ему проще объяснить, как это установить и заставить работать (Денвер тот же). Но в то же время это кроссплатформенно и вдобавок можно запускать на большинстве хостингов, что есть плюс.
Так что вот :)
Очнь подробно расписал. Спасибо.
Рекапча форева.
Пожалуйста :)
Может выложишь в свободный доступ функцию dbg($var), был бы оч. признателен =) Не пойму как она при AJAX запросе может что то выводить, разве только в лог файл.
Там все основано на фишке Котеровской AJAX библиотеки, которую я использую, перехватывающей весь вывод скрипта и передающей его в качестве отдельной переменной.
А код функции такой:
function dbg($var, $title = '') { if(!defined('DEBUG') || DEBUG == false) return; global $template; if(empty($template)) { echo "$title
"; } else { static $n = 1; $template->set_row_vars('debug', array( 'NUMBER' => $n, 'TITLE' => $title, 'CODE' => htmlspecialchars(var_export($var, true)), )); $n++; } }Ясно теперь. Я уж подумал про универсальное решение =) Но видимо такого нет. Здесь AJAX получает ответ и как он вычисляет, где нужные ему данные а где дебаг?
Не буду вдаваться в детали, дабы ненароком не исказить впечатление - лучше всего об этом прочитать на сайте автора :)
Отправить комментарий