Когда-нибудь у Вас возникнет желание сделать на сайте форум, т.к. трудно себе представить хорошо посещаемый сайт без оного. Для этого форум можно написать с нуля, а можно взять бесплатное готовое решение, например phpbb, и объединить его с сайтом. Вот об это я и хотел рассказать.
Процесс интеграции разделим на 2 части: «форумную» и «сайтовую». Под «форумной» будем понимать модификацию файлов форума, а «сайтовой» — сайта. Для объединения форума и сайта необходимо определиться с местом хранения пользователей: либо это будет таблица пользователей сайта, либо форума. Лучше всего сделать это все в одной таблице – форумной (по умолчанию phpbb_users), добавив в нее необходимые вам поля. По большому счету, необходимо привести механизм сессий на вашем сайте в соответствии с «форумным», т.е. завязать его на базу данных, но мы это делать не будем, т.к. это выходит за рамки данной статьи. Итак, начнем.
1. Открываем файл, где у нас хранятся функции (у меня function.php) и добавляем в него следующие функции из phpbb (forum/includes/function.php): phpbb_hash, phpbb_check_hash, _hash_gensalt_private, _hash_encode64, _hash_crypt_private, unique_id. Эти функции Portable PHP password hashing фрэймворка (за исключением unique_id) и их нам придется использовать на нашем сайте. Функция unique_id генерирует уникальный идентификатор, для совместимости мне пришлось в нее запихнуть заглушку:
Также, для совместимости в свой файл function.php я добавил функцию set_cookie из файла includes/session.php. Убрав из нее лишнее, она стала выглядеть так:
2. Далее открываем файл, где у нас происходит авторизация пользователей и модифицируем его следующим образом:
3. Теперь нужно найти место, куда мы вставим код проверки времени последнего посещения пользователя и код обновления времени последнего посещения, вроде этого:
Тем самым мы проверяем время неактивности пользователя и если оно больше разрешенного, то «выкидываем» его.
4. Далее замыкаем регистрацию нового пользователя и выход пользователя с сайта на «сайтовую» часть. Для этого в файле форума ucp.php в секции case 'register' и 'logout' добавляем эти соотвествующие коды:
т.е. перенаправлен пользователя на наши скрипты.
5. После чего модифицируем скрипт регистрации, добавив в него эти строчки:
В скрипте (функции) выхода с сайта необходимо удалять все куки.
На этом модификацию «сайтовой» части заканчиваем и приступаем к форумной.
Здесь нам нужен всего лишь один файл – forum/includes/session.php, находим в нем функцию session_create, в ней ищем строчку if($bot), после кода установки форумных кук добавляем:
Потом, чуть выше по коду, перед строчкой $this->session_id = $this->data['session_id'] = md5(unique_id()); вставляем этот код удаления сессии:
Напомню, что я привожу пример, где авторизация на сайте сделана через сессии, поэтому этот код необходим для удаления данных сессии, т.к. в противном случае, если форум «выкинет» пользователя, то его сессионные данные сохранятся.
Затем, идем в функцию session_begin. В ней изменим условие проверки кук с || на && (в самом начале функции), т.е.:
тем самым заставим авторизироваться на форуме только при наличии сессионной куки и куки с идентификатором пользователя.
Напоследок, немного изменим функцию set_cookie, чтобы она могла ставить и наши куки:
Здесь кроме, переменной $phpbb я намерено изменил gmdate на date. Забегая вперед, хочу отметить, что полная интеграция возможна лишь при правильном определении часового пояса пользователя и установки кук с учетом этого часового пояса. Все вышеприведенное работать будет, но возникнут небольшие неточности во времени работы сессии с автологином, т.е. при установки времени жизни куки автологина равной 1 дню, мы можем получить, что кука будет жить немного больше(или меньше) 1 дня. Это обусловлено тем, что сервер посылает заголовок Data со времени равным UTC-0, а expires куки ставится в местном времени. Но об этом речь в следующей статье :).
После того как мы модифицировали «форумную» и «серверные» части, необходимо подкорректировать некоторые настройки сайта и форума.
Во-первых, выше упоминалась константа COOKIE_PREFIX, как уже видно из названия это префикс кук и он должен быть равен префиксу кук форума.
Во-вторых, время жизни сессии и время действия куки автологина на форуме должны полностью соответствовать время жизни сессии и время действия куки автологина на сайте (что необходимо выставить в настройках форума и сайта). Внимание! Время жизни куки автологина на форуме выставляется в днях, а не в секундах!
В-третьих, если вы не используете автологин на форуме, то на сайте его также не нужно использовать и наоборот :).
В-четвертых, я намеренно (для упрощения) не использовал постоянный ключ сессии при автологине, который используется для повышения безопасности при авторизации на форуме (кука с названием COOKIE_PREFIX.'_k') и просто закомментировал у себя в файле /forum/includes/session.php строчку $this->set_login_key();. Если вы надумайте использовать его, то необходимо будет еще при авторизации пользователя устанавливать (обновлять) этот ключ.
Процесс интеграции разделим на 2 части: «форумную» и «сайтовую». Под «форумной» будем понимать модификацию файлов форума, а «сайтовой» — сайта. Для объединения форума и сайта необходимо определиться с местом хранения пользователей: либо это будет таблица пользователей сайта, либо форума. Лучше всего сделать это все в одной таблице – форумной (по умолчанию phpbb_users), добавив в нее необходимые вам поля. По большому счету, необходимо привести механизм сессий на вашем сайте в соответствии с «форумным», т.е. завязать его на базу данных, но мы это делать не будем, т.к. это выходит за рамки данной статьи. Итак, начнем.
Модификация «сайтовой» части
1. Открываем файл, где у нас хранятся функции (у меня function.php) и добавляем в него следующие функции из phpbb (forum/includes/function.php): phpbb_hash, phpbb_check_hash, _hash_gensalt_private, _hash_encode64, _hash_crypt_private, unique_id. Эти функции Portable PHP password hashing фрэймворка (за исключением unique_id) и их нам придется использовать на нашем сайте. Функция unique_id генерирует уникальный идентификатор, для совместимости мне пришлось в нее запихнуть заглушку:
function unique_id($extra = 'c'){
$rand_seed = '8a414598ba18a512b8fe97f1497fa22b';
$val = $rand_seed . microtime();
$val = md5($val);
return substr($val, 4, 16);
}
Также, для совместимости в свой файл function.php я добавил функцию set_cookie из файла includes/session.php. Убрав из нее лишнее, она стала выглядеть так:
function set_cookie($name, $cookiedata, $cookietime, $path='/', $domain = false){
$name_data = rawurlencode($name) . '=' . rawurlencode($cookiedata);
$expire = date('D, d-M-Y H:i:s \\G\\M\\T', $cookietime);
$domain = !$domain ? '' : '; domain=' . $domain;
header('Set-Cookie: ' . $name_data . (($cookietime) ? '; expires=' . $expire : '') . '; path=' . $path . $domain . '; HttpOnly', false);
}
2. Далее открываем файл, где у нас происходит авторизация пользователей и модифицируем его следующим образом:
// это добавляем в место проверки пароля
// где $pass пароль из массива $_POST, а
// $user_password пароль из базы данных
if(phpbb_check_hash($pass, $user_password)){
// если авторизация прошла успешно, то здесь ваш код
// т.к. у меня авторизация на сессиях, то я пишу свой, например такой:
$_SESSION['logged'] = true;
// обновляем время последнего посещения юзера
// здесь и ниже $db – объект для работы с базой данных,
// методы, которого говорят сами за себя (я думаю что-то
// подобное есть у каждого)
$db->query("update `phpbb_users` set `user_lastvisit` = time() where `user_id` = $user_id";
// определяем время жизни кук
$cookie_expires = time() + ($autologin ? 86400*$config['session_autologin_life'] : 31536000);
// далее ставим соответствующие куки (процесс обязательный)
// здесь и ниже COOKIE_PREFIX – префикс кук,
// $autologin – переменная-признак автологина,
// $config – массив с вашими настройками сайта (или что то его заменяющее),
// в которых необходимо обязательно предусмотреть
// время жизни сессии и время жизни куки автологина
// $config['session_life'] и $config['session_autologin_life'] соответственно
set_cookie(COOKIE_PREFIX.'_sid' $sessname, session_id(),$cookie_expires);
// пишем ид юзера в куку
// $user_id – ид юзера
set_cookie(COOKIE_PREFIX.'_u', $user_id, $cookie_expires);
set_cookie(COOKIE_PREFIX.'_k', '', $cookie_expires);
// кука признака автологина вашего сайта
set_cookie(COOKIE_PREFIX.'_a', 1, ($autologin ? 86400*$config['session_autologin_life'] : (-1)*31536000));
// затем «входим» на форум, где
// $browser – идентификация браузера пользователя:
// $browser = (!empty($_SERVER['HTTP_USER_AGENT'])) ? htmlspecialchars((string) $_SERVER['HTTP_USER_AGENT']) : '';
$db->query_found_rows("select SQL_CALC_FOUND_ROWS `session_user_id` from `phpbb_sessions` where `session_id`='".$db->safesql(session_id())."'");
if($db->found_rows==0)
$db->query('insert into `phpbb_sessions`
(`session_id`, `session_user_id`, `session_last_visit`, `session_start`, `session_time`,
`session_viewonline`, `session_browser`, `session_ip`, `session_autologin`)
values('".session_id() ."', $user_id, time(), time(), time(), 1, $browser, '".$_SERVER['REMOTE_ADDR']."', $autologin));
else
$db->query("update `phpbb_sessions`
set `session_user_id`=$user_id, `session_last_visit`=".time().", `session_start`=".time().",
`session_time`=".time().", `session_viewonline`=1, `session_browser`='".$db->safesql($browser)."',
`session_ip`='".$_SERVER['REMOTE_ADDR']."'
where `session_id`='".$db->safesql(session_id())."'");
3. Теперь нужно найти место, куда мы вставим код проверки времени последнего посещения пользователя и код обновления времени последнего посещения, вроде этого:
// время жизни сессии
$config['session_life'] = ($autologin ? 86400 * $config['session_autologin_life']: $config['session_life']);
if (time()-($user['user_lastvisit']+60)) > $config['cookie_life'])
// функция выхода из сайта
logout();
if (time() - $user['user_lastvisit']>60)
// функция обновления времени последнего посещения пользователя
last_time_update($user['user_id']);
Тем самым мы проверяем время неактивности пользователя и если оно больше разрешенного, то «выкидываем» его.
4. Далее замыкаем регистрацию нового пользователя и выход пользователя с сайта на «сайтовую» часть. Для этого в файле форума ucp.php в секции case 'register' и 'logout' добавляем эти соотвествующие коды:
case 'register':
// здесь ваша ссылка на скрипт регистрации
header('location: http://'.$_SERVER['HTTP_HOST'].'/register/');
exit();
case 'logout':
// здесь ваша ссылка на скрипт выхода с сайта
header('location: http://'.$_SERVER['HTTP_HOST'].'/logout/');
exit();
т.е. перенаправлен пользователя на наши скрипты.
5. После чего модифицируем скрипт регистрации, добавив в него эти строчки:
// делаем хэш, полученного пароля ($pass - пароль полученный при регистрации)
$hpass = phpbb_hash($pass);
// хэш почтового ящика ($email - адрес электронной почты полученный при регистрации)
$hemail = crc32(strtolower($email) . strlen($email));
...
// после всех ваших проверок (если таковые имеются) добавляем юзера
$db->query("insert into `phpbb_users`
(`username`, `username_clean`, `user_email`, `user_email_hash`, `user_password`, `user_regdate`, `user_form_salt`, group_id`, `user_permissions`, `user_ip`) values ('".$username."', '".strtolower($username) ."', '". $email ."', '".$hemail."', '".$hpass."', time(), '".unique_id()."', 2, '', '".$_SERVER['REMOTE_ADDR']. "'");
$db->query("insert into`phpbb_user_group`
(`user_id`, `user_pending`, `group_id`)
values (".$db->insert_id().", 0, 2)");
// обновляем статистику форума
// где $user_id – идентификатор нового пользователя, а $username – его имя
// и увеличиваем счетчик юзеров в phpbb
$db->query("update `phpbb_config` set `config_value`=`config_value`+1 where `config_name`='num_users'");
// добавляем информацию о новом юзере
$db->query("update `phpbb_config` set `config_value`=$user_id where `config_name`='newest_user_id'");
$db->query("update `phpbb_config` set `config_value`='$username' where `config_name`='newest_username'");
В скрипте (функции) выхода с сайта необходимо удалять все куки.
На этом модификацию «сайтовой» части заканчиваем и приступаем к форумной.
Модификация «форумной» части
Здесь нам нужен всего лишь один файл – forum/includes/session.php, находим в нем функцию session_create, в ней ищем строчку if($bot), после кода установки форумных кук добавляем:
if($this->data['user_id']!=ANONYMOUS){
// этот код запускает сессию с именем сессии
// как на сайте и пишет в нее данные сессиии ($_SESSION['logged'])
session_name($config['cookie_name'].'_sid');
session_id($this->session_id);
session_start();
$_SESSION['logged'] = true;
// кука – признак автологина сайта
if($session_autologin) {
$this->set_cookie($config['cookie_name'].'_a', 1, $cookie_expire, false);
}
}
Потом, чуть выше по коду, перед строчкой $this->session_id = $this->data['session_id'] = md5(unique_id()); вставляем этот код удаления сессии:
if(!empty($this->session_id)) {
session_name($config['cookie_name'].'_sid');
session_id($this->session_id);
session_start();
$_SESSION=array();
session_destroy();
}
Напомню, что я привожу пример, где авторизация на сайте сделана через сессии, поэтому этот код необходим для удаления данных сессии, т.к. в противном случае, если форум «выкинет» пользователя, то его сессионные данные сохранятся.
Затем, идем в функцию session_begin. В ней изменим условие проверки кук с || на && (в самом начале функции), т.е.:
if (isset($_COOKIE[$config['cookie_name'] . '_sid']) && isset($_COOKIE[$config['cookie_name'] . '_u']))
тем самым заставим авторизироваться на форуме только при наличии сессионной куки и куки с идентификатором пользователя.
Напоследок, немного изменим функцию set_cookie, чтобы она могла ставить и наши куки:
function set_cookie($name, $cookiedata, $cookietime, $phpbb=true){
global $config;
if($phpbb)
$name_data = rawurlencode($config['cookie_name'] . '_' . $name) . '=' . rawurlencode($cookiedata);
else
$name_data = rawurlencode($name) . '=' . rawurlencode($cookiedata);
$expire = date('D, d-M-Y H:i:s \\G\\M\\T', $cookietime);
$domain = (!$config['cookie_domain'] || $config['cookie_domain'] == 'localhost' || $config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . $config['cookie_domain'];
header('Set-Cookie: ' . $name_data . (($cookietime) ? '; expires=' . $expire : '') . '; path=' . $config['cookie_path'] . $domain . ((!$config['cookie_secure']) ? '' : '; secure') . '; HttpOnly', false);
}
Здесь кроме, переменной $phpbb я намерено изменил gmdate на date. Забегая вперед, хочу отметить, что полная интеграция возможна лишь при правильном определении часового пояса пользователя и установки кук с учетом этого часового пояса. Все вышеприведенное работать будет, но возникнут небольшие неточности во времени работы сессии с автологином, т.е. при установки времени жизни куки автологина равной 1 дню, мы можем получить, что кука будет жить немного больше(или меньше) 1 дня. Это обусловлено тем, что сервер посылает заголовок Data со времени равным UTC-0, а expires куки ставится в местном времени. Но об этом речь в следующей статье :).
Последние штрихи
После того как мы модифицировали «форумную» и «серверные» части, необходимо подкорректировать некоторые настройки сайта и форума.
Во-первых, выше упоминалась константа COOKIE_PREFIX, как уже видно из названия это префикс кук и он должен быть равен префиксу кук форума.
Во-вторых, время жизни сессии и время действия куки автологина на форуме должны полностью соответствовать время жизни сессии и время действия куки автологина на сайте (что необходимо выставить в настройках форума и сайта). Внимание! Время жизни куки автологина на форуме выставляется в днях, а не в секундах!
В-третьих, если вы не используете автологин на форуме, то на сайте его также не нужно использовать и наоборот :).
В-четвертых, я намеренно (для упрощения) не использовал постоянный ключ сессии при автологине, который используется для повышения безопасности при авторизации на форуме (кука с названием COOKIE_PREFIX.'_k') и просто закомментировал у себя в файле /forum/includes/session.php строчку $this->set_login_key();. Если вы надумайте использовать его, то необходимо будет еще при авторизации пользователя устанавливать (обновлять) этот ключ.