Краткий мануал о том, как сочленить эти два инструмента. На самом деле, все не так сложно.
Суть проблемы
phpBB имеет свой API, вопрос как его использовать. Вообще, достаточно подключить файл common.php, однако просто так это не работает. Во-первых пересекаются классы сессий. А во-вторых и в-третьих — читайте внутри.
Начнем
Пусть index.php Коханы лежит в /web, а форум в /web/forum
Инклуды
Чтобы не портить код форума, для начала нужно создать копии необходимых файлов. Можно конечно править на живую, но тогда форум перестанет работать (спасибо кэп), но зато будет работать интеграция. Поэтому не долго думая, переименуем:
forum/common.php => forum/common_kohana.php
forum/includes/session.php => forum/includes/session_kohana.php
Подключать впоследствии будем именно forum/common_kohana.php
Правим session_kohana.php
В это файле находятся два класса: Session и User. Session конфликтует с Кохановским, User я тоже переименовал. Итак, правим:
class session // => class session_kohana
class user extends session // => class user_kohana extends session_kohana
Правим common_kohana.php
Вот тут у нас идет подключения файла сессий:
require($phpbb_root_path . 'includes/session.' . $phpEx);
Это надо срочно исправлять:
require($phpbb_root_path . 'includes/session_kohana.' . $phpEx);
Опускаемся немного ниже. Здесь идет установка обработчика ошибок:
// Set PHP error handler to ours
set_error_handler(defined('PHPBB_MSG_HANDLER') ? PHPBB_MSG_HANDLER : 'msg_handler');
У коханы уже есть свой обработчик, весьма не плохой (с блэкджеком и трейсами), так что убиваем:
// Set PHP error handler to ours
//set_error_handler(defined('PHPBB_MSG_HANDLER') ? PHPBB_MSG_HANDLER : 'msg_handler');
Ниже создаются основные объекты, в том чилсе инстанс класса User:
// Instantiate some basic classes
$user = new user();
Этот класс хранится в файле forum/includes/session_kohana.php. Я переименовал и его тоже.
$user = new user_kohana();
Теперь вот какое дело. Кохана по умолчанию убивает нафиг глобальные переменные, поэтому их нужно вручную туда добавить. Добавляем буквально ниже:
$GLOBALS['user'] = $user;
$GLOBALS['auth'] = $auth;
$GLOBALS['template'] = $template;
$GLOBALS['cache'] = $cache;
$GLOBALS['db'] = $db;
Дальше подключается конфиг:
// Grab global variables, re-cache if necessary
$config = $cache->obtain_config();
Инстанс тоже заглобалим. Добавляем ниже
$GLOBALS['config'] = $config;
В самом конце файла идет установка хуков. Я до конца не разобрался насколько это влияет на работу, но вроде как они не нужны, т.к. не имеют смысла вне контекста работы юзера с форумом. Я закомментировал следующий код:
// Add own hook handler
require($phpbb_root_path . 'includes/hooks/index.' . $phpEx);
$phpbb_hook = new phpbb_hook(array('exit_handler', 'phpbb_user_session_handler', 'append_sid', array('template', 'display')));
foreach ($cache->obtain_hooks() as $hook)
{
@include($phpbb_root_path . 'includes/hooks/' . $hook . '.' . $phpEx);
}
С common.php разобрались
Подключение
Я написал маленький класс, который и не класс-то в полном понимании, а набор статических фукций. Эта функция подключает либы форума, собственно код взят из файла index.php:
public static function include_libs()
{
// Два раза повторять не надо
if (self::$libs_included)
return TRUE;
define('IN_PHPBB', true);
define('PHPBB_DB_NEW_LINK', 1);
$phpbb_root_path = (defined('PHPBB_ROOT_PATH')) ? PHPBB_ROOT_PATH : './forum/';
$phpEx = substr(strrchr(__FILE__, '.'), 1);
$GLOBALS['phpbb_root_path'] = $phpbb_root_path;
$GLOBALS['phpEx'] = $phpEx;
require_once($phpbb_root_path . 'common_kohana.' . $phpEx);
require_once($phpbb_root_path . 'includes/functions_user.' . $phpEx);
require_once($phpbb_root_path . 'includes/acp/auth.' . $phpEx);
// Start session management
$user->session_begin();
self::$libs_included = TRUE;
return TRUE;
}
Затык был в этом:
define('PHPBB_DB_NEW_LINK', 1);
Когда phpbb подключается к базе, он делает так:
$db->sql_connect($dbhost, $dbuser, $dbpasswd, $dbname, $dbport, false, defined('PHPBB_DB_NEW_LINK') ? PHPBB_DB_NEW_LINK : false);
Если не определить константу PHPBB_DB_NEW_LINK в 1, то произойдет следующее. Ваш сайт очевидно, использует базу данных. Так вот.
Если ваш форум висит на другой базе и ваши базы все находятся на одном сервере, одном юзере (и одном и том же пароле надо полагать), то они, а точнее указатели на них (переменные типа Resource) не будут работать как положено. Функция mysql_connect, которая вызывается из phpBB, будет пытаться использовать ваш текущий кохановский mysql_connection_id, если параметры ее вызозва совпадают с теми, при которых он и был создан в Кохане. Кохановские способы котроля уникальности базы через хеши тут не работают так как лежат на уровень выше, абстрагируясь классами.
Вот что сказано по этому поводу в документации:
Если второй вызов функции произошёл с теми же аргументами mysql_connect(), новое соединение не будет установлено. Вместо этого функция вернёт ссылку на уже установленное соединение. Параметр new_link может заставить функцию mysql_connect() открыть ещё одно соединение, даже если соединение с аналогичными параметрами уже открыто.
Ну вот. Теперь можно работать. Регистрация юзера происходит так:
public static function register_user($username, $password, $email)
{
global $user;
self::include_libs();
$user_row = array(
'username' => $username,
'user_password' => phpbb_hash($password),
'user_email' => $email,
'group_id' => 2,
'user_timezone' => 10.00,
'user_dst' => 1,
'user_lang' => 'ru',
'user_type' => 0,
'user_actkey' => '',
'user_ip' => Request::$client_ip,
'user_regdate' => time(),
'user_inactive_reason' => 0,
'user_inactive_time' => 0,
);
try
{
$user_id = user_add($user_row, FALSE);
return $user_id ? $user_id : FALSE;
}
catch (Exception $e)
{
return FALSE;
}
}
А так можно залогинить и разлогинить заданного юзера:
public static function login($user_id, $persist_login = FALSE)
{
global $user;
self::include_libs();
$user->session_create($user_id, false, $persist_login, true);
}
public static function logout()
{
global $user;
self::include_libs();
$user->session_kill(FALSE);
return TRUE;
}
Узнать залогинен ли юзер:
public function logged_in()
{
global $user;
$this->include_libs();
return ($user->data['user_id'] == ANONYMOUS)
? FALSE
: TRUE;
}
В общем можно использовать многие функции API форума, например назначать роли. Вот так происходит открытие юзеру доступа к закрытому форуму:
function forum_open($forum_id, $user_id)
{
$this->include_libs();
$role_id = 14; // Full access
$auth_admin = new auth_admin();
$q = DB::query(Database::SELECT,
"SELECT o.auth_option, r.auth_setting
FROM `bazar_acl_roles_data` AS r, `bazar_acl_options` AS o
WHERE o.auth_option_id = r.auth_option_id AND r.role_id = :role_id")
->param(':role_id', $role_id)
->as_object()
->execute('forum');
$auth_settings = array();
foreach ($q as $row)
{
if ($row->auth_option != 'f_')
{
$auth_settings[$row->auth_option] = $row->auth_setting;
}
}
$auth_admin->acl_set('user', $forum_id, $user_id, $auth_settings, $role_id, true);
}
А напоследок я скажу
Это плохой подход, т.к. нарушает элментарные принципы ООП. Глобальные переменные, глобальные функции, константы… В общем, так делать не надо. А как надо, хотелось бы услышать в комментариях. Когда уже появится нормальный механизм интеграции. Спасибо.
Сервис работает здесь. Если зарегиться на сайте и форуме, то при логине на сайт будет идти и логин на форум.
Оригинал статьи