Методы расширения кода

Хуки и фильтры

Хуки используются для написания плагинов и бывают двух видов:

  1. Хуки - нужны для того, чтобы к ним прикреплять php функции. Затем эти функции будут срабатывать в момент срабатывания хука.
  2. Фильтры - нужны, чтобы отфильтровать передаваемое значение, т.е. фильтр получает значение и обязательно должен его вернуть (изменённое или нет).

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

Пример хука встречаемого в коде:

public function processPayRequest()
{
        $paySystem = $this->input->get('psystem', TYPE_NOTAGS);
        bff::hook('bills.pay.process', $paySystem);
        ...

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

<?php

bff::hookAdd('bills.pay.process', function($paySystem){
    // собственный код
});

Обратите внимание на общий ключ 'bills.pay.process', он то и связывает данный код. Также в созданной вами функции доступны дополнительные параметры, в примере это $paySystem.

Пример фильтра встречаемого в коде:

protected function getContactTypes($options = false)
{
    $types = bff::filter('contacts.types.list', array(
          self::TYPE_SITE_ERROR   => array('title' => _t('contacts', 'Ошибка на сайте')),
          self::TYPE_TECH_SUPPORT => array('title' => _t('contacts', 'Технический вопрос')),
          self::TYPE_OTHER        => array('title' => _t('contacts', 'Другие вопросы')),
    ));
    ...

Обратите внимание на обвертку bff::filter(...); Она позволяет выполнять дополнительную фильтрацию данных, в данном примере список типов сообщения в форме контактов.
Для того чтобы это сделать напишем следующий код:

<?php

bff::hookAdd('contacts.types.list', function($list){
    # Добавляем новые пункты в список:
    $list[] = _t('contacts', 'Блокировка и модерация');
    $list[] = _t('contacts', 'Платные услуги');
    $list[] = _t('contacts', 'Реклама на сайте');

    # Переносим тип "Другие вопросы" в конец списка:
    $list[Contacts::TYPE_OTHER]['priority'] = 1000;
    return $list;
});

Снова видим общий ключ 'contacts.types.list' выполняющий связующую функцию.
Также в созданный нами фильтр первым параметром передаются данные, после обработки и изменения которых их обязательно нужно вернуть обратно, что и было сделано последней строкой return $list;.
При этом если в функцию передается больше данных, обрабатывается из них всегда только первый, остальные вспомогательные и призваны дополнить картину (контекст вызова).

Расширение классов посредством плагинов

У плагина есть возможность расширять классы модулей (моделей или компонентов) + компонентов ядра, поддержка расширения которых заложена, определить это можно по наличию завершающего символа подчеркивания _ в названии класса. Пример расширения:

// код объявленный в функции start плагина
bff::classExtension('Site_', 'Plugin_Example_Site_Admin', $this->path('site.admin.php'), true);
// код содержимого файла site.admin.php
<?php
class Plugin_Example_Site_Admin extends Plugin_Example_Site_Admin_Base
{
}
  • В примере был расширен класс admin контроллера модуля Site для этого была вызвана функция bff::classExtension
  • Первым параметром в которой указывается оригинальное название класса объявленного в файле /modules/site/site.adm.class.php
  • Далее идет название класса расширения, в примере это Plugin_Example_Site_Admin, название класса может быть любым уникальным названием, рекомендуем использовать в качестве префикса название самого класса плагина, в примере это Plugin_Example_.
  • Далее идет путь к файлу плагина в котором выполняется его объявление, в данном случае указан путь к файлу site.admin.php расположенному в директории плагина
  • В объявлении класса, как видите, есть базовый класс от которого он наследуется, именование данного класса должно состоят из имени объявляемого класса и завершающего _Base, в примере это Plugin_Example_Site_Admin+_Base
  • В конечном итоге данное действие равносильно следующему порядку наследования: Site_ => Plugin_Example_Site_Admin => Site. В центральной части данной цепочки может быть задействовано больше участников, поскольку один и тот же класс могут расширять несколько плагинов, порядок определяется порядком их подключения.
  • Важно понимать что названия контроллер-классов модулей (например Users_) одинаково для фронтенда и админ. панели, поэтому следует всегда указывать какой именно контроллер требуется расширить.
  • Обращаться к методам модулей проекта в методе start плагина/темы следует только посредством хуков, иначе это может нарушить порядок расширения классов данного модуля.

Модификация файлов

Также есть возможность вносить изменения в заданные строки файлов модулей и ядра, для этого необходимо создать специальный файл с названием mods.php в корне директории плагина, который должен возвращать массив инструкций модификации.

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

Пример содержания файла mods.php:

<?php

return array(
    array(
        'file' => '/modules/site/site.class.php',
        'search' => 'public function index()',
        'replace' => $this->path('mods/site.index.php'),
        'replace-file' => true,
        'position' => 'after',
        'offset' => 1,
        'index' => 1,
    ),
);

Опишем его подробнее:

  • file - в примере выполняется модификация исходного файла /modules/site/site.class.php
  • search - в файле выполняется поиск строки с текстом public function index()
  • replace - код для вставки берется из файла mods/site.index.php расположенного в директории плагина, о том что был указан именно путь к файлу сообщает настройка 'replace-file' => true. Также здесь можно указать просто текст для замены.
  • position - будет выполняться вставка после найденной строки
  • offset - вставка будет выполнятся с пропуском одной строки после строки поиска search
  • index - действие будет выполняться только для первой найденной строки (для случая если таких в исходном файле найдется несколько)  

Описание допустимых параметров:

  • file - string,array - путь к файлу относительно корня проекта, или же массив, позволяющий указывать путь к нескольким файлам
  • search - string - строка поиска или уникальный текст из строки, которую необходимо модифицировать или использовать в качестве отправной точки (якоря). Рекомендуем использовать максимально уникальную строку.
  • replace - string - строка для замены или же путь к файлу
  • replace-file - bool - в значении true сообщает что поле replace указывает на путь к файлу с текстом для замены; по-умолчанию: false
  • position - string - позиция (действие) относительно строки поиска: before - вставить перед, after - вставить после, replace - замена найденной строки (подстроки), replace-line - подмена найденной строки полностью
  • offset - int - кол-во строк которые следует отступить от найденной строки; по-умолчанию: 0; применимо для всех позиций кроме replace-line
  • index - int, array - индекс строки поиска, в случае если в файле несколько одинаковых строк, например если их 5 и вы указали 2 только вторая по счету найденная строка будет заменена (модифицирована); по-умолчанию: 0
  • regexp - bool - строка поиска search является регулярным выражением; по-умолчанию: false
  • ignore-if - string - строка в файле, в случае нахождения которой, модификация файла выполняться не будет
  • ignore-if-regexp - bool строка является регулярным выражением; по-умолчанию: false  

Копии файлов хранятся в директории /custom/mods/.

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

Также в режиме разработчика доступна возможность принудительной пересборки модификаторов с развернутым описанием порядка их применения, статуса и принадлежности к плагину/теме.

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