Одной из запутанных вещей в плагинах является то, что они так сильно различаются между собой. И только одна деталь является общей для всех: вызов плагина. Давайте рассмотрим несколько примеров базовых плагинов Joomla и их вызов.

System: SEF

Нашим первым примером будет SEF плагин. Это класс с именем plgSystemSef в файле plugins/system/sef/sef.php. Он позволяет Joomla использовать ЧПУ (человекопонятные УРЛ). Этот плагин сканирует HTML документ на наличие ссылок и преобразует их в человекопонятные ссылки. Он также заменяет относительные URL на полные URL для некоторых других типов ссылок.

Как он вызывается?

Давайте посмотрим, где же исполняется этот плагин – другими словами, рассмотрим код, который включает этот плагин и вызывает событие этого плагина. SEF плагин – это системный плагин и он вызывается вместе с событием onAfterRender. Перед тем как вызывать событие, мы должны включить файл плагина.

Если мы загружаем страницу с фронтенда нашего сайта, мы вызываем метод render() класса JSite (в файле includes/application.php). Почти в самом конце этого метода мы можем видеть следующий код:

JPluginHelper::importPlugin('system');



Эта команда загружает в рабочую память все включенные системные плагины (если плагин выключен в Менеджере плагинов, то он не загружается). Когда классы системных плагинов загружены в рабочую память, то мы можем вызвать одно или более системных событий. Так как эти плагины по сути являются простыми объявлениями классов, мы пока не выполнили никакого кода. Далее в методе render() класса JSite мы на самом деле вызываем метод onAfterRender():

// Trigger the onAfterRender event.
$this->triggerEvent('onAfterRender');



Этот код вызывает событие onAfterRender(). Давайте проследим за кодом, чтобы разобраться, как это работает. Переменная $this является объектом класса JSite, поэтому $this->triggerEvent вызывает метод triggerEvent() класса JSite с одним параметром – строкой "OnAfterRender".

JSite расширяет класс JApplication. Так как у JSite нет своего собственного метода triggerEvent() (другими словами, он не переопределяет наследуемый от родительского класса метод), то он вызывает метод из JApplication:
function triggerEvent($event, $args=null)
{
     $dispatcher = JDispatcher::getInstance();
     return $dispatcher->trigger($event, $args);
}



Этот код создает объект типа JDispatcher и затем вызывает метод trigger() этого объекта. Параметр $event установлен в значение "onAfterRender", а второй параметр $args установлен в значение по умолчанию "null", так как мы его не передавали.

Результатом этого является исполнение методов onAfterRender() каждого включенного плагина, который доступен в рабочей памяти. В данном случае поиск ограничен системными плагинами, потому что мы указали тип "system", когда вызывали importPlugin('system'), и именно поэтому в рабочую память были загружены только системные плагины.

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

Authentication: joomla

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

Плагин находится в файле plugins/authentication/joomla/joomla.php и имя его класса plgAuthenticationJoomla.

Как он вызывается?

Когда пользователь делает попытку авторизоваться на сайте, исполняется метод authenticate() класса JAuthentication. В этом методе мы видим уже ожидаемую строку кода:
$plugins = JPluginHelper::getPlugin('authentication');



Она загружает в рабочую память все включенные плагины аутентификации. Далее в этом методе мы видим оператор foreach:

foreach ($plugins as $plugin)
{
 $className = 'plg'.$plugin->type.$plugin->name;
 if (class_exists($className)) {
    $plugin = new $className($this, (array)$plugin);
 }
 else {
    // bail here if the plugin can't be created
    JError::raiseWarning(50, JText::sprintf('JLIB_USER_ERROR_AUTHENTICATION_FAILED_LOAD_PLUGIN', $className));
    continue;
 }
 
 // Try to authenticate
 $plugin->onUserAuthenticate($credentials, $options, $response);
}



В нем происходит проход по всем включенным плагинам аутентификации и проверка, существует ли класс с указанным именем. Если нет ни одного включенного плагина, то он завершается с ошибкой. А если классы существуют, то исполняется последняя строка, которая вызывает метод onUserAuthenticate для каждого плагина.

Content: joomla

Плагин находится в файле plugins/content/joomla/joomla.php и имя его класса plgContentJoomla. У него есть два метода. Метод onContentAfterSave() используется для отсылки уведомления пользователям по электронной почте, когда сохраняется новый материал. Метод onContentBefore Delete() используется для проверки категории перед её удалением - связаны ли с ней какие либо материалы, контакты и т.п.

Давайте посмотрим на метод onContentBeforeDelete().

Как он вызывается?

Когда пользователь удаляет категории, материалы, контакты и другие айтемы в административной части Joomla, то вызывается событие onContentBeforeDelete. Одно из мест, где это происходит – класс JModelAdmin. Если мы изучим метод delete, мы увидим следующий код:
// Trigger the onContentBeforeDelete event.
$result = $dispatcher->trigger($this->event_before_delete, array($context, $table));


Здесь свойство event_before_delete было установлено в значение onContentBeforeDelete в методе конструктора класса.

В этом коде следует обратить внимание на две вещи. Первое, мы ожидаем возвращаемое значение, которое сохраняется в переменной $result. Второе, мы передаем два параметра в метод trigger(): название события и массив с двумя элементами. Метод trigger() распаковывает этот массив и передает каждый из его элементов в качестве параметров в метод onContentBeforeDelete(). В нашем случае, это два параметра $context и $table. Переменная $context содержит в себе информацию о том, в каком контексте было вызвано это событие (например, "com_categories.category"). Переменная $table – это массив с данными, которые будут удалены.

Зачем использовать плагины?

Гибкость, вот ответ на этот вопрос. Давайте посмотрим на плагин "Content: joomla". Проверяя категорию перед её удалением в плагине, мы предоставляем администратору несколько вариантов. Во-первых, плагины могут быть выключены, что дает администратору возможность отключить проверку категории. Во-вторых, параметры в плагине позволяют включать/выключать проверки индивидуально. В-третьих, вы можете предоставить свой плагин, который заменяет либо дополняет функционал этого плагина или любого другого базового плагина. И, наконец, этот плагин предоставляет легкую шаблонную модель для разработчиков сторонних расширений, которые захотят внедрить проверку категорий в своих расширениях.

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

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