В следующих разделах подробно рассказывается о некоторых общих системах и концепциях, с которыми вы столкнетесь при разработке дополнения XenForo. Если вы знакомы с разработкой XenForo 1.x, то многие из этих концепций будут вам знакомы, хотя стоит их рассмотреть, так как есть несколько отличных новых инструментов и функций, которые помогут вам разрабатывать надстройки.
XF2 не разработан на каком-то конкретном фреймворке, как XF1, однако, мы использовали популярные, хорошо протестированных пакеты с открытым исходным кодом, для решения конкретных задач. Например, мы используем проект SwiftMailer для отправки электронной почты и проект с именем Guzzle в качестве HTTP-клиента. Все сторонние проекты загружаются из каталога src/vendor
.
В настоящее время разработчики дополнений не могут добавлять свои собственные зависимости к этому местоположению.
Прежде чем приступать к работе над разработкой XF2, вы можете потратить некоторое время на оценку приложения, с которым вы действительно будете создавать и редактировать файлы PHP. Это обычно называют IDE. Существует ряд вариантов, начиная от базового Блокнота и заканчивая чем-то вроде Sublime Text, который может быть расширен, чтобы иметь лучшую поддержку PHP с дополнениями, вплоть до среды разработки, такой как PhpStorm. Внутри мы в основном используем PhpStorm. Это премиальный и коммерческий продукт, но могут быть доступны свободные альтернативы. В любом случае, никто не может вам указывать, какую IDE вам использовать и вам придется потратить некоторое время, чтобы подобрать наиболее удобную среду.
XF2 использует автозагрузчик, который автоматически генерирует Composer. Это позволяет автоматически загружать весь код XF, сторонний код и любой код разработчика дополнений для всего проекта без необходимости include/require
ваши классы вручную.
Корень автозагрузки для всех дополнений XF является каталогом src/addons
. Это означает, что все ваши имена классов будут относиться к этому базовому местоположению. Также стоит отметить, что XF2 использует строгий шаблон «один класс - один файл». Каждый файл должен содержать только один класс, а имя этого класса должно определять точное местоположение файла PHP класса в файловой системе.
Например, если вы хотите создать новый класс в файле с именем src/addons/Demo/Setup.php
(где Demo
- ваш дополнительный код), этот класс будет называться Demo\Setup
. И наоборот, если бы у вас был класс с именем Demo\Entity\Thing
, вы узнаете, что файл для этого класса находится в пути src/addons/Demo/Entity/Thing.php
.
Всюду по XF мы используем пространства имён так что мы можем более кратко ссылаться на классы в одном и том же пространстве имен. Рекомендуется, чтобы все плагины также использовали пространства имен. В приведенном выше примере мы говорили о классе с именем Demo\Setup
. Используя пространства имен, класс фактически будет называться просто «Setup», но пространство имен будет установлено в Demo
. В качестве более конкретного примера мы также говорили выше о классе с именем Demo\Entity\Thing
. Давайте посмотрим, как будет выглядеть код PHP для этого класса:
<?php
namespace Demo\Entity;
class Thing
{
}
Если в каталоге src/addons/Demo/Entity
был класс с именем AnotherThing
, мы могли бы ссылаться на этот класс в классе Thing
просто как AnotherThing
, потому что этот класс находится в том же пространстве имён Demo\Entity
.
В некоторых местах, в XF мы сокращаем имена классов. Например, если вы хотите вызвать сущность User
(подробнее о сущностях мы расскажем ниже), вы можете сослаться на неё по короткому имени - XF:User
. Использование коротких имен классов и полного имени класса полностью зависит от контекста. Поэтому в контексте вызова сущности короткое имя класса будет указывать на полное имя - XF\Entity\User
. Часть XF
указывает путь к файлу (на основе ID дополнения), часть Entity
подразумевает вызов сущности, а часть User
указывает на конкретную сущность. Аналогично, когда вы начинаете создавать свои собственные классы, вы также будете использовать короткие имена классов для ссылки на свои собственные классы. Например, если вам нужно создать новую сущность Thing
для вашего дополнения Demo
, вы должны написать следующее:
\XF::em()->create('Demo:Thing');
Это вызвало бы класс Demo\Entity\Thing
. Аналогичным образом, если вы хотите получить доступ к репозиторию Thing
, вы должны написать его следующим образом:
\XF::repository('Demo:Thing');
Обратите внимание, что короткие имена классов идентичны. Вызов репозитория фактически вызовет Demo\Repository\Thing
.
Большое количество классов в XF2 является расширяемым, что позволяет разработчикам расширять и переопределять основной код без необходимости его прямого редактирования. Если вы знакомы с разработкой XF1, вы будете знакомы со следующим процессом:
- Создание PHP-файла Listener
- Создайть класс, который в конечном итоге расширит исходный класс
- Написать функцию, которая соответствует ожидаемой сигнатуре вызова для одного из событий
load_class
и добавляет имя вашего расширенного класса - Добавить в ACP «обработчик событий», который определяет класс и имя обработчика для функции, упомянутой выше, и необязательно указать подсказку, какой класс будет расширен
В XF2 мы удалили эти события в пользу определенной системы под названием «Расширения класса». Процесс выглядит следующим образом:
- Создайте класс, который в конечном итоге расширит исходный класс
- Добавьте «расширение класса» в ACP, которое указывает имя расширяемого класса и имя класса, который его расширяет
- Это явно сокращает часть шаблона, требуемого для расширения классов, а также предоставляет специальный пользовательский интерфейс для просмотра и управления этими расширениями. Давайте посмотрим на процесс, расширив публичный контроллер
Member
и добавив новое действие, которое отображает простое сообщение.
Первое, что нужно сделать, это создать дополнение. Мы ранее описывали, как это сделать, используя команду xf-addon:create
. В этом примере мы предположим, что вы создали дополнение с идентификатором и названием «Demo».
Теперь у вас будет файл addon.json для этого дополнения в следующем расположении src/addons/Demo/addon.json
.
Примечание Хотя, строго говоря, вы можете размещать свои расширенные классы везде, где захотите, в своем дополнительном каталоге, рекомендуется поместить расширенные классы в каталог, который легко идентифицирует: a) дополнение, коорому принадлежит класс b) тип расширяемого класса и c) название расширяемого класса. В следующих примерах мы расширяем публичный контроллер XF - Member, поэтому мы разместим наш расширенный класс по следующему пути:
src/addons/Demo/XF/Pub/Controller/Member.php
.
Расширенный класс должен существовать до того, как мы добавим расширение класса в ACP. Итак, следуйте следующим инструкциям:
- Создайте новый каталог с именем
XF
внутриsrc/addons/Demo
- Создайте новый каталог с именем
Pub
внутриsrc/addons/Demo/XF
- Создайте новый каталог с именем
Controller
внутриsrc/addons/Demo/XF/Pub
- Создайте новый файл с именем
Member.php
внутриsrc/addons/Demo/XF/Pub/Controller
.
Для начала, содержимое вашего файла должно быть таким:
<?php
namespace Demo\XF\Pub\Controller;
class Member extends XFCP_Member
{
}
Если вы знакомы с расширением PHP-классов в целом, но не знакомы с XF, приведенный выше пример может показаться запутанным. Причина этого заключается в том, что вы, возможно, ожидали расширить класс XF\Pub\Controller\Member
, а не XFCP_Member
. В XF мы используем систему «XenForo Class Proxy» (короткая версия XFCP) для создания «цепочки наследования», которая в конечном счете позволяет расширить один класс несколькими дополнениями. Мы пришли к решению ссылаться на фиктивный класс, имя которого состоит из префикса XFCP_
и названия расширяемого класса, в данном случае XFCP_Member
Сейчас, когда класс создан, мы можем создать расширение класса на странице расширения ACP > Development > Class > Add
class extension.
Все, что вам нужно сделать, это ввести имя базового класса (XF\Pub\Controller\Member
) в первом поле и имя расширенного класса (которое вы только что создали) во втором поле (Demo\XF\Pub\Controller\Member
) и нажмите кнопку «Сохранить».
Расширение вашего класса теперь должно быть активным, но в настоящее время оно ничего не делает. Чтобы что-то произошло, нам необходимо либо переопределить существующий метод в этом классе, создав метод с тем же именем, что и существующий, либо полностью добавив новый метод. Давайте сделаем последнее:
<?php
namespace Demo\XF\Pub\Controller;
class Member extends XFCP_Member
{
public function actionHelloWorld()
{
return $this->message('Hello world!');
}
}
Мы больше говорим о контроллерах, действиях и ответах на странице Основы контроллеров, поэтому не беспокойтесь об этом прямо сейчас.
Теперь мы добавили некоторый код в наш расширенный контроллер, давайте посмотрим его в действии. Просто введите следующий URL-адрес (относительно URL-адреса вашей доски): index.php?members/hello-world
. Теперь вы должны увидеть сообщение «Hello world!»!
Как упоминалось ранее, также возможно переопределить существующие методы внутри класса. Например, если мы изменим actionHelloWorld()
на actionIndex()
, вместо списка «Полезные пользователи», отобразится «Hello world!». Это не совсем правильный способ расширения существующего действия контроллера (или любого метода класса), но мы подробно рассмотрим это в Изменение ответа действие контроллера (правильно).
Многие объекты в XF создаются с помощью методов XF. Например, если мы хотим создать экземпляр конкретного репозитория, мы напишем следующее:
$repo = \XF::repository('Demo:Thing');
Это очень удобный способ создания объекта. Мы знаем, только посмотрев на него, какой объект будет создан. Полученный код в этом методе знает, как вернуть правильный объект для того, что мы запросили.
Однако, к сожалению, ваша IDE, вероятно, не имеет понятия (по крайней мере, по умолчанию). Что касается IDE, этот метод вернет экземпляр объекта XF\Mvc\Entity\Repository
. Это полезно в определенной степени, но существует множество методов, доступных в конкретном объекте Demo\Repository\Thing
, о которых ваша IDE не знает. Это в конечном итоге означает, что, когда вы попытаетесь использовать свой объект $repo
в коде, ваша среда IDE не сможет делать предложения или автоматически заполнять имена методов и требуемые аргументы.
В таких случаях будет полезен тайп-хинтинг, такой синтаксис должен поддерживаться большинством IDE, и текстоыми редакторами, которые "знакомы" с PHP. Мы просто изменим вызов репозитория следующим образом:
/** @var \Demo\Repository\Thing $repo */
$repo = \XF::repository('Demo:Thing');
Такая подсказка сообщает IDE, что $repo
относится к объекту, представленному классом Demo\Repository\Thing
, а не к объекту, который он автоматически выводил изначально.
Тайп-хинтинг так же очень полезен при расширении классов. Потенциальная проблема с нашими методами расширения класса заключается в том, что по существу ваши классы не расширяют исходный класс, который вы хотите расширить, но вместо этого проксируется через класс, который фактически не существует, например. XFCP_Member
, как в [пример выше] (/documentation/GeneralConcepts.md#part6).
Чтобы исправить эту проблему, мы автоматически создаем файл с именем extension_hint.php
и сохраняем его в вашем каталоге _output
.
Таким образом, создается ссылка, которую видит ваша IDE. Когда вы будете использовать $this
в любом из методов в расширенном классе, ваша IDE предложит автодополнение методов и свойств доступных в контроллере Member, или его родителях.