Code Extension Methods

Hooks and Filters

Hooks are used for writing plugins and come in two types:

  1. Hooks are used to attach PHP functions to them. Then these functions will be triggered when the hook is fired.
  2. Filters are used to filter the passed value. The filter receives a value and must return it (modified or not).

Functions attached to hooks and filters can receive additional data based on which you can create or complement some actions' logic.

Example of a hook found in the code:

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

Instead of calling the function bff::hook, you can add your own code, or rather a function that extends/changes the logic. To do this, you need to write the following code:

<?php

bff::hookAdd('bills.pay.process', function($paySystem){
    // your own code
});

Pay attention to the common key 'bills.pay.process', it connects this code. In the function you create, additional parameters are available. In the example, it is $paySystem.

Example of a filter found in the code:

protected function getContactTypes($options = false)
{
    $types = bff::filter('contacts.types.list', array(
          self::TYPE_SITE_ERROR   => array('title' => _t('contacts', 'Website error')),
          self::TYPE_TECH_SUPPORT => array('title' => _t('contacts', 'Technical question')),
          self::TYPE_OTHER        => array('title' => _t('contacts', 'Other questions')),
    ));
    ...

Pay attention to the wrapper bff::filter(...). It allows performing additional data filtering. In this example, it is the list of message types in the contact form.
To do this, write the following code:

<?php

bff::hookAdd('contacts.types.list', function($list){
    # Add new items to the list:
    $list[] = _t('contacts', 'Blocking and moderation');
    $list[] = _t('contacts', 'Paid services');
    $list[] = _t('contacts', 'Advertising on the site');

    # Move the "Other questions" type to the end of the list:
    $list[Contacts::TYPE_OTHER]['priority'] = 1000;
    return $list;
});

Again, we see the common key 'contacts.types.list' that acts as a connector.
Also, the data is passed as the first parameter to the filter we create. After processing and modifying them, they must be returned, as done in the last line return $list;.
At the same time, if more data is passed to the function, only the first one is processed, and the rest is auxiliary and is intended to complement the context (calling context).

The plugin has the ability to extend module classes (models or components) + core components, the support for extension of which is implemented, can be determined by the presence of the underscore character "_" at the end of the class name. An extension example:

// code declared in the start function of the plugin
bff::classExtension('Site_', 'Plugin_Example_Site_Admin', $this->path('site.admin.php'), true);
// code contained in the site.admin.php file
<?php
class Plugin_Example_Site_Admin extends Plugin_Example_Site_Admin_Base
{
}
  • In the example, the admin controller class of the Site module was extended by calling the bff::classExtension function.
  • The first parameter specifies the original name of the class declared in the /modules/site/site.adm.class.php file.
  • The next parameter is the name of the extension class, in this example, it is Plugin_Example_Site_Admin. The class name can be any unique name, it is recommended to use the name of the plugin itself as a prefix, in this example, it is Plugin_Example_.
  • The next parameter is the path to the plugin file where its declaration is performed. In this case, the path to the site.admin.php file located in the plugin directory.
  • As you can see in the class declaration, there is a base class from which it inherits. The naming of this class should consist of the name of the declared class and the suffix _Base, in this example, it is Plugin_Example_Site_Admin+_Base.
  • In the end, this action is equivalent to the following inheritance order: Site_ => Plugin_Example_Site_Admin => Site. There can be more participants in the central part of this chain since the same class can be extended by multiple plugins, and the order is determined by the order of their inclusion.
  • It is important to understand that the names of the module controller classes (for example, Users_) are the same for the frontend and admin panels, so you should always specify which controller needs to be extended.
  • It is recommended to access module methods in the start method of the plugin/theme only through hooks; otherwise, it may disrupt the order of extending the classes of the module.

Modifying Files

It is also possible to make changes to specific lines in module and core files. To do this, create a special file named mods.php in the root directory of the plugin, which should return an array of modification instructions.

Modifying files in this format means creating a copy of the original file and then making the modifications described using the instructions. At the time of accessing the original file, the system will include the modified file copy, allowing modifications to be made without affecting the original version of the files.

An example of the mods.php file contents:

<?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,
    ),
);

Let's explore it further:

  • file - in the example, it modifies the original file /modules/site/site.class.php
  • search - searches for the line with the text public function index() in the file
  • replace - the code for insertion is taken from the file mods/site.index.php located in the plugin directory. The setting 'replace-file' => true indicates that the path to a file with text for replacement was specified. Alternatively, you can specify the plain text for replacement.
  • position - the insertion is done after the found line
  • offset - the insertion is made with skipping one line after the search line
  • index - the action will only be performed for the first found line (in case there are multiple occurrences in the original file), defaults to 0  

Description of the allowable parameters:

  • file - string,array - the path to the file relative to the project root, or an array that allows specifying multiple file paths
  • search - string - the search string or a unique text from the line to be modified or used as a starting point (anchor). It is recommended to use a highly unique string.
  • replace - string - the string for replacement or the path to a file
  • replace-file - bool - if set to true, it indicates that the replace field specifies a path to a file with the text for replacement; by default: false
  • position - string - the position (action) relative to the search line: before - insert before, after - insert after, replace - replace the found substring, replace-line - completely replace the found line
  • offset - int - the number of lines to be skipped after the search line; by default: 0; applicable for all positions except replace-line
  • index - int,array - the index of the search line. In case there are multiple identical lines in the file, for example, if there are 5 and you specified 2, only the second found line will be replaced (modified); by default: 0
  • regexp - bool - the search string search is a regular expression; by default: false
  • ignore-if - string - a string in the file, if found, the file modification will not be performed
  • ignore-if-regexp - bool - the ignore-if string is a regular expression; by default: false  

Copies of files are stored in the /custom/mods/ directory.

Modification settings are applied and cached when enabling/disabling the plugin in the admin panel. For testing during development, use the debug mode.

In the developer mode, there is also the ability to forcibly rebuild modifiers with a detailed description of the order in which they are applied, their status, and affiliation with a plugin/theme.

An alternative to using modifiers is the use of blocks that allow integrating HTML blocks into theme or module template files using special comments (tags).