Заметки об изменениях в SMF 3.0

Обзор основных изменений в следующей линейке SMF — тройке (SMF 3).

Эта статья в первую очередь для тех, кто так или иначе связан с работой форумов на базе SMF, и установкой/разработкой дополнений к ним.

Здесь мы поговорим об изменениях, которые уже произошли в текущем исходном коде SMF 3.0 и затрагивают работу модификаций.

Системные требования

Первым самым важным изменением является обновление минимальных требований к установке SMF:

  • PHP 8.0
  • MySQL 8.0.35 (MariaDB 10.4)
  • PostgreSQL 12.17

Так что если вы до сих пор сидите на хостинге с PHP 7 и MySQL 5.7, есть повод задуматься. Если планируете обновляться. Чем дольше вы тянете, тем сложнее (а для некоторых и дороже) это будет сделать потом.

Изменения в package-info.xml

Чтобы ваша модификация устанавливалась в SMF 3.0, потребуется обновить номер версии в секциях install и uninstall:

    <install for="3.0.*">
        ...
    </install>

    <uninstall for="3.0.*">
        ...
    </uninstall>

Вместо звёздочки можно подставить конкретный номер версии. Или просто перечислять все поддерживаемые вашим модом версии через запятую.

Изменения в коде

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

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

Посмотрите на сравнительную таблицу, в которой я привёл некоторые изменения:

SMF 2.1.x SMF 3.0
$context Utils::$context
$smcFunc Utils::$smcFunc
$modSettings Config::$modSettings
$scripturl Config::$scripturl
$language Config::$language
$txt Lang::$txt
loadLanguage($lang) Lang::load($lang)
$settings Theme::$current->settings
$options Theme::$current->options
loadTemplate($name) Theme::loadTemplate($name)
loadCSSFile($file) Theme::loadCSSFile($file)
loadJavaScriptFile($file) Theme::loadJavaScriptFile($file)
$user_info User::$info
allowedTo($perm) User::hasPermission($perm)
parse_bbc($str) BBCodeParser::load()->parse($str)

На первый взгляд может показаться, что придётся создавать отдельную версию каждой модификации для SMF 3.0. Однако не всё так плохо. В SMF 3.0 Alpha 1 реализован режим обратной совместимости, который позволяет использовать почти весь ваш код без изменений (но есть исключения). Пока он включён (а включён он на данный момент по умолчанию), большинство модификаций SMF 2.1, работающих на хуках, будут совместимы и с 3.0. Нужно будет обновить лишь package-info.xml или устанавливать моды в режиме эмуляции SMF 2.1. (А кто помнит времена перехода с 2.0 на 2.1?)

Исключения, требующие внимания

Однако некоторые вещи всё-таки потребуют от вас определённых действий. Перечислим их.

Фоновые и запланированные задачи

Если вы использовали в SMF 2.1 для работы с фоновыми задачами стандартный класс SMF_BackgroundTask, в 3.0 вместо него нужно использовать SMF\Tasks\BackgroundTask (но можно оставить и SMF_BackgroundTask, он используется в качестве псевдонима). А для запланированных задач (их можно настраивать через админку) введён дополнительный класс SMF\Tasks\ScheduledTask. Подробнее можно почитать в комментариях в файле Sources/TaskRunner.php.

Хуки

Введён новый хук — integrate_permissions_list — для более удобного добавления разрешений. Но так как добавлен он только в 3.0, смысла использовать его пока мало. Разве что только в новых модах, предназначенных исключительно для 3.0.

SSI

Функции SSI перемещены в отдельный класс SMF\ServerSideIncludes с соответствующими статическими методами. Например, функция ssi_topBoards() в 3.0 теперь вызывается так: ServerSideIncludes::topBoards(). Сам файл SSI.php остался в корне форума и его по-прежнему надо подключать для работы с SSI. Примеры использования смотрите в ssi_examples.php.

Языковые файлы

В 3.0 Alpha 2 языковые файлы перемещены в корневую директорию Languages, в которой каждый язык хранится в отдельной директории. Например, английский — в en_US. Прежнее размещение — Themes/default/languagesпока оставлено для совместимости. То есть языковые файлы модов, расположенных там, будут загружаться, как и раньше. Глобальная переменная $language (в 3.0 — Config::$language) теперь содержит не название языка english, а локаль en_US для языка по умолчанию (для других языков, соответственно, их локали). Следовательно, если в вашей модификации использовалась какая-то привязка к языковым переменным (например, заголовки блоков портала на разных языках), вам придётся все эти названия менять в соответствии с картой Lang::LANG_TO_LOCALE.

Новая тема + иконки

В 3.0 Alpha 4 должна появиться новая тема по умолчанию, с поддержкой иконок Font Awesome (и да, они в итоге войдут в состав дистрибутива SMF, как когда-то jQuery) и тёмного режима. Это наверняка потребует адаптации используемых шаблонов.

Вот как будет выглядеть страница темы А вот как будет выглядеть админка

Плюрализация

В 3.0 Alpha 2 добавлена поддержка ICU MessageFormat для правильной локализации. После этого можно будет наконец без проблем переводить фразы с учётом количества, пола и т. п. в зависимости от текущей локали. Для всего этого предусмотрен новый статический метод Lang::getTxt. В английском всего две формы — one и other, а вот для русского, например, нужно будет при переводе добавлять ещё как минимум две: few и many. Как это будет выглядеть в коде? Примерно так:

<?php

echo 1 . ' ' . Lang::getTxt('user_plural', [1]); // 1 пользователь (1 user)
echo Lang::getTxt('number_of_users', [1]); // 1 пользователь (1 user)
echo 2 . ' ' . Lang::getTxt('user_plural', [2]); // 2 пользователя (2 users)
echo Lang::getTxt('number_of_users', [2]); // 2 пользователя (2 users)
echo 10 . ' ' . Lang::getTxt('user_plural', [10]); // 10 пользователей (10 users)
echo Lang::getTxt('number_of_users', [10]); // 10 пользователей (10 users)
echo 1.5 . ' ' . Lang::getTxt('user_plural', [1.5]); // 1.5 пользователя (1.5 users)
echo Lang::getTxt('number_of_users', [1.5]); // 1.5 пользователя (1.5 users)

А в языковых файлах так:

<?php

// en_US
$txt['user_plural'] = '{0, plural,
    one {user}
    other {users}
}';
$txt['number_of_users'] = '{0, plural,
    one {# user}
    other {# users}
}';

// ru_RU
$txt['user_plural'] = '{0, plural,
    one {пользователь}
    few {пользователя}
    many {пользователей}
    other {пользователя}
}';
$txt['number_of_users'] = '{0, plural,
    one {# пользователь}
    few {# пользователя}
    many {# пользователей}
    other {# пользователя}
}';
Категория Охватываемые числа
one 1, 21, 31, 101, …
few 2, 3, 4, 22, …
many 5, 6, 27, 108, …
other 0.0-1.5, 10.0, 100.0, 1000.0, …

Где вы видели полтора пользователя? Или это когда пользователя зарегистрировали через админку, но он так и не зашёл на форум? Нет, всё равно будет отображаться целое число.

Поэтому в русском языке, если заранее известно, что исходные числа не будут дробными, можно использовать только other, а many опустить:

<?php

$txt['number_of_users'] = '{0, plural,
    one {# пользователь}
    few {# пользователя}
    other {# пользователей}
}';

Для типа select нужно указывать возможные варианты и обязательный other, используемый, когда ни один вариант не подходит:

<?php

// Строчка в языковом файле
$txt['some_var'] = '{gender, select,
    =male {Играл}
    =female {Играла}
    other {Играли}
} недавно';

// Использование (в файле внутри Sources или Themes)
echo Lang::getTxt('some_var', ['gender' => 'male']);

По сути, имеем улучшенный sprintf, когда в строчках можно указывать ключевые слова вместо %s, %d и т. д.:

<?php

// Вариант с sprintf
$txt['other_var'] = '%s, у вас непрочитанных сообщений: %d';

echo sprintf(Lang::$txt['other_var'], User::$info['name'], 10);

// Вариант с ICU MessageFormat
$txt['other_var'] = '{username}, у вас {messages, plural,
    one {# непрочитанное сообщение}
    few {# непрочитанных сообщения}
    other {# непрочитанных сообщений}
}';

echo Lang::getTxt('other_var', [
    'username' => User::$info['name'],
    'messages' => 10
]);

Заключение

Желающие могут присоединиться к проекту, для этого не обязательно быть программистом, инженером или переводчиком. Например, люди с хорошим знанием современного русского языка могут заняться вычиткой текущего перевода. А скрупулёзные пользователи могут присылать информацию о найденных ими багах, или предлагать новые фичи для внедрения. Нет смысла сидеть и ворчать, что бесплатный SMF не так хорош, как платный XenForo, если вы сами не готовы ничего сделать для улучшения.

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

К моменту релиза финальной третьей версии (когда? — как только так сразу) могут появиться ещё какие-нибудь нюансы. Эта статья будет дополняться по мере их появления. Время подготовиться у нас есть.