Добрый день.

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




Размеры


Размер основной части кода XenForo 3,24 МБ (3 405 312 байт). Еще 8,31 МБ (8 724 429 байт) занимает Zend Framework, который поставляется вместе с кодом XenForo.

MVC


XenForo полностью основан на MVC архитектуре. Есть отдельные классы для моделей, видов и контроллеров. Корневой index.php имеет размер в 467 байт и заканчивается, как и положено в таких случаях
$fc = new XenForo_FrontController(new XenForo_Dependencies_Public());
$fc->run();

Разумеется, в ходу автозагрузчик классов. Система именования классов как в ZF.

Сама MVC пока внутри использует классы Zend, но то, что XenForo_Controller не унаследован от соответствующих классов ZF, говорит о том, что эта часть будет переписана.

Все контроллеры унаследованы примерно одинаково: class XenForo_ControllerPublic_Forum extends XenForo_ControllerPublic_Abstract. После Symfony с ее actions такой подход несколько непривычен, но к нему быстро привыкаешь.

Система роутинга спроектирована расширяемой. Расширение происходит через привязку префиксов URL к классам-обработчикам. SEO из коробки, разумеется. Никаких плагинов не требуется.

Модель контента


Архитектура XenForo закладывалась надолго :) Во всяком случае, в первый раз в форумном движке я встретил более-менее внятную архитектуру контента, слегка напоминающую Drupal («Everything is a node», помните?). Пока в XenForo типов контента четыре: категория, раздел форума, страница и ссылка (обычный редирект). Каждый тоже называется Node. Причем, каждый экземпляр узла может быть предком/потомком экземпляра другого типа. Пока не знаю, как эта гремучая смесь, например, из страницы и дочернего ей раздела форума, будет отображаться, не пробовал :). Причем, некоторые типы контента можно слегка расширять прямо из коробки. Так, например, для страницы помимо, собственно, ее HTML содержимого можно указать PHP обработчик, который получит из БД дополнительные данные, изменит д��зайн страницы, вставит в нее информацию или вообще плюнет в пользователя ошибкой. PHP обработчики везде указываются не кодом на PHP, как этого можно было ожидать, а именами класса и метода. При активации хука класс будет загружен автолоадером (соответственно, должен быть правильно расположен в файловой системе).

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

Использование сторонних библиотек


XenForo использует Sabre и Zend Framework. Причем, наличие в коде мест, подобных

............
		/*require_once('Zend/Loader/Autoloader.php');

		$autoloader = Zend_Loader_Autoloader::getInstance();
		$autoloader->pushAutoloader(array($this, 'autoload'));*/
		spl_autoload_register(array($this, 'autoload'));

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

Из ZF используется совсем немного классов. В их числе Zend_Registry (в качестве реестра для синглтон-подобных объектов вроде экземпляра XenForo_Db), Zend_Config, Zend_Cache (много чего кеширует, в частности используется в базовом классе моделей XenForo_Model), Zend_Service_ReCaptcha, Zend_Http_Client, Zend_Mail, Zend_Validate и некоторые другие.

Для работы с базой данных используется часть бандла Zend_DB (Zend_Db_Adapater_Abstract и Ко), завернутая в классе XenForo_DB, что опять говорит о том, что и эту часть в последствии перепишут.

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

Архитектура аддонов


Для расширения форума предназначено несколько событий, на которые можно повесить обработчики. Обработчики указываются, как уже было сказано, через имя класса и метода, что позволяет использовать акселераторы вроде APC для кеширования аддонов. Список обработчиков вместе с базовой информацией о плагине помещается в *.xml файл. Писать его руками не обязательно. Достаточно активировать отладочный режим на форуме и в админке появятся дополнительные возможности. PHP файлы аддона, разумеется, надо будет сначала распаковать в соответствующую папку. Классы XenForo лежат в /library/XenForo, стало быть ваши будут лежать в /library/VasyaCorp. Меня это порадовало, поскольку довольно часто я занимался поиском остатков плагина в файловой системе при его удалении из VBulletin.

Поскольку практически каждый аддон должен иметь настройки, их можно создавать прямо в админке и экспортировать в *.xml плагина вместе с информацией о хуках. Располагаются они там же, где и настройки самого XenForo. Встраивать их можно в произвольные места раздела общих настроек XenForo. Устроено все примерно также, как это было в VBulletin. Очень удобно и практически нет необходимости писать код для их поддержки.

Меня немного удивило количество хуков. Я полагаю, все помнят оглушительное количество хуков в VBulletin, которое даже в голове нельзя было удержать? В XenForo хуков всего… 17! Такое количество объясняется их назначением. Вот их имена:

  • container_admin_params
  • container_public_params
  • controller_pre_dispatch
  • front_controller_post_view
  • front_controller_pre_dispatch
  • front_controller_pre_route
  • front_controller_pre_view
  • init_dependencies
  • load_class_bb_code
  • load_class_controller
  • load_class_datawriter
  • load_class_model
  • load_class_route_prefix
  • load_class_search_data
  • load_class_view
  • navigation_tabs
  • visitor_setup


Небольшая инспекция кода показала, что семейство хуков load_class_* предназначено для динамического расширения системы классов XenForo. Вот ключевой кусочек кода:


	public static function resolveDynamicClass($class, $type, $fakeBase = false)
	{
		if (!XenForo_Application::autoload($class))
		{
			if ($fakeBase)
			{
				$fakeNeeded = true;
			}
			else
			{
				return false;
			}
		}
		else
		{
			$fakeNeeded = false;
		}

		if (!empty(self::$_classCache[$class]))
		{
			return self::$_classCache[$class];
		}

		$createClass = $class;

		$extend = array();
		XenForo_CodeEvent::fire('load_class_' . $type, array($class, &$extend));

		if ($fakeNeeded)
		{
			if (!$extend)
			{
				return false;
			}

			eval('class ' . $class . ' extends ' . $fakeBase . ' {}');
		}

		if ($extend)
		{
			try
			{
				foreach ($extend AS $dynamicClass)
				{
					// XenForo Class Proxy, in case you're wondering
					$proxyClass = 'XFCP_' . $dynamicClass;
					eval('class ' . $proxyClass . ' extends ' . $createClass . ' {}');
					XenForo_Application::autoload($dynamicClass);
					$createClass = $dynamicClass;
				}
			}
			catch (Exception $e)
			{
				self::$_classCache[$class] = $class;
				throw $e;
			}
		}

		self::$_classCache[$class] = $createClass;
		return $createClass;
	}


А несколько неожиданное для меня использование хука visitor_setup для изменения всего чего угодно на странице показано тут. Практически через любой хук вы можете залезть в сердце системы и наделать делов.

В общем, малое число хуков, похоже, вполне себе компенсируется их мощью.

Система шаблонов


Как и во всех современных форумах в XenForo есть КУЧА шаблонов. Синтаксис довольно мощный (см. отрывки внизу), впрочем, этим уже никого не удивишь. Спецтеги пишутся как в PHPTAL («неймспейсированный» XML/HTML). Один шаблон может содержать несколько файлов. Например, шаблон forum_list содержит forum_list, node_list (он вставляется в forum_list), node_list.css, sidebar.css, sidebar_online_users. CSS в шаблоне подключается через xen:require, что позволяет кешировать все CSS файлы как угодно.

<xen:require css="node_list.css" />

<xen:if hascontent="true">
<fieldset>
	<ol class="nodeList sectionMain" id="forums">
	<xen:contentcheck>
		<xen:foreach loop="$renderedNodes" value="$node">{xen:raw $node}</xen:foreach>
	</xen:contentcheck>
	</ol>
</fieldset>
</xen:if>
-------------------------------------
<xen:edithint template="node_link.css" />
-------------------------------------
<xen:contentcheck>
					<xen:foreach loop="$onlineUsers.records" value="$user">
						<xen:if is="{$user.is_moderator} OR {$user.is_admin}">
							<li>
								<xen:avatar user="$user" size="s" img="true" />
								<a href="{xen:link members, $user}" class="username">{xen:helper richUserName, $user}</a>
								<div class="muted">{xen:helper userTitle, $user}</div>
							</li>
						</xen:if>
					</xen:foreach>
				</xen:contentcheck>


Обратите внимание на использование хелперов в шаблонах. Хелпер, как и в Symfony, это просто метод класса. Например, вот код хелпера helperUserLink:

public static function helperUserLink(array $user)
	{
		return '<a href="'
			. XenForo_Link::buildPublicLink('members', $user)
			. '" class="username">'
			. htmlspecialchars($user['username'])
			. '</a>';
	}


Поисковая машина


Поисковая машина в XenForo реализована в виде отдельного класса (в отличие от ненавидимой мною реализации в VB, в которой, по-моему, сами разработчики давно уже запутались, просто не сознаются в этом), унаследованного от XenForo_Search_SourceHandler_Abstract. В настоящий момент реализация только одна — MySqlFt.php (class XenForo_Search_SourceHandler_MySqlFt extends XenForo_Search_SourceHandler_Abstract), но в ближайшее время должен появится Sphinx. Мода такая нынче :) Форумы уже не те, что раньше. Растут, как на дрожжах…

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

Система локализации


Система локализации практически полностью повторяет свой аналог из VBulletin с тем исключением, что фразовые группы отсутствуют вообще. Вот начало файла фраз английского языка:
<?xml version="1.0" encoding="utf-8"?>
<phrases>
  <phrase title="1_more_message" global_cache="0" version_id="1000017" version_string="1.0.0 Alpha 7"><![CDATA[1 more message]]></phrase>
  <phrase title="about" global_cache="0" version_id="1000015" version_string="1.0.0 Alpha 5"><![CDATA[About]]></phrase>


Каждый аддон может иметь собственные фразы. Языки можно экспортировать. Переводить аддоны можно прямо в админке.

Кстати, русская локализация XenForo уже почти готова.

Система стилей


Я не дизайнер и вообще у меня плохой вкус. Все, что касается дизайна и его разработки меня пугает. Но в XenForo мы видим древовидную систему стилей, аналогичную VB, в которой мы можем взять какой-то стиль за основу и изменить часть его настроек или шаблонов. Только Кир с Майком реализовали самый крутой редактор CSS, который мне приходилось вообще доводилось в жизни видеть. Dreamweaver отдыхает :). В XenForo настраивается все и настраивается вполне себе визуально. Забудьте о редактировании CSS напрямую, о листе с двумя тысячами переменных, который какой-то мудак нехороший человек реализовал в VB. Здесь все очень грамотно сгруппировано так, что 99% секций помещаются на одном экране. Если вы не такой лох в плане дизайна, как я и имеете художественный вкус, наверное, вы можете уволить своего дизайнера, поскольку стиль вы сможете сделать и сами (через пару месяцев, как наиграетесь с редактором).

Система аутентификации


У вас есть база данных, скажем, от WordPress? Импортируйте пользователей из WP в базу XenForo с сохранением их паролей! Для того, чтобы потом все заработало, вам придется всего лишь расширить класс XenForo_Authentication_Abstract чтобы подсказать XenForo каким образом проверять пароль. Данные пользователей с вашими системами аутентификации связывает таблица xf_user_authenticate.

Службы связи


Уже достаточно давно на форумах позволяется указывать свои контакты в различных системах коммуникации типа ICQ. В XenForo существует легко расширяемая система контактных служб. Службы можно добавлять в панели управления. Класс поддержки службы выглядит примерно так:

<?php
class XenForo_Model_IdentityService_Icq extends XenForo_Model_IdentityService_Abstract
{
	protected function _getIdentityServiceId()
	{
		return 'icq';
	}

	static public function verifyAccountName(&$accountName, &$error)
	{
		if (!preg_match('/^\d+$/', $accountName))
		{
			$error = new XenForo_Phrase('please_enter_valid_icq_uin_using_numeric_characters_only');
			return false;
		}

		return true;
	}
}


Теперь уже никто не сможет ввести в поле ICQ своего профиля строчку «Продам носки шерстяные» :)

Система прав и привилегий


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

Типов разрешений четыре. Inherit, Allow, Revoke, Deny. Пока мне не удалось понять, чем Revoke отличается от Deny. Причем, Revoke отсутствует в правах группы по умолчанию, что наталкивает на мысль, что этот тип связан с наследованием прав.

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

$users[$userId]['canCleanSpam'] = (XenForo_Permission::hasPermission($visitor['permissions'], 'general', 'cleanSpam') && $this->getModelFromCache('XenForo_Model_User')->couldBeSpammer($users[$userId]));


Общие впечатления


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

Кстати, я очень ждал выхода PHPBB3, хотел им заняться серьезно. Но его архитектура для меня выглядит гораздо менее четкой и логичной, чем архитектура XenForo. Честно признаться, я так и не въехал в PHPBB3. Будем ждать четверку на Symfony2…

Вот, пожалуй, и все. Вопросы по плагинам для XenForo можно задавать тут. А на официальном форуме уже есть несколько (18 на текущий момент), можно скачать и посмотреть. Сам я пока лицензию на движок не покупал, не было повода. Поэтому за предоставленный для изучения код благодарю моих друзей, уже купивших официальную версию.

Прошу прощения, если кому-то данная статья покажется неполной или некорректно построенной. Это мой первый обзор архитектуры чего-либо, поэтому я с радостью приму любую критику в комментариях. Часть критики, надо полагать, я сразу увижу на своей карме и рейтинге :) Если вам еще что-то интересно узнать, сообщите об этом в комментах, если сумею разобраться быстро — дополню статью.