Форум IP.Board

Обзор механизма пометки прочитанности тем

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

Краткая история

Механизм в первых версиях IP.Board был основан на cookies. Он работал должным образом, но не без проблем, потому что все, что связанно с работой cookies, всегда немного странно.

Ну а самый большой недуг данного способа то, что прочитанные темы привязывались к конкретному компьютеру, другими словами прочитав тему на одном компьютере, а затем зайдя в форум с другого, тема будет помечена как еще не прочитанная.

С приходом IP.Board 2 механизм отметки тем был переведен на хранение в таблицах базы данных самого форума. Это существенно увеличило стабильность системы и позволило избавиться от привязки к определенному компьютеру.

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

Извлекаем опыт

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

Немало важна и проблема производительности. Как мы можем увеличить производительность не потеряв функциональность? Самым простым решением было бы поместить прочитанные темы в пользовательскую сессию. Однако это хорошо в теории, а на практике имело ряд препятствий. Во-первых, управление сессиями в IP.Board 2 не централизовано. Да был класс, но управление сессиями происходило не всегда через объект этого класса. Так файлы register.php и login.php могли беспрепятственно вносить изменения в таблицу сессий, не предупреждая остальные компоненты системы об этом. Когда вы пытаетесь сохранить данные в целостности, это недопустимо. К тому же управление сессиями далеко не точная наука. Так изменение IP адреса может привести к многократному созданию сессий за одно посещение.

Решение этих проблем должно быть централизованным, расширяемым и надежным.

Так что же нового?

Первым улучшением стало усиление обработки сессий. Весь механизм управления теперь осуществляется через единственный класс (publicSessions). Все остальные работают с сессиями через него. Это позволяет данному классу иметь полный контроль над всеми данными сессий.

Наибольшей проблемой было разрешение сессиям управлять данными о прочитанности тем и записью данных в базу только в случае удаления сессии. В теории это кажется простым, однако на практике все несколько запутаннее. Для технически подкованных более подробные детали приведены немного ниже.

В двух словах, система загружает данные о прочитанности (далее маркеры) из таблицы в базе данных при создании сессии для нового пользователя. Маркеры хранятся в сессии в месте с прочими данными. При каждом просмотре страницы данные сессии обновляются (время выполнения, местонахождения пользователя и т.п). Это продолжается до тех пор, пока сессию не нужно будет удалить, например, в случае неактивности пользователя и истечения времени жизни сессии. Сессия, приговоренная к удалению, передается другим классам на проверку. Однако, так как cookies ограничены по размеру, хранятся только последнее 100 прочтенных тем.

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

# Для отметки сущности прочтенной (в данном примере сущность это тема форума)
$itemMarking->markRead( array( 'forumID' => 2, 'topicID' => 10 ) );

# Проверка статуса сущности
if ( $itemMarking->isRead( array( 'forumID' => 2, 'itemID' => 99, 'itemLastUpdate' => 1200098989 ) ) === TRUE )
{
	....
}

# Получение последней даты когда сущность была отмечена прочтенной
$lastMarked = $itemmarking->fetchTimeLastMarked( array( 'forumID' => 2, 'itemID' => 99 ) );

Авторы модификации смогут использовать новую систему в своих плагинах.

А теперь обещанная техническая часть.

Для тех кто технически подкован данная часть описывает принципы работы системы в IP.Board 3.0.0

Первой проблемой было правильное использование __construct и __destruct в PHP5. Это было легко для __construct, который как и положенно запускался при инициализации объекта класса. Так что с ним проблем не возникло. Класс системы отслеживания статуса прочитанности тем получает и обрабатывает данные из cookies, а так же получает и обрабатывает данные из сессий в __construct.

Реальная же проблема была с __destruct. Вернее в порядке вызова оных. В PHP 5.0.0 и до PHP 5.2.4 данные деструкторы вызывались в порядке создания объектов. Из-за этого объект DB уничтожался раньше объекта classItemMarking, тем самым препятствуя работе с базой в деструкторе classItemMarking. В PHP 5.2.5 ситуация изменилась и деструкторы вызываются в обратном порядке. Таким образом использование деструкторов не позволяет сделать наше решение надежным на разных платформах, пришлось искать иное решение.

К счастью register_shutdown_function() запускается перед всеми __destruct потому мы использовали ее. Существует __myDestruct метод в ipsRegistry, который вызывает все __myDestruct() методы в классах потомках (DB, Member, Request, Cache, Settings и т.п.). Таким образом запускается собственный (ручной) деструктор в itemMarking, что позволяет сохранить все данные для удаляемых сессий и предоставить информацию для обновления сессии.

Back to top button